diff options
| author | 2023-11-26 21:08:53 -0500 | |
|---|---|---|
| committer | 2023-11-26 21:08:53 -0500 | |
| commit | 1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a (patch) | |
| tree | c219aacab776c0a1e3956614b60a01fa2f6164cb /src | |
| parent | shader_recompiler: Align SSBO offsets in GlobalMemory functions (diff) | |
| parent | Merge pull request #11535 from GPUCode/upload_cmdbuf (diff) | |
| download | yuzu-1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a.tar.gz yuzu-1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a.tar.xz yuzu-1d11fe00a3000efbf6a0a4bb690e0d544a1b7b4a.zip | |
Merge branch 'master' into ssbo-align
Diffstat (limited to '')
370 files changed, 23771 insertions, 16371 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d7f68618c..e04d2418b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -21,7 +21,7 @@ if (MSVC) | |||
| 21 | # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. | 21 | # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. |
| 22 | add_definitions(-DWIN32_LEAN_AND_MEAN) | 22 | add_definitions(-DWIN32_LEAN_AND_MEAN) |
| 23 | 23 | ||
| 24 | # Ensure that projects build with Unicode support. | 24 | # Ensure that projects are built with Unicode support. |
| 25 | add_definitions(-DUNICODE -D_UNICODE) | 25 | add_definitions(-DUNICODE -D_UNICODE) |
| 26 | 26 | ||
| 27 | # /W4 - Level 4 warnings | 27 | # /W4 - Level 4 warnings |
| @@ -54,11 +54,11 @@ if (MSVC) | |||
| 54 | /GT | 54 | /GT |
| 55 | 55 | ||
| 56 | # Modules | 56 | # Modules |
| 57 | /experimental:module- # Disable module support explicitly due to conflicts with precompiled headers | 57 | /experimental:module- # Explicitly disable module support due to conflicts with precompiled headers. |
| 58 | 58 | ||
| 59 | # External headers diagnostics | 59 | # External headers diagnostics |
| 60 | /external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers | 60 | /external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers |
| 61 | /external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers | 61 | /external:W0 # Sets the default warning level to 0 for external headers, effectively disabling warnings for them. |
| 62 | 62 | ||
| 63 | # Warnings | 63 | # Warnings |
| 64 | /W4 | 64 | /W4 |
| @@ -187,6 +187,7 @@ add_subdirectory(audio_core) | |||
| 187 | add_subdirectory(video_core) | 187 | add_subdirectory(video_core) |
| 188 | add_subdirectory(network) | 188 | add_subdirectory(network) |
| 189 | add_subdirectory(input_common) | 189 | add_subdirectory(input_common) |
| 190 | add_subdirectory(frontend_common) | ||
| 190 | add_subdirectory(shader_recompiler) | 191 | add_subdirectory(shader_recompiler) |
| 191 | 192 | ||
| 192 | if (YUZU_ROOM) | 193 | if (YUZU_ROOM) |
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index ac43d84b7..5721327e7 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts | |||
| @@ -47,6 +47,10 @@ android { | |||
| 47 | jniLibs.useLegacyPackaging = true | 47 | jniLibs.useLegacyPackaging = true |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | androidResources { | ||
| 51 | generateLocaleConfig = true | ||
| 52 | } | ||
| 53 | |||
| 50 | defaultConfig { | 54 | defaultConfig { |
| 51 | // TODO If this is ever modified, change application_id in strings.xml | 55 | // TODO If this is ever modified, change application_id in strings.xml |
| 52 | applicationId = "org.yuzu.yuzu_emu" | 56 | applicationId = "org.yuzu.yuzu_emu" |
| @@ -215,7 +219,6 @@ dependencies { | |||
| 215 | implementation("io.coil-kt:coil:2.2.2") | 219 | implementation("io.coil-kt:coil:2.2.2") |
| 216 | implementation("androidx.core:core-splashscreen:1.0.1") | 220 | implementation("androidx.core:core-splashscreen:1.0.1") |
| 217 | implementation("androidx.window:window:1.2.0-beta03") | 221 | implementation("androidx.window:window:1.2.0-beta03") |
| 218 | implementation("org.ini4j:ini4j:0.5.4") | ||
| 219 | implementation("androidx.constraintlayout:constraintlayout:2.1.4") | 222 | implementation("androidx.constraintlayout:constraintlayout:2.1.4") |
| 220 | implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") | 223 | implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") |
| 221 | implementation("androidx.navigation:navigation-fragment-ktx:2.7.4") | 224 | implementation("androidx.navigation:navigation-fragment-ktx:2.7.4") |
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index a67351727..f10131b24 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml | |||
| @@ -26,7 +26,6 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||
| 26 | android:supportsRtl="true" | 26 | android:supportsRtl="true" |
| 27 | android:isGame="true" | 27 | android:isGame="true" |
| 28 | android:appCategory="game" | 28 | android:appCategory="game" |
| 29 | android:localeConfig="@xml/locales_config" | ||
| 30 | android:banner="@drawable/tv_banner" | 29 | android:banner="@drawable/tv_banner" |
| 31 | android:fullBackupContent="@xml/data_extraction_rules" | 30 | android:fullBackupContent="@xml/data_extraction_rules" |
| 32 | android:dataExtractionRules="@xml/data_extraction_rules_api_31" | 31 | android:dataExtractionRules="@xml/data_extraction_rules_api_31" |
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 07f1b4842..f2ba2504c 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 | |||
| @@ -230,8 +230,6 @@ object NativeLibrary { | |||
| 230 | */ | 230 | */ |
| 231 | external fun onTouchReleased(finger_id: Int) | 231 | external fun onTouchReleased(finger_id: Int) |
| 232 | 232 | ||
| 233 | external fun reloadSettings() | ||
| 234 | |||
| 235 | external fun initGameIni(gameID: String?) | 233 | external fun initGameIni(gameID: String?) |
| 236 | 234 | ||
| 237 | external fun setAppDirectory(directory: String) | 235 | external fun setAppDirectory(directory: String) |
| @@ -252,7 +250,7 @@ object NativeLibrary { | |||
| 252 | 250 | ||
| 253 | external fun reloadKeys(): Boolean | 251 | external fun reloadKeys(): Boolean |
| 254 | 252 | ||
| 255 | external fun initializeSystem() | 253 | external fun initializeSystem(reload: Boolean) |
| 256 | 254 | ||
| 257 | external fun defaultCPUCore(): Int | 255 | external fun defaultCPUCore(): Int |
| 258 | 256 | ||
| @@ -462,12 +460,12 @@ object NativeLibrary { | |||
| 462 | } | 460 | } |
| 463 | 461 | ||
| 464 | fun setEmulationActivity(emulationActivity: EmulationActivity?) { | 462 | fun setEmulationActivity(emulationActivity: EmulationActivity?) { |
| 465 | Log.verbose("[NativeLibrary] Registering EmulationActivity.") | 463 | Log.debug("[NativeLibrary] Registering EmulationActivity.") |
| 466 | sEmulationActivity = WeakReference(emulationActivity) | 464 | sEmulationActivity = WeakReference(emulationActivity) |
| 467 | } | 465 | } |
| 468 | 466 | ||
| 469 | fun clearEmulationActivity() { | 467 | fun clearEmulationActivity() { |
| 470 | Log.verbose("[NativeLibrary] Unregistering EmulationActivity.") | 468 | Log.debug("[NativeLibrary] Unregistering EmulationActivity.") |
| 471 | sEmulationActivity.clear() | 469 | sEmulationActivity.clear() |
| 472 | } | 470 | } |
| 473 | 471 | ||
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 8c053670c..d114bd53d 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 | |||
| @@ -11,6 +11,7 @@ import java.io.File | |||
| 11 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization | 11 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization |
| 12 | import org.yuzu.yuzu_emu.utils.DocumentsTree | 12 | import org.yuzu.yuzu_emu.utils.DocumentsTree |
| 13 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper | 13 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper |
| 14 | import org.yuzu.yuzu_emu.utils.Log | ||
| 14 | 15 | ||
| 15 | fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir | 16 | fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir |
| 16 | 17 | ||
| @@ -49,6 +50,7 @@ class YuzuApplication : Application() { | |||
| 49 | DirectoryInitialization.start() | 50 | DirectoryInitialization.start() |
| 50 | GpuDriverHelper.initializeDriverParameters() | 51 | GpuDriverHelper.initializeDriverParameters() |
| 51 | NativeLibrary.logDeviceInfo() | 52 | NativeLibrary.logDeviceInfo() |
| 53 | Log.logDeviceInfo() | ||
| 52 | 54 | ||
| 53 | createNotificationChannels() | 55 | createNotificationChannels() |
| 54 | } | 56 | } |
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 f37875ffe..f41d7bdbf 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 | |||
| @@ -47,6 +47,7 @@ import org.yuzu.yuzu_emu.model.EmulationViewModel | |||
| 47 | import org.yuzu.yuzu_emu.model.Game | 47 | import org.yuzu.yuzu_emu.model.Game |
| 48 | import org.yuzu.yuzu_emu.utils.ForegroundService | 48 | import org.yuzu.yuzu_emu.utils.ForegroundService |
| 49 | import org.yuzu.yuzu_emu.utils.InputHandler | 49 | import org.yuzu.yuzu_emu.utils.InputHandler |
| 50 | import org.yuzu.yuzu_emu.utils.Log | ||
| 50 | import org.yuzu.yuzu_emu.utils.MemoryUtil | 51 | import org.yuzu.yuzu_emu.utils.MemoryUtil |
| 51 | import org.yuzu.yuzu_emu.utils.NfcReader | 52 | import org.yuzu.yuzu_emu.utils.NfcReader |
| 52 | import org.yuzu.yuzu_emu.utils.ThemeHelper | 53 | import org.yuzu.yuzu_emu.utils.ThemeHelper |
| @@ -80,6 +81,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | override fun onCreate(savedInstanceState: Bundle?) { | 83 | override fun onCreate(savedInstanceState: Bundle?) { |
| 84 | Log.gameLaunched = true | ||
| 83 | ThemeHelper.setTheme(this) | 85 | ThemeHelper.setTheme(this) |
| 84 | 86 | ||
| 85 | super.onCreate(savedInstanceState) | 87 | super.onCreate(savedInstanceState) |
| @@ -105,7 +107,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 105 | 107 | ||
| 106 | val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | 108 | val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| 107 | if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { | 109 | if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { |
| 108 | if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) { | 110 | if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) { |
| 109 | Toast.makeText( | 111 | Toast.makeText( |
| 110 | this, | 112 | this, |
| 111 | getString( | 113 | getString( |
| @@ -371,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 371 | val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() | 373 | val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() |
| 372 | .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() | 374 | .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() |
| 373 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { | 375 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { |
| 376 | val isEmulationActive = emulationViewModel.emulationStarted.value && | ||
| 377 | !emulationViewModel.isEmulationStopping.value | ||
| 374 | pictureInPictureParamsBuilder.setAutoEnterEnabled( | 378 | pictureInPictureParamsBuilder.setAutoEnterEnabled( |
| 375 | BooleanSetting.PICTURE_IN_PICTURE.boolean | 379 | BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive |
| 376 | ) | 380 | ) |
| 377 | } | 381 | } |
| 378 | setPictureInPictureParams(pictureInPictureParamsBuilder.build()) | 382 | setPictureInPictureParams(pictureInPictureParamsBuilder.build()) |
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 0c82cdba8..2ef638559 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 | |||
| @@ -22,12 +22,16 @@ import androidx.core.graphics.drawable.toBitmap | |||
| 22 | import androidx.core.graphics.drawable.toDrawable | 22 | import androidx.core.graphics.drawable.toDrawable |
| 23 | import androidx.documentfile.provider.DocumentFile | 23 | import androidx.documentfile.provider.DocumentFile |
| 24 | import androidx.lifecycle.ViewModelProvider | 24 | import androidx.lifecycle.ViewModelProvider |
| 25 | import androidx.lifecycle.lifecycleScope | ||
| 25 | import androidx.navigation.findNavController | 26 | import androidx.navigation.findNavController |
| 26 | import androidx.preference.PreferenceManager | 27 | import androidx.preference.PreferenceManager |
| 27 | import androidx.recyclerview.widget.AsyncDifferConfig | 28 | import androidx.recyclerview.widget.AsyncDifferConfig |
| 28 | import androidx.recyclerview.widget.DiffUtil | 29 | import androidx.recyclerview.widget.DiffUtil |
| 29 | import androidx.recyclerview.widget.ListAdapter | 30 | import androidx.recyclerview.widget.ListAdapter |
| 30 | import androidx.recyclerview.widget.RecyclerView | 31 | import androidx.recyclerview.widget.RecyclerView |
| 32 | import kotlinx.coroutines.Dispatchers | ||
| 33 | import kotlinx.coroutines.launch | ||
| 34 | import kotlinx.coroutines.withContext | ||
| 31 | import org.yuzu.yuzu_emu.HomeNavigationDirections | 35 | import org.yuzu.yuzu_emu.HomeNavigationDirections |
| 32 | import org.yuzu.yuzu_emu.R | 36 | import org.yuzu.yuzu_emu.R |
| 33 | import org.yuzu.yuzu_emu.YuzuApplication | 37 | import org.yuzu.yuzu_emu.YuzuApplication |
| @@ -92,28 +96,34 @@ class GameAdapter(private val activity: AppCompatActivity) : | |||
| 92 | data = Uri.parse(holder.game.path) | 96 | data = Uri.parse(holder.game.path) |
| 93 | } | 97 | } |
| 94 | 98 | ||
| 95 | val layerDrawable = ResourcesCompat.getDrawable( | 99 | activity.lifecycleScope.launch { |
| 96 | YuzuApplication.appContext.resources, | 100 | withContext(Dispatchers.IO) { |
| 97 | R.drawable.shortcut, | 101 | val layerDrawable = ResourcesCompat.getDrawable( |
| 98 | null | 102 | YuzuApplication.appContext.resources, |
| 99 | ) as LayerDrawable | 103 | R.drawable.shortcut, |
| 100 | layerDrawable.setDrawableByLayerId( | 104 | null |
| 101 | R.id.shortcut_foreground, | 105 | ) as LayerDrawable |
| 102 | GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources) | 106 | layerDrawable.setDrawableByLayerId( |
| 103 | ) | 107 | R.id.shortcut_foreground, |
| 104 | val inset = YuzuApplication.appContext.resources | 108 | GameIconUtils.getGameIcon(activity, holder.game) |
| 105 | .getDimensionPixelSize(R.dimen.icon_inset) | 109 | .toDrawable(YuzuApplication.appContext.resources) |
| 106 | layerDrawable.setLayerInset(1, inset, inset, inset, inset) | ||
| 107 | val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path) | ||
| 108 | .setShortLabel(holder.game.title) | ||
| 109 | .setIcon( | ||
| 110 | IconCompat.createWithAdaptiveBitmap( | ||
| 111 | layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) | ||
| 112 | ) | 110 | ) |
| 113 | ) | 111 | val inset = YuzuApplication.appContext.resources |
| 114 | .setIntent(openIntent) | 112 | .getDimensionPixelSize(R.dimen.icon_inset) |
| 115 | .build() | 113 | layerDrawable.setLayerInset(1, inset, inset, inset, inset) |
| 116 | ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) | 114 | val shortcut = |
| 115 | ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path) | ||
| 116 | .setShortLabel(holder.game.title) | ||
| 117 | .setIcon( | ||
| 118 | IconCompat.createWithAdaptiveBitmap( | ||
| 119 | layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) | ||
| 120 | ) | ||
| 121 | ) | ||
| 122 | .setIntent(openIntent) | ||
| 123 | .build() | ||
| 124 | ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) | ||
| 125 | } | ||
| 126 | } | ||
| 117 | 127 | ||
| 118 | val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) | 128 | val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) |
| 119 | view.findNavController().navigate(action) | 129 | view.findNavController().navigate(action) |
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 08e2a973d..d005c656e 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 | |||
| @@ -7,7 +7,7 @@ import android.text.TextUtils | |||
| 7 | import android.widget.Toast | 7 | import android.widget.Toast |
| 8 | import org.yuzu.yuzu_emu.R | 8 | import org.yuzu.yuzu_emu.R |
| 9 | import org.yuzu.yuzu_emu.YuzuApplication | 9 | import org.yuzu.yuzu_emu.YuzuApplication |
| 10 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | 10 | import org.yuzu.yuzu_emu.utils.NativeConfig |
| 11 | 11 | ||
| 12 | object Settings { | 12 | object Settings { |
| 13 | private val context get() = YuzuApplication.appContext | 13 | private val context get() = YuzuApplication.appContext |
| @@ -19,7 +19,7 @@ object Settings { | |||
| 19 | context.getString(R.string.ini_saved), | 19 | context.getString(R.string.ini_saved), |
| 20 | Toast.LENGTH_SHORT | 20 | Toast.LENGTH_SHORT |
| 21 | ).show() | 21 | ).show() |
| 22 | SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG) | 22 | NativeConfig.saveSettings() |
| 23 | } else { | 23 | } else { |
| 24 | // TODO: Save custom game settings | 24 | // TODO: Save custom game settings |
| 25 | Toast.makeText( | 25 | Toast.makeText( |
| @@ -82,7 +82,6 @@ object Settings { | |||
| 82 | 82 | ||
| 83 | enum class MenuTag(val titleId: Int) { | 83 | enum class MenuTag(val titleId: Int) { |
| 84 | SECTION_ROOT(R.string.advanced_settings), | 84 | SECTION_ROOT(R.string.advanced_settings), |
| 85 | SECTION_GENERAL(R.string.preferences_general), | ||
| 86 | SECTION_SYSTEM(R.string.preferences_system), | 85 | SECTION_SYSTEM(R.string.preferences_system), |
| 87 | SECTION_RENDERER(R.string.preferences_graphics), | 86 | SECTION_RENDERER(R.string.preferences_graphics), |
| 88 | SECTION_AUDIO(R.string.preferences_audio), | 87 | SECTION_AUDIO(R.string.preferences_audio), |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt index 522cc49df..425160024 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt | |||
| @@ -3,10 +3,13 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model.view | 4 | package org.yuzu.yuzu_emu.features.settings.model.view |
| 5 | 5 | ||
| 6 | import androidx.annotation.DrawableRes | ||
| 7 | |||
| 6 | class RunnableSetting( | 8 | class RunnableSetting( |
| 7 | titleId: Int, | 9 | titleId: Int, |
| 8 | descriptionId: Int, | 10 | descriptionId: Int, |
| 9 | val isRuntimeRunnable: Boolean, | 11 | val isRuntimeRunnable: Boolean, |
| 12 | @DrawableRes val iconId: Int = 0, | ||
| 10 | val runnable: () -> Unit | 13 | val runnable: () -> Unit |
| 11 | ) : SettingsItem(emptySetting, titleId, descriptionId) { | 14 | ) : SettingsItem(emptySetting, titleId, descriptionId) { |
| 12 | override val type = TYPE_RUNNABLE | 15 | override val type = TYPE_RUNNABLE |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index b3b3fc209..6aba69dbe 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt | |||
| @@ -73,7 +73,7 @@ abstract class SettingsItem( | |||
| 73 | R.string.frame_limit_slider, | 73 | R.string.frame_limit_slider, |
| 74 | R.string.frame_limit_slider_description, | 74 | R.string.frame_limit_slider_description, |
| 75 | 1, | 75 | 1, |
| 76 | 200, | 76 | 400, |
| 77 | "%" | 77 | "%" |
| 78 | ) | 78 | ) |
| 79 | ) | 79 | ) |
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 b343e527e..94953b18a 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,11 +3,14 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model.view | 4 | package org.yuzu.yuzu_emu.features.settings.model.view |
| 5 | 5 | ||
| 6 | import androidx.annotation.DrawableRes | ||
| 7 | import androidx.annotation.StringRes | ||
| 6 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 8 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 7 | 9 | ||
| 8 | class SubmenuSetting( | 10 | class SubmenuSetting( |
| 9 | titleId: Int, | 11 | @StringRes titleId: Int, |
| 10 | descriptionId: Int, | 12 | @StringRes descriptionId: Int, |
| 13 | @DrawableRes val iconId: Int, | ||
| 11 | val menuKey: Settings.MenuTag | 14 | val menuKey: Settings.MenuTag |
| 12 | ) : SettingsItem(emptySetting, titleId, descriptionId) { | 15 | ) : SettingsItem(emptySetting, titleId, descriptionId) { |
| 13 | override val type = TYPE_SUBMENU | 16 | override val type = TYPE_SUBMENU |
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 c73edd50e..48bdbdd75 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 | |||
| @@ -21,7 +21,6 @@ import androidx.navigation.navArgs | |||
| 21 | import com.google.android.material.color.MaterialColors | 21 | import com.google.android.material.color.MaterialColors |
| 22 | import kotlinx.coroutines.flow.collectLatest | 22 | import kotlinx.coroutines.flow.collectLatest |
| 23 | import kotlinx.coroutines.launch | 23 | import kotlinx.coroutines.launch |
| 24 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 25 | import java.io.IOException | 24 | import java.io.IOException |
| 26 | import org.yuzu.yuzu_emu.R | 25 | import org.yuzu.yuzu_emu.R |
| 27 | import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding | 26 | import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding |
| @@ -165,11 +164,12 @@ class SettingsActivity : AppCompatActivity() { | |||
| 165 | settingsViewModel.shouldSave = false | 164 | settingsViewModel.shouldSave = false |
| 166 | 165 | ||
| 167 | // Delete settings file because the user may have changed values that do not exist in the UI | 166 | // Delete settings file because the user may have changed values that do not exist in the UI |
| 167 | NativeConfig.unloadConfig() | ||
| 168 | val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) | 168 | val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) |
| 169 | if (!settingsFile.delete()) { | 169 | if (!settingsFile.delete()) { |
| 170 | throw IOException("Failed to delete $settingsFile") | 170 | throw IOException("Failed to delete $settingsFile") |
| 171 | } | 171 | } |
| 172 | NativeLibrary.reloadSettings() | 172 | NativeConfig.initializeConfig() |
| 173 | 173 | ||
| 174 | Toast.makeText( | 174 | Toast.makeText( |
| 175 | applicationContext, | 175 | applicationContext, |
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 70d8ec14b..769baf744 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 | |||
| @@ -20,7 +20,6 @@ import androidx.lifecycle.repeatOnLifecycle | |||
| 20 | import androidx.navigation.findNavController | 20 | import androidx.navigation.findNavController |
| 21 | import androidx.navigation.fragment.navArgs | 21 | import androidx.navigation.fragment.navArgs |
| 22 | import androidx.recyclerview.widget.LinearLayoutManager | 22 | import androidx.recyclerview.widget.LinearLayoutManager |
| 23 | import com.google.android.material.divider.MaterialDividerItemDecoration | ||
| 24 | import com.google.android.material.transition.MaterialSharedAxis | 23 | import com.google.android.material.transition.MaterialSharedAxis |
| 25 | import kotlinx.coroutines.flow.collectLatest | 24 | import kotlinx.coroutines.flow.collectLatest |
| 26 | import kotlinx.coroutines.launch | 25 | import kotlinx.coroutines.launch |
| @@ -68,15 +67,9 @@ class SettingsFragment : Fragment() { | |||
| 68 | ) | 67 | ) |
| 69 | 68 | ||
| 70 | binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId) | 69 | binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId) |
| 71 | val dividerDecoration = MaterialDividerItemDecoration( | ||
| 72 | requireContext(), | ||
| 73 | LinearLayoutManager.VERTICAL | ||
| 74 | ) | ||
| 75 | dividerDecoration.isLastItemDecorated = false | ||
| 76 | binding.listSettings.apply { | 70 | binding.listSettings.apply { |
| 77 | adapter = settingsAdapter | 71 | adapter = settingsAdapter |
| 78 | layoutManager = LinearLayoutManager(requireContext()) | 72 | layoutManager = LinearLayoutManager(requireContext()) |
| 79 | addItemDecoration(dividerDecoration) | ||
| 80 | } | 73 | } |
| 81 | 74 | ||
| 82 | binding.toolbarSettings.setNavigationOnClickListener { | 75 | binding.toolbarSettings.setNavigationOnClickListener { |
| @@ -94,17 +87,6 @@ class SettingsFragment : Fragment() { | |||
| 94 | } | 87 | } |
| 95 | } | 88 | } |
| 96 | } | 89 | } |
| 97 | launch { | ||
| 98 | settingsViewModel.isUsingSearch.collectLatest { | ||
| 99 | if (it) { | ||
| 100 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) | ||
| 101 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) | ||
| 102 | } else { | ||
| 103 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) | ||
| 104 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) | ||
| 105 | } | ||
| 106 | } | ||
| 107 | } | ||
| 108 | } | 90 | } |
| 109 | 91 | ||
| 110 | if (args.menuTag == Settings.MenuTag.SECTION_ROOT) { | 92 | if (args.menuTag == Settings.MenuTag.SECTION_ROOT) { |
| @@ -112,8 +94,6 @@ class SettingsFragment : Fragment() { | |||
| 112 | binding.toolbarSettings.setOnMenuItemClickListener { | 94 | binding.toolbarSettings.setOnMenuItemClickListener { |
| 113 | when (it.itemId) { | 95 | when (it.itemId) { |
| 114 | R.id.action_search -> { | 96 | R.id.action_search -> { |
| 115 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) | ||
| 116 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) | ||
| 117 | view.findNavController() | 97 | view.findNavController() |
| 118 | .navigate(R.id.action_settingsFragment_to_settingsSearchFragment) | 98 | .navigate(R.id.action_settingsFragment_to_settingsSearchFragment) |
| 119 | true | 99 | true |
| @@ -129,11 +109,6 @@ class SettingsFragment : Fragment() { | |||
| 129 | setInsets() | 109 | setInsets() |
| 130 | } | 110 | } |
| 131 | 111 | ||
| 132 | override fun onResume() { | ||
| 133 | super.onResume() | ||
| 134 | settingsViewModel.setIsUsingSearch(false) | ||
| 135 | } | ||
| 136 | |||
| 137 | private fun setInsets() { | 112 | private fun setInsets() { |
| 138 | ViewCompat.setOnApplyWindowInsetsListener( | 113 | ViewCompat.setOnApplyWindowInsetsListener( |
| 139 | binding.root | 114 | binding.root |
| @@ -144,10 +119,9 @@ class SettingsFragment : Fragment() { | |||
| 144 | val leftInsets = barInsets.left + cutoutInsets.left | 119 | val leftInsets = barInsets.left + cutoutInsets.left |
| 145 | val rightInsets = barInsets.right + cutoutInsets.right | 120 | val rightInsets = barInsets.right + cutoutInsets.right |
| 146 | 121 | ||
| 147 | val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge) | ||
| 148 | val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams | 122 | val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams |
| 149 | mlpSettingsList.leftMargin = sideMargin + leftInsets | 123 | mlpSettingsList.leftMargin = leftInsets |
| 150 | mlpSettingsList.rightMargin = sideMargin + rightInsets | 124 | mlpSettingsList.rightMargin = rightInsets |
| 151 | binding.listSettings.layoutParams = mlpSettingsList | 125 | binding.listSettings.layoutParams = mlpSettingsList |
| 152 | binding.listSettings.updatePadding( | 126 | binding.listSettings.updatePadding( |
| 153 | bottom = barInsets.bottom | 127 | bottom = barInsets.bottom |
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 766414a6c..8b71e32f3 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 | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.ui | 4 | package org.yuzu.yuzu_emu.features.settings.ui |
| 5 | 5 | ||
| 6 | import android.content.Context | ||
| 7 | import android.content.SharedPreferences | 6 | import android.content.SharedPreferences |
| 8 | import android.os.Build | 7 | import android.os.Build |
| 9 | import android.widget.Toast | 8 | import android.widget.Toast |
| @@ -32,8 +31,6 @@ class SettingsFragmentPresenter( | |||
| 32 | private val preferences: SharedPreferences | 31 | private val preferences: SharedPreferences |
| 33 | get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | 32 | get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| 34 | 33 | ||
| 35 | private val context: Context get() = YuzuApplication.appContext | ||
| 36 | |||
| 37 | // Extension for populating settings list based on paired settings | 34 | // Extension for populating settings list based on paired settings |
| 38 | fun ArrayList<SettingsItem>.add(key: String) { | 35 | fun ArrayList<SettingsItem>.add(key: String) { |
| 39 | val item = SettingsItem.settingsItems[key]!! | 36 | val item = SettingsItem.settingsItems[key]!! |
| @@ -53,7 +50,6 @@ class SettingsFragmentPresenter( | |||
| 53 | val sl = ArrayList<SettingsItem>() | 50 | val sl = ArrayList<SettingsItem>() |
| 54 | when (menuTag) { | 51 | when (menuTag) { |
| 55 | Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl) | 52 | Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl) |
| 56 | Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl) | ||
| 57 | Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) | 53 | Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) |
| 58 | Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) | 54 | Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) |
| 59 | Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl) | 55 | Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl) |
| @@ -75,30 +71,53 @@ class SettingsFragmentPresenter( | |||
| 75 | 71 | ||
| 76 | private fun addConfigSettings(sl: ArrayList<SettingsItem>) { | 72 | private fun addConfigSettings(sl: ArrayList<SettingsItem>) { |
| 77 | sl.apply { | 73 | sl.apply { |
| 78 | add(SubmenuSetting(R.string.preferences_general, 0, Settings.MenuTag.SECTION_GENERAL)) | ||
| 79 | add(SubmenuSetting(R.string.preferences_system, 0, Settings.MenuTag.SECTION_SYSTEM)) | ||
| 80 | add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.MenuTag.SECTION_RENDERER)) | ||
| 81 | add(SubmenuSetting(R.string.preferences_audio, 0, Settings.MenuTag.SECTION_AUDIO)) | ||
| 82 | add(SubmenuSetting(R.string.preferences_debug, 0, Settings.MenuTag.SECTION_DEBUG)) | ||
| 83 | add( | 74 | add( |
| 84 | RunnableSetting(R.string.reset_to_default, 0, false) { | 75 | SubmenuSetting( |
| 85 | settingsViewModel.setShouldShowResetSettingsDialog(true) | 76 | R.string.preferences_system, |
| 86 | } | 77 | R.string.preferences_system_description, |
| 78 | R.drawable.ic_system_settings, | ||
| 79 | Settings.MenuTag.SECTION_SYSTEM | ||
| 80 | ) | ||
| 81 | ) | ||
| 82 | add( | ||
| 83 | SubmenuSetting( | ||
| 84 | R.string.preferences_graphics, | ||
| 85 | R.string.preferences_graphics_description, | ||
| 86 | R.drawable.ic_graphics, | ||
| 87 | Settings.MenuTag.SECTION_RENDERER | ||
| 88 | ) | ||
| 89 | ) | ||
| 90 | add( | ||
| 91 | SubmenuSetting( | ||
| 92 | R.string.preferences_audio, | ||
| 93 | R.string.preferences_audio_description, | ||
| 94 | R.drawable.ic_audio, | ||
| 95 | Settings.MenuTag.SECTION_AUDIO | ||
| 96 | ) | ||
| 97 | ) | ||
| 98 | add( | ||
| 99 | SubmenuSetting( | ||
| 100 | R.string.preferences_debug, | ||
| 101 | R.string.preferences_debug_description, | ||
| 102 | R.drawable.ic_code, | ||
| 103 | Settings.MenuTag.SECTION_DEBUG | ||
| 104 | ) | ||
| 105 | ) | ||
| 106 | add( | ||
| 107 | RunnableSetting( | ||
| 108 | R.string.reset_to_default, | ||
| 109 | R.string.reset_to_default_description, | ||
| 110 | false, | ||
| 111 | R.drawable.ic_restore | ||
| 112 | ) { settingsViewModel.setShouldShowResetSettingsDialog(true) } | ||
| 87 | ) | 113 | ) |
| 88 | } | 114 | } |
| 89 | } | 115 | } |
| 90 | 116 | ||
| 91 | private fun addGeneralSettings(sl: ArrayList<SettingsItem>) { | 117 | private fun addSystemSettings(sl: ArrayList<SettingsItem>) { |
| 92 | sl.apply { | 118 | sl.apply { |
| 93 | add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key) | 119 | add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key) |
| 94 | add(ShortSetting.RENDERER_SPEED_LIMIT.key) | 120 | add(ShortSetting.RENDERER_SPEED_LIMIT.key) |
| 95 | add(IntSetting.CPU_ACCURACY.key) | ||
| 96 | add(BooleanSetting.PICTURE_IN_PICTURE.key) | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | private fun addSystemSettings(sl: ArrayList<SettingsItem>) { | ||
| 101 | sl.apply { | ||
| 102 | add(BooleanSetting.USE_DOCKED_MODE.key) | 121 | add(BooleanSetting.USE_DOCKED_MODE.key) |
| 103 | add(IntSetting.REGION_INDEX.key) | 122 | add(IntSetting.REGION_INDEX.key) |
| 104 | add(IntSetting.LANGUAGE_INDEX.key) | 123 | add(IntSetting.LANGUAGE_INDEX.key) |
| @@ -116,6 +135,7 @@ class SettingsFragmentPresenter( | |||
| 116 | add(IntSetting.RENDERER_ANTI_ALIASING.key) | 135 | add(IntSetting.RENDERER_ANTI_ALIASING.key) |
| 117 | add(IntSetting.RENDERER_SCREEN_LAYOUT.key) | 136 | add(IntSetting.RENDERER_SCREEN_LAYOUT.key) |
| 118 | add(IntSetting.RENDERER_ASPECT_RATIO.key) | 137 | add(IntSetting.RENDERER_ASPECT_RATIO.key) |
| 138 | add(BooleanSetting.PICTURE_IN_PICTURE.key) | ||
| 119 | add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) | 139 | add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) |
| 120 | add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) | 140 | add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) |
| 121 | add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) | 141 | add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) |
| @@ -249,6 +269,7 @@ class SettingsFragmentPresenter( | |||
| 249 | add(BooleanSetting.RENDERER_DEBUG.key) | 269 | add(BooleanSetting.RENDERER_DEBUG.key) |
| 250 | 270 | ||
| 251 | add(HeaderSetting(R.string.cpu)) | 271 | add(HeaderSetting(R.string.cpu)) |
| 272 | add(IntSetting.CPU_ACCURACY.key) | ||
| 252 | add(BooleanSetting.CPU_DEBUG_MODE.key) | 273 | add(BooleanSetting.CPU_DEBUG_MODE.key) |
| 253 | add(SettingsItem.FASTMEM_COMBINED) | 274 | add(SettingsItem.FASTMEM_COMBINED) |
| 254 | } | 275 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt index 83a2e94f1..036195624 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.ui.viewholder | 4 | package org.yuzu.yuzu_emu.features.settings.ui.viewholder |
| 5 | 5 | ||
| 6 | import android.view.View | 6 | import android.view.View |
| 7 | import androidx.core.content.res.ResourcesCompat | ||
| 7 | import org.yuzu.yuzu_emu.NativeLibrary | 8 | import org.yuzu.yuzu_emu.NativeLibrary |
| 8 | import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding | 9 | import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding |
| 9 | import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting | 10 | import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting |
| @@ -16,6 +17,19 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA | |||
| 16 | 17 | ||
| 17 | override fun bind(item: SettingsItem) { | 18 | override fun bind(item: SettingsItem) { |
| 18 | setting = item as RunnableSetting | 19 | setting = item as RunnableSetting |
| 20 | if (item.iconId != 0) { | ||
| 21 | binding.icon.visibility = View.VISIBLE | ||
| 22 | binding.icon.setImageDrawable( | ||
| 23 | ResourcesCompat.getDrawable( | ||
| 24 | binding.icon.resources, | ||
| 25 | item.iconId, | ||
| 26 | binding.icon.context.theme | ||
| 27 | ) | ||
| 28 | ) | ||
| 29 | } else { | ||
| 30 | binding.icon.visibility = View.GONE | ||
| 31 | } | ||
| 32 | |||
| 19 | binding.textSettingName.setText(item.nameId) | 33 | binding.textSettingName.setText(item.nameId) |
| 20 | if (item.descriptionId != 0) { | 34 | if (item.descriptionId != 0) { |
| 21 | binding.textSettingDescription.setText(item.descriptionId) | 35 | binding.textSettingDescription.setText(item.descriptionId) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt index 1cf581a9d..8100c65dd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | package org.yuzu.yuzu_emu.features.settings.ui.viewholder | 4 | package org.yuzu.yuzu_emu.features.settings.ui.viewholder |
| 5 | 5 | ||
| 6 | import android.view.View | 6 | import android.view.View |
| 7 | import androidx.core.content.res.ResourcesCompat | ||
| 7 | import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding | 8 | import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding |
| 8 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | 9 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem |
| 9 | import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting | 10 | import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting |
| @@ -15,6 +16,19 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd | |||
| 15 | 16 | ||
| 16 | override fun bind(item: SettingsItem) { | 17 | override fun bind(item: SettingsItem) { |
| 17 | this.item = item as SubmenuSetting | 18 | this.item = item as SubmenuSetting |
| 19 | if (item.iconId != 0) { | ||
| 20 | binding.icon.visibility = View.VISIBLE | ||
| 21 | binding.icon.setImageDrawable( | ||
| 22 | ResourcesCompat.getDrawable( | ||
| 23 | binding.icon.resources, | ||
| 24 | item.iconId, | ||
| 25 | binding.icon.context.theme | ||
| 26 | ) | ||
| 27 | ) | ||
| 28 | } else { | ||
| 29 | binding.icon.visibility = View.GONE | ||
| 30 | } | ||
| 31 | |||
| 18 | binding.textSettingName.setText(item.nameId) | 32 | binding.textSettingName.setText(item.nameId) |
| 19 | if (item.descriptionId != 0) { | 33 | if (item.descriptionId != 0) { |
| 20 | binding.textSettingDescription.setText(item.descriptionId) | 34 | binding.textSettingDescription.setText(item.descriptionId) |
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 2b04d666a..3ae5b4653 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,15 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.utils | 4 | package org.yuzu.yuzu_emu.features.settings.utils |
| 5 | 5 | ||
| 6 | import android.widget.Toast | ||
| 7 | import java.io.* | 6 | import java.io.* |
| 8 | import org.ini4j.Wini | ||
| 9 | import org.yuzu.yuzu_emu.R | ||
| 10 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 11 | import org.yuzu.yuzu_emu.features.settings.model.* | ||
| 12 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization | 7 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization |
| 13 | import org.yuzu.yuzu_emu.utils.Log | ||
| 14 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 15 | 8 | ||
| 16 | /** | 9 | /** |
| 17 | * Contains static methods for interacting with .ini files in which settings are stored. | 10 | * Contains static methods for interacting with .ini files in which settings are stored. |
| @@ -19,41 +12,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig | |||
| 19 | object SettingsFile { | 12 | object SettingsFile { |
| 20 | const val FILE_NAME_CONFIG = "config" | 13 | const val FILE_NAME_CONFIG = "config" |
| 21 | 14 | ||
| 22 | /** | ||
| 23 | * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error | ||
| 24 | * telling why it failed. | ||
| 25 | * | ||
| 26 | * @param fileName The target filename without a path or extension. | ||
| 27 | */ | ||
| 28 | fun saveFile(fileName: String) { | ||
| 29 | val ini = getSettingsFile(fileName) | ||
| 30 | try { | ||
| 31 | val wini = Wini(ini) | ||
| 32 | for (specificCategory in Settings.Category.values()) { | ||
| 33 | val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal) | ||
| 34 | for (setting in Settings.settingsList) { | ||
| 35 | if (setting.key!!.isEmpty()) continue | ||
| 36 | |||
| 37 | val settingCategoryHeader = | ||
| 38 | NativeConfig.getConfigHeader(setting.category.ordinal) | ||
| 39 | val iniSetting: String? = wini.get(categoryHeader, setting.key) | ||
| 40 | if (iniSetting != null || settingCategoryHeader == categoryHeader) { | ||
| 41 | wini.put(settingCategoryHeader, setting.key, setting.valueAsString) | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | wini.store() | ||
| 46 | } catch (e: IOException) { | ||
| 47 | Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message) | ||
| 48 | val context = YuzuApplication.appContext | ||
| 49 | Toast.makeText( | ||
| 50 | context, | ||
| 51 | context.getString(R.string.error_saving, fileName, e.message), | ||
| 52 | Toast.LENGTH_SHORT | ||
| 53 | ).show() | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | fun getSettingsFile(fileName: String): File = | 15 | fun getSettingsFile(fileName: String): File = |
| 58 | File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini") | 16 | File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini") |
| 59 | } | 17 | } |
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 2ff827c6b..a1620fbb7 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 | |||
| @@ -114,10 +114,10 @@ class AboutFragment : Fragment() { | |||
| 114 | val leftInsets = barInsets.left + cutoutInsets.left | 114 | val leftInsets = barInsets.left + cutoutInsets.left |
| 115 | val rightInsets = barInsets.right + cutoutInsets.right | 115 | val rightInsets = barInsets.right + cutoutInsets.right |
| 116 | 116 | ||
| 117 | val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams | 117 | val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams |
| 118 | mlpAppBar.leftMargin = leftInsets | 118 | mlpToolbar.leftMargin = leftInsets |
| 119 | mlpAppBar.rightMargin = rightInsets | 119 | mlpToolbar.rightMargin = rightInsets |
| 120 | binding.appbarAbout.layoutParams = mlpAppBar | 120 | binding.toolbarAbout.layoutParams = mlpToolbar |
| 121 | 121 | ||
| 122 | val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams | 122 | val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams |
| 123 | mlpScrollAbout.leftMargin = leftInsets | 123 | mlpScrollAbout.leftMargin = leftInsets |
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 07bd78bf7..c32fa0d7e 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 | |||
| @@ -10,7 +10,6 @@ import android.content.DialogInterface | |||
| 10 | import android.content.SharedPreferences | 10 | import android.content.SharedPreferences |
| 11 | import android.content.pm.ActivityInfo | 11 | import android.content.pm.ActivityInfo |
| 12 | import android.content.res.Configuration | 12 | import android.content.res.Configuration |
| 13 | import android.graphics.Color | ||
| 14 | import android.net.Uri | 13 | import android.net.Uri |
| 15 | import android.os.Bundle | 14 | import android.os.Bundle |
| 16 | import android.os.Handler | 15 | import android.os.Handler |
| @@ -155,7 +154,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 155 | } | 154 | } |
| 156 | 155 | ||
| 157 | binding.surfaceEmulation.holder.addCallback(this) | 156 | binding.surfaceEmulation.holder.addCallback(this) |
| 158 | binding.showFpsText.setTextColor(Color.YELLOW) | ||
| 159 | binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } | 157 | binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } |
| 160 | 158 | ||
| 161 | binding.drawerLayout.addDrawerListener(object : DrawerListener { | 159 | binding.drawerLayout.addDrawerListener(object : DrawerListener { |
| @@ -312,6 +310,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 312 | ViewUtils.showView(binding.surfaceInputOverlay) | 310 | ViewUtils.showView(binding.surfaceInputOverlay) |
| 313 | ViewUtils.hideView(binding.loadingIndicator) | 311 | ViewUtils.hideView(binding.loadingIndicator) |
| 314 | 312 | ||
| 313 | emulationState.updateSurface() | ||
| 314 | |||
| 315 | // Setup overlay | 315 | // Setup overlay |
| 316 | updateShowFpsOverlay() | 316 | updateShowFpsOverlay() |
| 317 | } | 317 | } |
| @@ -412,12 +412,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 412 | val FRAMETIME = 2 | 412 | val FRAMETIME = 2 |
| 413 | val SPEED = 3 | 413 | val SPEED = 3 |
| 414 | perfStatsUpdater = { | 414 | perfStatsUpdater = { |
| 415 | if (emulationViewModel.emulationStarted.value == true) { | 415 | if (emulationViewModel.emulationStarted.value) { |
| 416 | val perfStats = NativeLibrary.getPerfStats() | 416 | val perfStats = NativeLibrary.getPerfStats() |
| 417 | if (perfStats[FPS] > 0 && _binding != null) { | 417 | if (_binding != null) { |
| 418 | binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) | 418 | binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) |
| 419 | } | 419 | } |
| 420 | perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) | 420 | perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800) |
| 421 | } | 421 | } |
| 422 | } | 422 | } |
| 423 | perfStatsUpdateHandler.post(perfStatsUpdater!!) | 423 | perfStatsUpdateHandler.post(perfStatsUpdater!!) |
| @@ -462,7 +462,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 462 | if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { | 462 | if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { |
| 463 | // Restrict emulation and overlays to the top of the screen | 463 | // Restrict emulation and overlays to the top of the screen |
| 464 | binding.emulationContainer.layoutParams.height = it.bounds.top | 464 | binding.emulationContainer.layoutParams.height = it.bounds.top |
| 465 | binding.overlayContainer.layoutParams.height = it.bounds.top | ||
| 466 | // Restrict input and menu drawer to the bottom of the screen | 465 | // Restrict input and menu drawer to the bottom of the screen |
| 467 | binding.inputContainer.layoutParams.height = it.bounds.bottom | 466 | binding.inputContainer.layoutParams.height = it.bounds.bottom |
| 468 | binding.inGameMenu.layoutParams.height = it.bounds.bottom | 467 | binding.inGameMenu.layoutParams.height = it.bounds.bottom |
| @@ -476,7 +475,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 476 | if (!isFolding) { | 475 | if (!isFolding) { |
| 477 | binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT | 476 | binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT |
| 478 | binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT | 477 | binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT |
| 479 | binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT | ||
| 480 | binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT | 478 | binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT |
| 481 | isInFoldableLayout = false | 479 | isInFoldableLayout = false |
| 482 | updateOrientation() | 480 | updateOrientation() |
| @@ -484,7 +482,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 484 | } | 482 | } |
| 485 | binding.emulationContainer.requestLayout() | 483 | binding.emulationContainer.requestLayout() |
| 486 | binding.inputContainer.requestLayout() | 484 | binding.inputContainer.requestLayout() |
| 487 | binding.overlayContainer.requestLayout() | ||
| 488 | binding.inGameMenu.requestLayout() | 485 | binding.inGameMenu.requestLayout() |
| 489 | } | 486 | } |
| 490 | 487 | ||
| @@ -710,24 +707,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 710 | } | 707 | } |
| 711 | 708 | ||
| 712 | v.setPadding(left, cutInsets.top, right, 0) | 709 | v.setPadding(left, cutInsets.top, right, 0) |
| 713 | |||
| 714 | // Ensure FPS text doesn't get cut off by rounded display corners | ||
| 715 | val sidePadding = resources.getDimensionPixelSize(R.dimen.spacing_xtralarge) | ||
| 716 | if (cutInsets.left == 0) { | ||
| 717 | binding.showFpsText.setPadding( | ||
| 718 | sidePadding, | ||
| 719 | cutInsets.top, | ||
| 720 | cutInsets.right, | ||
| 721 | cutInsets.bottom | ||
| 722 | ) | ||
| 723 | } else { | ||
| 724 | binding.showFpsText.setPadding( | ||
| 725 | cutInsets.left, | ||
| 726 | cutInsets.top, | ||
| 727 | cutInsets.right, | ||
| 728 | cutInsets.bottom | ||
| 729 | ) | ||
| 730 | } | ||
| 731 | windowInsets | 710 | windowInsets |
| 732 | } | 711 | } |
| 733 | } | 712 | } |
| @@ -805,6 +784,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 805 | } | 784 | } |
| 806 | 785 | ||
| 807 | @Synchronized | 786 | @Synchronized |
| 787 | fun updateSurface() { | ||
| 788 | if (surface != null) { | ||
| 789 | NativeLibrary.surfaceChanged(surface) | ||
| 790 | } | ||
| 791 | } | ||
| 792 | |||
| 793 | @Synchronized | ||
| 808 | fun clearSurface() { | 794 | fun clearSurface() { |
| 809 | if (surface == null) { | 795 | if (surface == null) { |
| 810 | Log.warning("[EmulationFragment] clearSurface called, but surface already null.") | 796 | Log.warning("[EmulationFragment] clearSurface called, but surface already null.") |
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 6e19fc6c0..4720daec4 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 | |||
| @@ -42,6 +42,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel | |||
| 42 | import org.yuzu.yuzu_emu.ui.main.MainActivity | 42 | import org.yuzu.yuzu_emu.ui.main.MainActivity |
| 43 | import org.yuzu.yuzu_emu.utils.FileUtil | 43 | import org.yuzu.yuzu_emu.utils.FileUtil |
| 44 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper | 44 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper |
| 45 | import org.yuzu.yuzu_emu.utils.Log | ||
| 45 | 46 | ||
| 46 | class HomeSettingsFragment : Fragment() { | 47 | class HomeSettingsFragment : Fragment() { |
| 47 | private var _binding: FragmentHomeSettingsBinding? = null | 48 | private var _binding: FragmentHomeSettingsBinding? = null |
| @@ -86,28 +87,6 @@ class HomeSettingsFragment : Fragment() { | |||
| 86 | ) | 87 | ) |
| 87 | add( | 88 | add( |
| 88 | HomeSetting( | 89 | HomeSetting( |
| 89 | R.string.open_user_folder, | ||
| 90 | R.string.open_user_folder_description, | ||
| 91 | R.drawable.ic_folder_open, | ||
| 92 | { openFileManager() } | ||
| 93 | ) | ||
| 94 | ) | ||
| 95 | add( | ||
| 96 | HomeSetting( | ||
| 97 | R.string.preferences_theme, | ||
| 98 | R.string.theme_and_color_description, | ||
| 99 | R.drawable.ic_palette, | ||
| 100 | { | ||
| 101 | val action = HomeNavigationDirections.actionGlobalSettingsActivity( | ||
| 102 | null, | ||
| 103 | Settings.MenuTag.SECTION_THEME | ||
| 104 | ) | ||
| 105 | binding.root.findNavController().navigate(action) | ||
| 106 | } | ||
| 107 | ) | ||
| 108 | ) | ||
| 109 | add( | ||
| 110 | HomeSetting( | ||
| 111 | R.string.gpu_driver_manager, | 90 | R.string.gpu_driver_manager, |
| 112 | R.string.install_gpu_driver_description, | 91 | R.string.install_gpu_driver_description, |
| 113 | R.drawable.ic_build, | 92 | R.drawable.ic_build, |
| @@ -123,17 +102,6 @@ class HomeSettingsFragment : Fragment() { | |||
| 123 | ) | 102 | ) |
| 124 | add( | 103 | add( |
| 125 | HomeSetting( | 104 | HomeSetting( |
| 126 | R.string.manage_yuzu_data, | ||
| 127 | R.string.manage_yuzu_data_description, | ||
| 128 | R.drawable.ic_install, | ||
| 129 | { | ||
| 130 | binding.root.findNavController() | ||
| 131 | .navigate(R.id.action_homeSettingsFragment_to_installableFragment) | ||
| 132 | } | ||
| 133 | ) | ||
| 134 | ) | ||
| 135 | add( | ||
| 136 | HomeSetting( | ||
| 137 | R.string.applets, | 105 | R.string.applets, |
| 138 | R.string.applets_description, | 106 | R.string.applets_description, |
| 139 | R.drawable.ic_applet, | 107 | R.drawable.ic_applet, |
| @@ -148,6 +116,17 @@ class HomeSettingsFragment : Fragment() { | |||
| 148 | ) | 116 | ) |
| 149 | add( | 117 | add( |
| 150 | HomeSetting( | 118 | HomeSetting( |
| 119 | R.string.manage_yuzu_data, | ||
| 120 | R.string.manage_yuzu_data_description, | ||
| 121 | R.drawable.ic_install, | ||
| 122 | { | ||
| 123 | binding.root.findNavController() | ||
| 124 | .navigate(R.id.action_homeSettingsFragment_to_installableFragment) | ||
| 125 | } | ||
| 126 | ) | ||
| 127 | ) | ||
| 128 | add( | ||
| 129 | HomeSetting( | ||
| 151 | R.string.select_games_folder, | 130 | R.string.select_games_folder, |
| 152 | R.string.select_games_folder_description, | 131 | R.string.select_games_folder_description, |
| 153 | R.drawable.ic_add, | 132 | R.drawable.ic_add, |
| @@ -172,6 +151,28 @@ class HomeSettingsFragment : Fragment() { | |||
| 172 | ) | 151 | ) |
| 173 | add( | 152 | add( |
| 174 | HomeSetting( | 153 | HomeSetting( |
| 154 | R.string.open_user_folder, | ||
| 155 | R.string.open_user_folder_description, | ||
| 156 | R.drawable.ic_folder_open, | ||
| 157 | { openFileManager() } | ||
| 158 | ) | ||
| 159 | ) | ||
| 160 | add( | ||
| 161 | HomeSetting( | ||
| 162 | R.string.preferences_theme, | ||
| 163 | R.string.theme_and_color_description, | ||
| 164 | R.drawable.ic_palette, | ||
| 165 | { | ||
| 166 | val action = HomeNavigationDirections.actionGlobalSettingsActivity( | ||
| 167 | null, | ||
| 168 | Settings.MenuTag.SECTION_THEME | ||
| 169 | ) | ||
| 170 | binding.root.findNavController().navigate(action) | ||
| 171 | } | ||
| 172 | ) | ||
| 173 | ) | ||
| 174 | add( | ||
| 175 | HomeSetting( | ||
| 175 | R.string.about, | 176 | R.string.about, |
| 176 | R.string.about_description, | 177 | R.string.about_description, |
| 177 | R.drawable.ic_info_outline, | 178 | R.drawable.ic_info_outline, |
| @@ -312,19 +313,32 @@ class HomeSettingsFragment : Fragment() { | |||
| 312 | } | 313 | } |
| 313 | } | 314 | } |
| 314 | 315 | ||
| 316 | // Share the current log if we just returned from a game but share the old log | ||
| 317 | // if we just started the app and the old log exists. | ||
| 315 | private fun shareLog() { | 318 | private fun shareLog() { |
| 316 | val file = DocumentFile.fromSingleUri( | 319 | val currentLog = DocumentFile.fromSingleUri( |
| 317 | mainActivity, | 320 | mainActivity, |
| 318 | DocumentsContract.buildDocumentUri( | 321 | DocumentsContract.buildDocumentUri( |
| 319 | DocumentProvider.AUTHORITY, | 322 | DocumentProvider.AUTHORITY, |
| 320 | "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt" | 323 | "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt" |
| 321 | ) | 324 | ) |
| 322 | )!! | 325 | )!! |
| 323 | if (file.exists()) { | 326 | val oldLog = DocumentFile.fromSingleUri( |
| 324 | val intent = Intent(Intent.ACTION_SEND) | 327 | mainActivity, |
| 325 | .setDataAndType(file.uri, FileUtil.TEXT_PLAIN) | 328 | DocumentsContract.buildDocumentUri( |
| 326 | .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) | 329 | DocumentProvider.AUTHORITY, |
| 327 | .putExtra(Intent.EXTRA_STREAM, file.uri) | 330 | "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt.old.txt" |
| 331 | ) | ||
| 332 | )!! | ||
| 333 | |||
| 334 | val intent = Intent(Intent.ACTION_SEND) | ||
| 335 | .setDataAndType(currentLog.uri, FileUtil.TEXT_PLAIN) | ||
| 336 | .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) | ||
| 337 | if (!Log.gameLaunched && oldLog.exists()) { | ||
| 338 | intent.putExtra(Intent.EXTRA_STREAM, oldLog.uri) | ||
| 339 | startActivity(Intent.createChooser(intent, getText(R.string.share_log))) | ||
| 340 | } else if (currentLog.exists()) { | ||
| 341 | intent.putExtra(Intent.EXTRA_STREAM, currentLog.uri) | ||
| 328 | startActivity(Intent.createChooser(intent, getText(R.string.share_log))) | 342 | startActivity(Intent.createChooser(intent, getText(R.string.share_log))) |
| 329 | } else { | 343 | } else { |
| 330 | Toast.makeText( | 344 | Toast.makeText( |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt index ec116ab62..6940fc757 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt | |||
| @@ -21,6 +21,8 @@ import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding | |||
| 21 | import org.yuzu.yuzu_emu.model.HomeViewModel | 21 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 22 | import org.yuzu.yuzu_emu.model.Installable | 22 | import org.yuzu.yuzu_emu.model.Installable |
| 23 | import org.yuzu.yuzu_emu.ui.main.MainActivity | 23 | import org.yuzu.yuzu_emu.ui.main.MainActivity |
| 24 | import java.time.LocalDateTime | ||
| 25 | import java.time.format.DateTimeFormatter | ||
| 24 | 26 | ||
| 25 | class InstallableFragment : Fragment() { | 27 | class InstallableFragment : Fragment() { |
| 26 | private var _binding: FragmentInstallablesBinding? = null | 28 | private var _binding: FragmentInstallablesBinding? = null |
| @@ -78,7 +80,15 @@ class InstallableFragment : Fragment() { | |||
| 78 | R.string.manage_save_data, | 80 | R.string.manage_save_data, |
| 79 | R.string.import_export_saves_description, | 81 | R.string.import_export_saves_description, |
| 80 | install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }, | 82 | install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }, |
| 81 | export = { mainActivity.exportSave() } | 83 | export = { |
| 84 | mainActivity.exportSaves.launch( | ||
| 85 | "yuzu saves - ${ | ||
| 86 | LocalDateTime.now().format( | ||
| 87 | DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") | ||
| 88 | ) | ||
| 89 | }.zip" | ||
| 90 | ) | ||
| 91 | } | ||
| 82 | ) | 92 | ) |
| 83 | } else { | 93 | } else { |
| 84 | Installable( | 94 | Installable( |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt index 9d0594c6e..f95d545bf 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt | |||
| @@ -40,8 +40,10 @@ class SettingsSearchFragment : Fragment() { | |||
| 40 | 40 | ||
| 41 | override fun onCreate(savedInstanceState: Bundle?) { | 41 | override fun onCreate(savedInstanceState: Bundle?) { |
| 42 | super.onCreate(savedInstanceState) | 42 | super.onCreate(savedInstanceState) |
| 43 | enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) | 43 | enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) |
| 44 | returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) | 44 | returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) |
| 45 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) | ||
| 46 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) | ||
| 45 | } | 47 | } |
| 46 | 48 | ||
| 47 | override fun onCreateView( | 49 | override fun onCreateView( |
| @@ -55,7 +57,6 @@ class SettingsSearchFragment : Fragment() { | |||
| 55 | 57 | ||
| 56 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | 58 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
| 57 | super.onViewCreated(view, savedInstanceState) | 59 | super.onViewCreated(view, savedInstanceState) |
| 58 | settingsViewModel.setIsUsingSearch(true) | ||
| 59 | 60 | ||
| 60 | if (savedInstanceState != null) { | 61 | if (savedInstanceState != null) { |
| 61 | binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) | 62 | binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) |
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 de84b2adb..2fa3ab31b 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 | |||
| @@ -18,8 +18,8 @@ class Game( | |||
| 18 | val version: String = "", | 18 | val version: String = "", |
| 19 | val isHomebrew: Boolean = false | 19 | val isHomebrew: Boolean = false |
| 20 | ) : Parcelable { | 20 | ) : Parcelable { |
| 21 | val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime" | 21 | val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime" |
| 22 | val keyLastPlayedTime get() = "${programId}_LastPlayed" | 22 | val keyLastPlayedTime get() = "${path}_LastPlayed" |
| 23 | 23 | ||
| 24 | override fun equals(other: Any?): Boolean { | 24 | override fun equals(other: Any?): Boolean { |
| 25 | if (other !is Game) { | 25 | if (other !is Game) { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt index 53fa7a8de..6f947674e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt | |||
| @@ -29,9 +29,6 @@ class SettingsViewModel : ViewModel() { | |||
| 29 | val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList | 29 | val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList |
| 30 | private val _shouldReloadSettingsList = MutableStateFlow(false) | 30 | private val _shouldReloadSettingsList = MutableStateFlow(false) |
| 31 | 31 | ||
| 32 | val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch | ||
| 33 | private val _isUsingSearch = MutableStateFlow(false) | ||
| 34 | |||
| 35 | val sliderProgress: StateFlow<Int> get() = _sliderProgress | 32 | val sliderProgress: StateFlow<Int> get() = _sliderProgress |
| 36 | private val _sliderProgress = MutableStateFlow(-1) | 33 | private val _sliderProgress = MutableStateFlow(-1) |
| 37 | 34 | ||
| @@ -57,10 +54,6 @@ class SettingsViewModel : ViewModel() { | |||
| 57 | _shouldReloadSettingsList.value = value | 54 | _shouldReloadSettingsList.value = value |
| 58 | } | 55 | } |
| 59 | 56 | ||
| 60 | fun setIsUsingSearch(value: Boolean) { | ||
| 61 | _isUsingSearch.value = value | ||
| 62 | } | ||
| 63 | |||
| 64 | fun setSliderTextValue(value: Float, units: String) { | 57 | fun setSliderTextValue(value: Float, units: String) { |
| 65 | _sliderProgress.value = value.toInt() | 58 | _sliderProgress.value = value.toInt() |
| 66 | _sliderTextValue.value = String.format( | 59 | _sliderTextValue.value = String.format( |
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 ba1177426..bd2f4cd25 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 | |||
| @@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.ui.main | |||
| 6 | import android.content.Intent | 6 | import android.content.Intent |
| 7 | import android.net.Uri | 7 | import android.net.Uri |
| 8 | import android.os.Bundle | 8 | import android.os.Bundle |
| 9 | import android.provider.DocumentsContract | ||
| 10 | import android.view.View | 9 | import android.view.View |
| 11 | import android.view.ViewGroup.MarginLayoutParams | 10 | import android.view.ViewGroup.MarginLayoutParams |
| 12 | import android.view.WindowManager | 11 | import android.view.WindowManager |
| @@ -20,7 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen | |||
| 20 | import androidx.core.view.ViewCompat | 19 | import androidx.core.view.ViewCompat |
| 21 | import androidx.core.view.WindowCompat | 20 | import androidx.core.view.WindowCompat |
| 22 | import androidx.core.view.WindowInsetsCompat | 21 | import androidx.core.view.WindowInsetsCompat |
| 23 | import androidx.documentfile.provider.DocumentFile | ||
| 24 | import androidx.lifecycle.Lifecycle | 22 | import androidx.lifecycle.Lifecycle |
| 25 | import androidx.lifecycle.lifecycleScope | 23 | import androidx.lifecycle.lifecycleScope |
| 26 | import androidx.lifecycle.repeatOnLifecycle | 24 | import androidx.lifecycle.repeatOnLifecycle |
| @@ -41,7 +39,6 @@ import org.yuzu.yuzu_emu.NativeLibrary | |||
| 41 | import org.yuzu.yuzu_emu.R | 39 | import org.yuzu.yuzu_emu.R |
| 42 | import org.yuzu.yuzu_emu.activities.EmulationActivity | 40 | import org.yuzu.yuzu_emu.activities.EmulationActivity |
| 43 | import org.yuzu.yuzu_emu.databinding.ActivityMainBinding | 41 | import org.yuzu.yuzu_emu.databinding.ActivityMainBinding |
| 44 | import org.yuzu.yuzu_emu.features.DocumentProvider | ||
| 45 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 42 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 46 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment | 43 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment |
| 47 | import org.yuzu.yuzu_emu.fragments.MessageDialogFragment | 44 | import org.yuzu.yuzu_emu.fragments.MessageDialogFragment |
| @@ -53,9 +50,6 @@ import org.yuzu.yuzu_emu.model.TaskViewModel | |||
| 53 | import org.yuzu.yuzu_emu.utils.* | 50 | import org.yuzu.yuzu_emu.utils.* |
| 54 | import java.io.BufferedInputStream | 51 | import java.io.BufferedInputStream |
| 55 | import java.io.BufferedOutputStream | 52 | import java.io.BufferedOutputStream |
| 56 | import java.io.FileOutputStream | ||
| 57 | import java.time.LocalDateTime | ||
| 58 | import java.time.format.DateTimeFormatter | ||
| 59 | import java.util.zip.ZipEntry | 53 | import java.util.zip.ZipEntry |
| 60 | import java.util.zip.ZipInputStream | 54 | import java.util.zip.ZipInputStream |
| 61 | 55 | ||
| @@ -73,7 +67,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 73 | 67 | ||
| 74 | // Get first subfolder in saves folder (should be the user folder) | 68 | // Get first subfolder in saves folder (should be the user folder) |
| 75 | val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" | 69 | val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" |
| 76 | private var lastZipCreated: File? = null | ||
| 77 | 70 | ||
| 78 | override fun onCreate(savedInstanceState: Bundle?) { | 71 | override fun onCreate(savedInstanceState: Bundle?) { |
| 79 | val splashScreen = installSplashScreen() | 72 | val splashScreen = installSplashScreen() |
| @@ -403,7 +396,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 403 | } else { | 396 | } else { |
| 404 | firmwarePath.deleteRecursively() | 397 | firmwarePath.deleteRecursively() |
| 405 | cacheFirmwareDir.copyRecursively(firmwarePath, true) | 398 | cacheFirmwareDir.copyRecursively(firmwarePath, true) |
| 406 | NativeLibrary.initializeSystem() | 399 | NativeLibrary.initializeSystem(true) |
| 407 | getString(R.string.save_file_imported_success) | 400 | getString(R.string.save_file_imported_success) |
| 408 | } | 401 | } |
| 409 | } catch (e: Exception) { | 402 | } catch (e: Exception) { |
| @@ -632,6 +625,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 632 | } | 625 | } |
| 633 | 626 | ||
| 634 | // Clear existing user data | 627 | // Clear existing user data |
| 628 | NativeConfig.unloadConfig() | ||
| 635 | File(DirectoryInitialization.userDirectory!!).deleteRecursively() | 629 | File(DirectoryInitialization.userDirectory!!).deleteRecursively() |
| 636 | 630 | ||
| 637 | // Copy archive to internal storage | 631 | // Copy archive to internal storage |
| @@ -649,7 +643,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 649 | } | 643 | } |
| 650 | 644 | ||
| 651 | // Reinitialize relevant data | 645 | // Reinitialize relevant data |
| 652 | NativeLibrary.initializeSystem() | 646 | NativeLibrary.initializeSystem(true) |
| 647 | NativeConfig.initializeConfig() | ||
| 653 | gamesViewModel.reloadGames(false) | 648 | gamesViewModel.reloadGames(false) |
| 654 | 649 | ||
| 655 | return@newInstance getString(R.string.user_data_import_success) | 650 | return@newInstance getString(R.string.user_data_import_success) |
| @@ -657,74 +652,30 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 657 | } | 652 | } |
| 658 | 653 | ||
| 659 | /** | 654 | /** |
| 660 | * Zips the save files located in the given folder path and creates a new zip file with the current date and time. | ||
| 661 | * @return true if the zip file is successfully created, false otherwise. | ||
| 662 | */ | ||
| 663 | private fun zipSave(): Boolean { | ||
| 664 | try { | ||
| 665 | val tempFolder = File(getPublicFilesDir().canonicalPath, "temp") | ||
| 666 | tempFolder.mkdirs() | ||
| 667 | val saveFolder = File(savesFolderRoot) | ||
| 668 | val outputZipFile = File( | ||
| 669 | tempFolder, | ||
| 670 | "yuzu saves - ${ | ||
| 671 | LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) | ||
| 672 | }.zip" | ||
| 673 | ) | ||
| 674 | outputZipFile.createNewFile() | ||
| 675 | val result = FileUtil.zipFromInternalStorage( | ||
| 676 | saveFolder, | ||
| 677 | savesFolderRoot, | ||
| 678 | BufferedOutputStream(FileOutputStream(outputZipFile)) | ||
| 679 | ) | ||
| 680 | if (result == TaskState.Failed) { | ||
| 681 | return false | ||
| 682 | } | ||
| 683 | lastZipCreated = outputZipFile | ||
| 684 | } catch (e: Exception) { | ||
| 685 | return false | ||
| 686 | } | ||
| 687 | return true | ||
| 688 | } | ||
| 689 | |||
| 690 | /** | ||
| 691 | * Exports the save file located in the given folder path by creating a zip file and sharing it via intent. | 655 | * Exports the save file located in the given folder path by creating a zip file and sharing it via intent. |
| 692 | */ | 656 | */ |
| 693 | fun exportSave() { | 657 | val exportSaves = registerForActivityResult( |
| 694 | CoroutineScope(Dispatchers.IO).launch { | 658 | ActivityResultContracts.CreateDocument("application/zip") |
| 695 | val wasZipCreated = zipSave() | 659 | ) { result -> |
| 696 | val lastZipFile = lastZipCreated | 660 | if (result == null) { |
| 697 | if (!wasZipCreated || lastZipFile == null) { | 661 | return@registerForActivityResult |
| 698 | withContext(Dispatchers.Main) { | 662 | } |
| 699 | Toast.makeText( | ||
| 700 | this@MainActivity, | ||
| 701 | getString(R.string.export_save_failed), | ||
| 702 | Toast.LENGTH_LONG | ||
| 703 | ).show() | ||
| 704 | } | ||
| 705 | return@launch | ||
| 706 | } | ||
| 707 | 663 | ||
| 708 | withContext(Dispatchers.Main) { | 664 | IndeterminateProgressDialogFragment.newInstance( |
| 709 | val file = DocumentFile.fromSingleUri( | 665 | this, |
| 710 | this@MainActivity, | 666 | R.string.save_files_exporting, |
| 711 | DocumentsContract.buildDocumentUri( | 667 | false |
| 712 | DocumentProvider.AUTHORITY, | 668 | ) { |
| 713 | "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}" | 669 | val zipResult = FileUtil.zipFromInternalStorage( |
| 714 | ) | 670 | File(savesFolderRoot), |
| 715 | )!! | 671 | savesFolderRoot, |
| 716 | val intent = Intent(Intent.ACTION_SEND) | 672 | BufferedOutputStream(contentResolver.openOutputStream(result)) |
| 717 | .setDataAndType(file.uri, "application/zip") | 673 | ) |
| 718 | .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) | 674 | return@newInstance when (zipResult) { |
| 719 | .putExtra(Intent.EXTRA_STREAM, file.uri) | 675 | TaskState.Completed -> getString(R.string.export_success) |
| 720 | startForResultExportSave.launch( | 676 | TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed) |
| 721 | Intent.createChooser( | ||
| 722 | intent, | ||
| 723 | getString(R.string.share_save_file) | ||
| 724 | ) | ||
| 725 | ) | ||
| 726 | } | 677 | } |
| 727 | } | 678 | }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) |
| 728 | } | 679 | } |
| 729 | 680 | ||
| 730 | private val startForResultExportSave = | 681 | private val startForResultExportSave = |
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 79a07f7ef..21270fc84 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 | |||
| @@ -15,7 +15,8 @@ object DirectoryInitialization { | |||
| 15 | fun start() { | 15 | fun start() { |
| 16 | if (!areDirectoriesReady) { | 16 | if (!areDirectoriesReady) { |
| 17 | initializeInternalStorage() | 17 | initializeInternalStorage() |
| 18 | NativeLibrary.initializeSystem() | 18 | NativeLibrary.initializeSystem(false) |
| 19 | NativeConfig.initializeConfig() | ||
| 19 | areDirectoriesReady = true | 20 | areDirectoriesReady = true |
| 20 | } | 21 | } |
| 21 | } | 22 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt index 654d62f52..2e9b0beb8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt | |||
| @@ -8,9 +8,9 @@ import android.graphics.BitmapFactory | |||
| 8 | import android.widget.ImageView | 8 | import android.widget.ImageView |
| 9 | import androidx.core.graphics.drawable.toBitmap | 9 | import androidx.core.graphics.drawable.toBitmap |
| 10 | import androidx.core.graphics.drawable.toDrawable | 10 | import androidx.core.graphics.drawable.toDrawable |
| 11 | import androidx.lifecycle.LifecycleOwner | ||
| 11 | import coil.ImageLoader | 12 | import coil.ImageLoader |
| 12 | import coil.decode.DataSource | 13 | import coil.decode.DataSource |
| 13 | import coil.executeBlocking | ||
| 14 | import coil.fetch.DrawableResult | 14 | import coil.fetch.DrawableResult |
| 15 | import coil.fetch.FetchResult | 15 | import coil.fetch.FetchResult |
| 16 | import coil.fetch.Fetcher | 16 | import coil.fetch.Fetcher |
| @@ -76,12 +76,13 @@ object GameIconUtils { | |||
| 76 | imageLoader.enqueue(request) | 76 | imageLoader.enqueue(request) |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | fun getGameIcon(game: Game): Bitmap { | 79 | suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap { |
| 80 | val request = ImageRequest.Builder(YuzuApplication.appContext) | 80 | val request = ImageRequest.Builder(YuzuApplication.appContext) |
| 81 | .data(game) | 81 | .data(game) |
| 82 | .lifecycle(lifecycleOwner) | ||
| 82 | .error(R.drawable.default_icon) | 83 | .error(R.drawable.default_icon) |
| 83 | .build() | 84 | .build() |
| 84 | return imageLoader.executeBlocking(request) | 85 | return imageLoader.execute(request) |
| 85 | .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888) | 86 | .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888) |
| 86 | } | 87 | } |
| 87 | } | 88 | } |
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 fc6a8b5cb..e63382e1d 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 | |||
| @@ -27,6 +27,8 @@ object InputHandler { | |||
| 27 | 0x054C -> getInputDS5ButtonKey(event.keyCode) | 27 | 0x054C -> getInputDS5ButtonKey(event.keyCode) |
| 28 | 0x057E -> getInputJoyconButtonKey(event.keyCode) | 28 | 0x057E -> getInputJoyconButtonKey(event.keyCode) |
| 29 | 0x1532 -> getInputRazerButtonKey(event.keyCode) | 29 | 0x1532 -> getInputRazerButtonKey(event.keyCode) |
| 30 | 0x3537 -> getInputRedmagicButtonKey(event.keyCode) | ||
| 31 | 0x358A -> getInputBackboneLabsButtonKey(event.keyCode) | ||
| 30 | else -> getInputGenericButtonKey(event.keyCode) | 32 | else -> getInputGenericButtonKey(event.keyCode) |
| 31 | } | 33 | } |
| 32 | 34 | ||
| @@ -68,7 +70,7 @@ object InputHandler { | |||
| 68 | private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int { | 70 | private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int { |
| 69 | var deviceIndex = index | 71 | var deviceIndex = index |
| 70 | if (deviceId != -1) { | 72 | if (deviceId != -1) { |
| 71 | deviceIndex = controllerIds[deviceId]!! | 73 | deviceIndex = controllerIds[deviceId] ?: 0 |
| 72 | } | 74 | } |
| 73 | 75 | ||
| 74 | // TODO: Joycons are handled as different controllers. Find a way to merge them. | 76 | // TODO: Joycons are handled as different controllers. Find a way to merge them. |
| @@ -227,6 +229,42 @@ object InputHandler { | |||
| 227 | } | 229 | } |
| 228 | } | 230 | } |
| 229 | 231 | ||
| 232 | private fun getInputRedmagicButtonKey(key: Int): Int { | ||
| 233 | return when (key) { | ||
| 234 | KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B | ||
| 235 | KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A | ||
| 236 | KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y | ||
| 237 | KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X | ||
| 238 | KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L | ||
| 239 | KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R | ||
| 240 | KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL | ||
| 241 | KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR | ||
| 242 | KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L | ||
| 243 | KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R | ||
| 244 | KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS | ||
| 245 | KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS | ||
| 246 | else -> -1 | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | private fun getInputBackboneLabsButtonKey(key: Int): Int { | ||
| 251 | return when (key) { | ||
| 252 | KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B | ||
| 253 | KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A | ||
| 254 | KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y | ||
| 255 | KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X | ||
| 256 | KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L | ||
| 257 | KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R | ||
| 258 | KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL | ||
| 259 | KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR | ||
| 260 | KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L | ||
| 261 | KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R | ||
| 262 | KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS | ||
| 263 | KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS | ||
| 264 | else -> -1 | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 230 | private fun getInputGenericButtonKey(key: Int): Int { | 268 | private fun getInputGenericButtonKey(key: Int): Int { |
| 231 | return when (key) { | 269 | return when (key) { |
| 232 | KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A | 270 | KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt index a193e82a4..aebe84b0f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt | |||
| @@ -3,38 +3,29 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.utils | 4 | package org.yuzu.yuzu_emu.utils |
| 5 | 5 | ||
| 6 | import android.util.Log | 6 | import android.os.Build |
| 7 | import org.yuzu.yuzu_emu.BuildConfig | 7 | |
| 8 | |||
| 9 | /** | ||
| 10 | * Contains methods that call through to [android.util.Log], but | ||
| 11 | * with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log | ||
| 12 | * levels in release builds. | ||
| 13 | */ | ||
| 14 | object Log { | 8 | object Log { |
| 15 | private const val TAG = "Yuzu Frontend" | 9 | // Tracks whether we should share the old log or the current log |
| 10 | var gameLaunched = false | ||
| 16 | 11 | ||
| 17 | fun verbose(message: String) { | 12 | external fun debug(message: String) |
| 18 | if (BuildConfig.DEBUG) { | ||
| 19 | Log.v(TAG, message) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | 13 | ||
| 23 | fun debug(message: String) { | 14 | external fun warning(message: String) |
| 24 | if (BuildConfig.DEBUG) { | ||
| 25 | Log.d(TAG, message) | ||
| 26 | } | ||
| 27 | } | ||
| 28 | 15 | ||
| 29 | fun info(message: String) { | 16 | external fun info(message: String) |
| 30 | Log.i(TAG, message) | ||
| 31 | } | ||
| 32 | 17 | ||
| 33 | fun warning(message: String) { | 18 | external fun error(message: String) |
| 34 | Log.w(TAG, message) | ||
| 35 | } | ||
| 36 | 19 | ||
| 37 | fun error(message: String) { | 20 | external fun critical(message: String) |
| 38 | Log.e(TAG, message) | 21 | |
| 22 | fun logDeviceInfo() { | ||
| 23 | info("Device Manufacturer - ${Build.MANUFACTURER}") | ||
| 24 | info("Device Model - ${Build.MODEL}") | ||
| 25 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { | ||
| 26 | info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}") | ||
| 27 | info("SoC Model - ${Build.SOC_MODEL}") | ||
| 28 | } | ||
| 29 | info("Total System Memory - ${MemoryUtil.getDeviceRAM()}") | ||
| 39 | } | 30 | } |
| 40 | } | 31 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt index aa4a5539a..9076a86c4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt | |||
| @@ -27,7 +27,7 @@ object MemoryUtil { | |||
| 27 | const val Pb = Tb * 1024 | 27 | const val Pb = Tb * 1024 |
| 28 | const val Eb = Pb * 1024 | 28 | const val Eb = Pb * 1024 |
| 29 | 29 | ||
| 30 | private fun bytesToSizeUnit(size: Float): String = | 30 | private fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String = |
| 31 | when { | 31 | when { |
| 32 | size < Kb -> { | 32 | size < Kb -> { |
| 33 | context.getString( | 33 | context.getString( |
| @@ -39,63 +39,59 @@ object MemoryUtil { | |||
| 39 | size < Mb -> { | 39 | size < Mb -> { |
| 40 | context.getString( | 40 | context.getString( |
| 41 | R.string.memory_formatted, | 41 | R.string.memory_formatted, |
| 42 | (size / Kb).hundredths, | 42 | if (roundUp) ceil(size / Kb) else (size / Kb).hundredths, |
| 43 | context.getString(R.string.memory_kilobyte) | 43 | context.getString(R.string.memory_kilobyte) |
| 44 | ) | 44 | ) |
| 45 | } | 45 | } |
| 46 | size < Gb -> { | 46 | size < Gb -> { |
| 47 | context.getString( | 47 | context.getString( |
| 48 | R.string.memory_formatted, | 48 | R.string.memory_formatted, |
| 49 | (size / Mb).hundredths, | 49 | if (roundUp) ceil(size / Mb) else (size / Mb).hundredths, |
| 50 | context.getString(R.string.memory_megabyte) | 50 | context.getString(R.string.memory_megabyte) |
| 51 | ) | 51 | ) |
| 52 | } | 52 | } |
| 53 | size < Tb -> { | 53 | size < Tb -> { |
| 54 | context.getString( | 54 | context.getString( |
| 55 | R.string.memory_formatted, | 55 | R.string.memory_formatted, |
| 56 | (size / Gb).hundredths, | 56 | if (roundUp) ceil(size / Gb) else (size / Gb).hundredths, |
| 57 | context.getString(R.string.memory_gigabyte) | 57 | context.getString(R.string.memory_gigabyte) |
| 58 | ) | 58 | ) |
| 59 | } | 59 | } |
| 60 | size < Pb -> { | 60 | size < Pb -> { |
| 61 | context.getString( | 61 | context.getString( |
| 62 | R.string.memory_formatted, | 62 | R.string.memory_formatted, |
| 63 | (size / Tb).hundredths, | 63 | if (roundUp) ceil(size / Tb) else (size / Tb).hundredths, |
| 64 | context.getString(R.string.memory_terabyte) | 64 | context.getString(R.string.memory_terabyte) |
| 65 | ) | 65 | ) |
| 66 | } | 66 | } |
| 67 | size < Eb -> { | 67 | size < Eb -> { |
| 68 | context.getString( | 68 | context.getString( |
| 69 | R.string.memory_formatted, | 69 | R.string.memory_formatted, |
| 70 | (size / Pb).hundredths, | 70 | if (roundUp) ceil(size / Pb) else (size / Pb).hundredths, |
| 71 | context.getString(R.string.memory_petabyte) | 71 | context.getString(R.string.memory_petabyte) |
| 72 | ) | 72 | ) |
| 73 | } | 73 | } |
| 74 | else -> { | 74 | else -> { |
| 75 | context.getString( | 75 | context.getString( |
| 76 | R.string.memory_formatted, | 76 | R.string.memory_formatted, |
| 77 | (size / Eb).hundredths, | 77 | if (roundUp) ceil(size / Eb) else (size / Eb).hundredths, |
| 78 | context.getString(R.string.memory_exabyte) | 78 | context.getString(R.string.memory_exabyte) |
| 79 | ) | 79 | ) |
| 80 | } | 80 | } |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for | 83 | val totalMemory: Float |
| 84 | // the potential error created by memInfo.totalMem | ||
| 85 | private val totalMemory: Float | ||
| 86 | get() { | 84 | get() { |
| 87 | val memInfo = ActivityManager.MemoryInfo() | 85 | val memInfo = ActivityManager.MemoryInfo() |
| 88 | with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { | 86 | with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { |
| 89 | getMemoryInfo(memInfo) | 87 | getMemoryInfo(memInfo) |
| 90 | } | 88 | } |
| 91 | 89 | ||
| 92 | return ceil( | 90 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { |
| 93 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { | 91 | memInfo.advertisedMem.toFloat() |
| 94 | memInfo.advertisedMem.toFloat() | 92 | } else { |
| 95 | } else { | 93 | memInfo.totalMem.toFloat() |
| 96 | memInfo.totalMem.toFloat() | 94 | } |
| 97 | } | ||
| 98 | ) | ||
| 99 | } | 95 | } |
| 100 | 96 | ||
| 101 | fun isLessThan(minimum: Int, size: Float): Boolean = | 97 | fun isLessThan(minimum: Int, size: Float): Boolean = |
| @@ -109,5 +105,7 @@ object MemoryUtil { | |||
| 109 | else -> totalMemory < Kb && totalMemory < minimum | 105 | else -> totalMemory < Kb && totalMemory < minimum |
| 110 | } | 106 | } |
| 111 | 107 | ||
| 112 | fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory) | 108 | // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for |
| 109 | // the potential error created by memInfo.totalMem | ||
| 110 | fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true) | ||
| 113 | } | 111 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt index 9425f8b99..87e579fa7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt | |||
| @@ -4,6 +4,30 @@ | |||
| 4 | package org.yuzu.yuzu_emu.utils | 4 | package org.yuzu.yuzu_emu.utils |
| 5 | 5 | ||
| 6 | object NativeConfig { | 6 | object NativeConfig { |
| 7 | /** | ||
| 8 | * Creates a Config object and opens the emulation config. | ||
| 9 | */ | ||
| 10 | @Synchronized | ||
| 11 | external fun initializeConfig() | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Destroys the stored config object. This automatically saves the existing config. | ||
| 15 | */ | ||
| 16 | @Synchronized | ||
| 17 | external fun unloadConfig() | ||
| 18 | |||
| 19 | /** | ||
| 20 | * Reads values saved to the config file and saves them. | ||
| 21 | */ | ||
| 22 | @Synchronized | ||
| 23 | external fun reloadSettings() | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Saves settings values in memory to disk. | ||
| 27 | */ | ||
| 28 | @Synchronized | ||
| 29 | external fun saveSettings() | ||
| 30 | |||
| 7 | external fun getBoolean(key: String, getDefault: Boolean): Boolean | 31 | external fun getBoolean(key: String, getDefault: Boolean): Boolean |
| 8 | external fun setBoolean(key: String, value: Boolean) | 32 | external fun setBoolean(key: String, value: Boolean) |
| 9 | 33 | ||
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 1c36661f5..2acc93da8 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt | |||
| @@ -6,9 +6,6 @@ add_library(yuzu-android SHARED | |||
| 6 | android_common/android_common.h | 6 | android_common/android_common.h |
| 7 | applets/software_keyboard.cpp | 7 | applets/software_keyboard.cpp |
| 8 | applets/software_keyboard.h | 8 | applets/software_keyboard.h |
| 9 | config.cpp | ||
| 10 | config.h | ||
| 11 | default_ini.h | ||
| 12 | emu_window/emu_window.cpp | 9 | emu_window/emu_window.cpp |
| 13 | emu_window/emu_window.h | 10 | emu_window/emu_window.h |
| 14 | id_cache.cpp | 11 | id_cache.cpp |
| @@ -16,14 +13,17 @@ add_library(yuzu-android SHARED | |||
| 16 | native.cpp | 13 | native.cpp |
| 17 | native.h | 14 | native.h |
| 18 | native_config.cpp | 15 | native_config.cpp |
| 19 | uisettings.cpp | 16 | android_settings.cpp |
| 20 | game_metadata.cpp | 17 | game_metadata.cpp |
| 18 | native_log.cpp | ||
| 19 | android_config.cpp | ||
| 20 | android_config.h | ||
| 21 | ) | 21 | ) |
| 22 | 22 | ||
| 23 | set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) | 23 | set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) |
| 24 | 24 | ||
| 25 | target_link_libraries(yuzu-android PRIVATE audio_core common core input_common) | 25 | target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common) |
| 26 | target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log) | 26 | target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log) |
| 27 | if (ARCHITECTURE_arm64) | 27 | if (ARCHITECTURE_arm64) |
| 28 | target_link_libraries(yuzu-android PRIVATE adrenotools) | 28 | target_link_libraries(yuzu-android PRIVATE adrenotools) |
| 29 | endif() | 29 | endif() |
diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp new file mode 100644 index 000000000..3041c25c9 --- /dev/null +++ b/src/android/app/src/main/jni/android_config.cpp | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "android_config.h" | ||
| 5 | #include "android_settings.h" | ||
| 6 | #include "common/settings_setting.h" | ||
| 7 | |||
| 8 | AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_type) | ||
| 9 | : Config(config_type) { | ||
| 10 | Initialize(config_name); | ||
| 11 | if (config_type != ConfigType::InputProfile) { | ||
| 12 | ReadAndroidValues(); | ||
| 13 | SaveAndroidValues(); | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | AndroidConfig::~AndroidConfig() { | ||
| 18 | if (global) { | ||
| 19 | AndroidConfig::SaveAllValues(); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | void AndroidConfig::ReloadAllValues() { | ||
| 24 | Reload(); | ||
| 25 | ReadAndroidValues(); | ||
| 26 | SaveAndroidValues(); | ||
| 27 | } | ||
| 28 | |||
| 29 | void AndroidConfig::SaveAllValues() { | ||
| 30 | Save(); | ||
| 31 | SaveAndroidValues(); | ||
| 32 | } | ||
| 33 | |||
| 34 | void AndroidConfig::ReadAndroidValues() { | ||
| 35 | if (global) { | ||
| 36 | ReadAndroidUIValues(); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | void AndroidConfig::ReadAndroidUIValues() { | ||
| 41 | BeginGroup(Settings::TranslateCategory(Settings::Category::Android)); | ||
| 42 | |||
| 43 | ReadCategory(Settings::Category::Android); | ||
| 44 | |||
| 45 | EndGroup(); | ||
| 46 | } | ||
| 47 | |||
| 48 | void AndroidConfig::SaveAndroidValues() { | ||
| 49 | if (global) { | ||
| 50 | SaveAndroidUIValues(); | ||
| 51 | } | ||
| 52 | |||
| 53 | WriteToIni(); | ||
| 54 | } | ||
| 55 | |||
| 56 | void AndroidConfig::SaveAndroidUIValues() { | ||
| 57 | BeginGroup(Settings::TranslateCategory(Settings::Category::Android)); | ||
| 58 | |||
| 59 | WriteCategory(Settings::Category::Android); | ||
| 60 | |||
| 61 | EndGroup(); | ||
| 62 | } | ||
| 63 | |||
| 64 | std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) { | ||
| 65 | auto& map = Settings::values.linkage.by_category; | ||
| 66 | if (map.contains(category)) { | ||
| 67 | return Settings::values.linkage.by_category[category]; | ||
| 68 | } | ||
| 69 | return AndroidSettings::values.linkage.by_category[category]; | ||
| 70 | } | ||
diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h new file mode 100644 index 000000000..e679392fd --- /dev/null +++ b/src/android/app/src/main/jni/android_config.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "frontend_common/config.h" | ||
| 7 | |||
| 8 | class AndroidConfig final : public Config { | ||
| 9 | public: | ||
| 10 | explicit AndroidConfig(const std::string& config_name = "config", | ||
| 11 | ConfigType config_type = ConfigType::GlobalConfig); | ||
| 12 | ~AndroidConfig() override; | ||
| 13 | |||
| 14 | void ReloadAllValues() override; | ||
| 15 | void SaveAllValues() override; | ||
| 16 | |||
| 17 | protected: | ||
| 18 | void ReadAndroidValues(); | ||
| 19 | void ReadAndroidUIValues(); | ||
| 20 | void ReadHidbusValues() override {} | ||
| 21 | void ReadDebugControlValues() override {} | ||
| 22 | void ReadPathValues() override {} | ||
| 23 | void ReadShortcutValues() override {} | ||
| 24 | void ReadUIValues() override {} | ||
| 25 | void ReadUIGamelistValues() override {} | ||
| 26 | void ReadUILayoutValues() override {} | ||
| 27 | void ReadMultiplayerValues() override {} | ||
| 28 | |||
| 29 | void SaveAndroidValues(); | ||
| 30 | void SaveAndroidUIValues(); | ||
| 31 | void SaveHidbusValues() override {} | ||
| 32 | void SaveDebugControlValues() override {} | ||
| 33 | void SavePathValues() override {} | ||
| 34 | void SaveShortcutValues() override {} | ||
| 35 | void SaveUIValues() override {} | ||
| 36 | void SaveUIGamelistValues() override {} | ||
| 37 | void SaveUILayoutValues() override {} | ||
| 38 | void SaveMultiplayerValues() override {} | ||
| 39 | |||
| 40 | std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override; | ||
| 41 | }; | ||
diff --git a/src/android/app/src/main/jni/uisettings.cpp b/src/android/app/src/main/jni/android_settings.cpp index f2f0bad50..16023a6b0 100644 --- a/src/android/app/src/main/jni/uisettings.cpp +++ b/src/android/app/src/main/jni/android_settings.cpp | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "uisettings.h" | 4 | #include "android_settings.h" |
| 5 | 5 | ||
| 6 | namespace AndroidSettings { | 6 | namespace AndroidSettings { |
| 7 | 7 | ||
diff --git a/src/android/app/src/main/jni/uisettings.h b/src/android/app/src/main/jni/android_settings.h index 494654af7..37bc33918 100644 --- a/src/android/app/src/main/jni/uisettings.h +++ b/src/android/app/src/main/jni/android_settings.h | |||
| @@ -13,7 +13,7 @@ struct Values { | |||
| 13 | Settings::Linkage linkage; | 13 | Settings::Linkage linkage; |
| 14 | 14 | ||
| 15 | // Android | 15 | // Android |
| 16 | Settings::Setting<bool> picture_in_picture{linkage, true, "picture_in_picture", | 16 | Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture", |
| 17 | Settings::Category::Android}; | 17 | Settings::Category::Android}; |
| 18 | Settings::Setting<s32> screen_layout{linkage, | 18 | Settings::Setting<s32> screen_layout{linkage, |
| 19 | 5, | 19 | 5, |
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp deleted file mode 100644 index 81120ab0f..000000000 --- a/src/android/app/src/main/jni/config.cpp +++ /dev/null | |||
| @@ -1,330 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <memory> | ||
| 5 | #include <optional> | ||
| 6 | #include <sstream> | ||
| 7 | |||
| 8 | #include <INIReader.h> | ||
| 9 | #include "common/fs/file.h" | ||
| 10 | #include "common/fs/fs.h" | ||
| 11 | #include "common/fs/path_util.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/settings.h" | ||
| 14 | #include "common/settings_enums.h" | ||
| 15 | #include "core/hle/service/acc/profile_manager.h" | ||
| 16 | #include "input_common/main.h" | ||
| 17 | #include "jni/config.h" | ||
| 18 | #include "jni/default_ini.h" | ||
| 19 | #include "uisettings.h" | ||
| 20 | |||
| 21 | namespace FS = Common::FS; | ||
| 22 | |||
| 23 | Config::Config(const std::string& config_name, ConfigType config_type) | ||
| 24 | : type(config_type), global{config_type == ConfigType::GlobalConfig} { | ||
| 25 | Initialize(config_name); | ||
| 26 | } | ||
| 27 | |||
| 28 | Config::~Config() = default; | ||
| 29 | |||
| 30 | bool Config::LoadINI(const std::string& default_contents, bool retry) { | ||
| 31 | void(FS::CreateParentDir(config_loc)); | ||
| 32 | config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc)); | ||
| 33 | const auto config_loc_str = FS::PathToUTF8String(config_loc); | ||
| 34 | if (config->ParseError() < 0) { | ||
| 35 | if (retry) { | ||
| 36 | LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", | ||
| 37 | config_loc_str); | ||
| 38 | |||
| 39 | void(FS::CreateParentDir(config_loc)); | ||
| 40 | void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents)); | ||
| 41 | |||
| 42 | config = std::make_unique<INIReader>(config_loc_str); | ||
| 43 | |||
| 44 | return LoadINI(default_contents, false); | ||
| 45 | } | ||
| 46 | LOG_ERROR(Config, "Failed."); | ||
| 47 | return false; | ||
| 48 | } | ||
| 49 | LOG_INFO(Config, "Successfully loaded {}", config_loc_str); | ||
| 50 | return true; | ||
| 51 | } | ||
| 52 | |||
| 53 | template <> | ||
| 54 | void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { | ||
| 55 | std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault()); | ||
| 56 | if (setting_value.empty()) { | ||
| 57 | setting_value = setting.GetDefault(); | ||
| 58 | } | ||
| 59 | setting = std::move(setting_value); | ||
| 60 | } | ||
| 61 | |||
| 62 | template <> | ||
| 63 | void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) { | ||
| 64 | setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); | ||
| 65 | } | ||
| 66 | |||
| 67 | template <typename Type, bool ranged> | ||
| 68 | void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) { | ||
| 69 | setting = static_cast<Type>( | ||
| 70 | config->GetInteger(group, setting.GetLabel(), static_cast<long>(setting.GetDefault()))); | ||
| 71 | } | ||
| 72 | |||
| 73 | void Config::ReadValues() { | ||
| 74 | ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); | ||
| 75 | ReadSetting("ControlsGeneral", Settings::values.touch_device); | ||
| 76 | ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled); | ||
| 77 | ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled); | ||
| 78 | ReadSetting("ControlsGeneral", Settings::values.vibration_enabled); | ||
| 79 | ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations); | ||
| 80 | ReadSetting("ControlsGeneral", Settings::values.motion_enabled); | ||
| 81 | Settings::values.touchscreen.enabled = | ||
| 82 | config->GetBoolean("ControlsGeneral", "touch_enabled", true); | ||
| 83 | Settings::values.touchscreen.rotation_angle = | ||
| 84 | config->GetInteger("ControlsGeneral", "touch_angle", 0); | ||
| 85 | Settings::values.touchscreen.diameter_x = | ||
| 86 | config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); | ||
| 87 | Settings::values.touchscreen.diameter_y = | ||
| 88 | config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); | ||
| 89 | |||
| 90 | int num_touch_from_button_maps = | ||
| 91 | config->GetInteger("ControlsGeneral", "touch_from_button_map", 0); | ||
| 92 | if (num_touch_from_button_maps > 0) { | ||
| 93 | for (int i = 0; i < num_touch_from_button_maps; ++i) { | ||
| 94 | Settings::TouchFromButtonMap map; | ||
| 95 | map.name = config->Get("ControlsGeneral", | ||
| 96 | std::string("touch_from_button_maps_") + std::to_string(i) + | ||
| 97 | std::string("_name"), | ||
| 98 | "default"); | ||
| 99 | const int num_touch_maps = config->GetInteger( | ||
| 100 | "ControlsGeneral", | ||
| 101 | std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"), | ||
| 102 | 0); | ||
| 103 | map.buttons.reserve(num_touch_maps); | ||
| 104 | |||
| 105 | for (int j = 0; j < num_touch_maps; ++j) { | ||
| 106 | std::string touch_mapping = | ||
| 107 | config->Get("ControlsGeneral", | ||
| 108 | std::string("touch_from_button_maps_") + std::to_string(i) + | ||
| 109 | std::string("_bind_") + std::to_string(j), | ||
| 110 | ""); | ||
| 111 | map.buttons.emplace_back(std::move(touch_mapping)); | ||
| 112 | } | ||
| 113 | |||
| 114 | Settings::values.touch_from_button_maps.emplace_back(std::move(map)); | ||
| 115 | } | ||
| 116 | } else { | ||
| 117 | Settings::values.touch_from_button_maps.emplace_back( | ||
| 118 | Settings::TouchFromButtonMap{"default", {}}); | ||
| 119 | num_touch_from_button_maps = 1; | ||
| 120 | } | ||
| 121 | Settings::values.touch_from_button_map_index = std::clamp( | ||
| 122 | Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); | ||
| 123 | |||
| 124 | ReadSetting("ControlsGeneral", Settings::values.udp_input_servers); | ||
| 125 | |||
| 126 | // Data Storage | ||
| 127 | ReadSetting("Data Storage", Settings::values.use_virtual_sd); | ||
| 128 | FS::SetYuzuPath(FS::YuzuPath::NANDDir, | ||
| 129 | config->Get("Data Storage", "nand_directory", | ||
| 130 | FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); | ||
| 131 | FS::SetYuzuPath(FS::YuzuPath::SDMCDir, | ||
| 132 | config->Get("Data Storage", "sdmc_directory", | ||
| 133 | FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); | ||
| 134 | FS::SetYuzuPath(FS::YuzuPath::LoadDir, | ||
| 135 | config->Get("Data Storage", "load_directory", | ||
| 136 | FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); | ||
| 137 | FS::SetYuzuPath(FS::YuzuPath::DumpDir, | ||
| 138 | config->Get("Data Storage", "dump_directory", | ||
| 139 | FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); | ||
| 140 | ReadSetting("Data Storage", Settings::values.gamecard_inserted); | ||
| 141 | ReadSetting("Data Storage", Settings::values.gamecard_current_game); | ||
| 142 | ReadSetting("Data Storage", Settings::values.gamecard_path); | ||
| 143 | |||
| 144 | // System | ||
| 145 | ReadSetting("System", Settings::values.current_user); | ||
| 146 | Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0, | ||
| 147 | Service::Account::MAX_USERS - 1); | ||
| 148 | |||
| 149 | // Disable docked mode by default on Android | ||
| 150 | Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false) | ||
| 151 | ? Settings::ConsoleMode::Docked | ||
| 152 | : Settings::ConsoleMode::Handheld); | ||
| 153 | |||
| 154 | const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false); | ||
| 155 | if (rng_seed_enabled) { | ||
| 156 | Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0)); | ||
| 157 | } else { | ||
| 158 | Settings::values.rng_seed.SetValue(0); | ||
| 159 | } | ||
| 160 | Settings::values.rng_seed_enabled.SetValue(rng_seed_enabled); | ||
| 161 | |||
| 162 | const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false); | ||
| 163 | if (custom_rtc_enabled) { | ||
| 164 | Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0); | ||
| 165 | } else { | ||
| 166 | Settings::values.custom_rtc = 0; | ||
| 167 | } | ||
| 168 | Settings::values.custom_rtc_enabled = custom_rtc_enabled; | ||
| 169 | |||
| 170 | ReadSetting("System", Settings::values.language_index); | ||
| 171 | ReadSetting("System", Settings::values.region_index); | ||
| 172 | ReadSetting("System", Settings::values.time_zone_index); | ||
| 173 | ReadSetting("System", Settings::values.sound_index); | ||
| 174 | |||
| 175 | // Core | ||
| 176 | ReadSetting("Core", Settings::values.use_multi_core); | ||
| 177 | ReadSetting("Core", Settings::values.memory_layout_mode); | ||
| 178 | |||
| 179 | // Cpu | ||
| 180 | ReadSetting("Cpu", Settings::values.cpu_accuracy); | ||
| 181 | ReadSetting("Cpu", Settings::values.cpu_debug_mode); | ||
| 182 | ReadSetting("Cpu", Settings::values.cpuopt_page_tables); | ||
| 183 | ReadSetting("Cpu", Settings::values.cpuopt_block_linking); | ||
| 184 | ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer); | ||
| 185 | ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher); | ||
| 186 | ReadSetting("Cpu", Settings::values.cpuopt_context_elimination); | ||
| 187 | ReadSetting("Cpu", Settings::values.cpuopt_const_prop); | ||
| 188 | ReadSetting("Cpu", Settings::values.cpuopt_misc_ir); | ||
| 189 | ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks); | ||
| 190 | ReadSetting("Cpu", Settings::values.cpuopt_fastmem); | ||
| 191 | ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives); | ||
| 192 | ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives); | ||
| 193 | ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts); | ||
| 194 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma); | ||
| 195 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error); | ||
| 196 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr); | ||
| 197 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan); | ||
| 198 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check); | ||
| 199 | ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor); | ||
| 200 | |||
| 201 | // Renderer | ||
| 202 | ReadSetting("Renderer", Settings::values.renderer_backend); | ||
| 203 | ReadSetting("Renderer", Settings::values.renderer_debug); | ||
| 204 | ReadSetting("Renderer", Settings::values.renderer_shader_feedback); | ||
| 205 | ReadSetting("Renderer", Settings::values.enable_nsight_aftermath); | ||
| 206 | ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks); | ||
| 207 | ReadSetting("Renderer", Settings::values.vulkan_device); | ||
| 208 | |||
| 209 | ReadSetting("Renderer", Settings::values.resolution_setup); | ||
| 210 | ReadSetting("Renderer", Settings::values.scaling_filter); | ||
| 211 | ReadSetting("Renderer", Settings::values.fsr_sharpening_slider); | ||
| 212 | ReadSetting("Renderer", Settings::values.anti_aliasing); | ||
| 213 | ReadSetting("Renderer", Settings::values.fullscreen_mode); | ||
| 214 | ReadSetting("Renderer", Settings::values.aspect_ratio); | ||
| 215 | ReadSetting("Renderer", Settings::values.max_anisotropy); | ||
| 216 | ReadSetting("Renderer", Settings::values.use_speed_limit); | ||
| 217 | ReadSetting("Renderer", Settings::values.speed_limit); | ||
| 218 | ReadSetting("Renderer", Settings::values.use_disk_shader_cache); | ||
| 219 | ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation); | ||
| 220 | ReadSetting("Renderer", Settings::values.vsync_mode); | ||
| 221 | ReadSetting("Renderer", Settings::values.shader_backend); | ||
| 222 | ReadSetting("Renderer", Settings::values.use_asynchronous_shaders); | ||
| 223 | ReadSetting("Renderer", Settings::values.nvdec_emulation); | ||
| 224 | ReadSetting("Renderer", Settings::values.use_fast_gpu_time); | ||
| 225 | ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache); | ||
| 226 | |||
| 227 | ReadSetting("Renderer", Settings::values.bg_red); | ||
| 228 | ReadSetting("Renderer", Settings::values.bg_green); | ||
| 229 | ReadSetting("Renderer", Settings::values.bg_blue); | ||
| 230 | |||
| 231 | // Use GPU accuracy normal by default on Android | ||
| 232 | Settings::values.gpu_accuracy = static_cast<Settings::GpuAccuracy>(config->GetInteger( | ||
| 233 | "Renderer", "gpu_accuracy", static_cast<u32>(Settings::GpuAccuracy::Normal))); | ||
| 234 | |||
| 235 | // Use GPU default anisotropic filtering on Android | ||
| 236 | Settings::values.max_anisotropy = | ||
| 237 | static_cast<Settings::AnisotropyMode>(config->GetInteger("Renderer", "max_anisotropy", 1)); | ||
| 238 | |||
| 239 | // Disable ASTC compute by default on Android | ||
| 240 | Settings::values.accelerate_astc.SetValue( | ||
| 241 | config->GetBoolean("Renderer", "accelerate_astc", false) ? Settings::AstcDecodeMode::Gpu | ||
| 242 | : Settings::AstcDecodeMode::Cpu); | ||
| 243 | |||
| 244 | // Enable asynchronous presentation by default on Android | ||
| 245 | Settings::values.async_presentation = | ||
| 246 | config->GetBoolean("Renderer", "async_presentation", true); | ||
| 247 | |||
| 248 | // Disable force_max_clock by default on Android | ||
| 249 | Settings::values.renderer_force_max_clock = | ||
| 250 | config->GetBoolean("Renderer", "force_max_clock", false); | ||
| 251 | |||
| 252 | // Disable use_reactive_flushing by default on Android | ||
| 253 | Settings::values.use_reactive_flushing = | ||
| 254 | config->GetBoolean("Renderer", "use_reactive_flushing", false); | ||
| 255 | |||
| 256 | // Audio | ||
| 257 | ReadSetting("Audio", Settings::values.sink_id); | ||
| 258 | ReadSetting("Audio", Settings::values.audio_output_device_id); | ||
| 259 | ReadSetting("Audio", Settings::values.volume); | ||
| 260 | |||
| 261 | // Miscellaneous | ||
| 262 | // log_filter has a different default here than from common | ||
| 263 | Settings::values.log_filter = "*:Info"; | ||
| 264 | ReadSetting("Miscellaneous", Settings::values.use_dev_keys); | ||
| 265 | |||
| 266 | // Debugging | ||
| 267 | Settings::values.record_frame_times = | ||
| 268 | config->GetBoolean("Debugging", "record_frame_times", false); | ||
| 269 | ReadSetting("Debugging", Settings::values.dump_exefs); | ||
| 270 | ReadSetting("Debugging", Settings::values.dump_nso); | ||
| 271 | ReadSetting("Debugging", Settings::values.enable_fs_access_log); | ||
| 272 | ReadSetting("Debugging", Settings::values.reporting_services); | ||
| 273 | ReadSetting("Debugging", Settings::values.quest_flag); | ||
| 274 | ReadSetting("Debugging", Settings::values.use_debug_asserts); | ||
| 275 | ReadSetting("Debugging", Settings::values.use_auto_stub); | ||
| 276 | ReadSetting("Debugging", Settings::values.disable_macro_jit); | ||
| 277 | ReadSetting("Debugging", Settings::values.disable_macro_hle); | ||
| 278 | ReadSetting("Debugging", Settings::values.use_gdbstub); | ||
| 279 | ReadSetting("Debugging", Settings::values.gdbstub_port); | ||
| 280 | |||
| 281 | const auto title_list = config->Get("AddOns", "title_ids", ""); | ||
| 282 | std::stringstream ss(title_list); | ||
| 283 | std::string line; | ||
| 284 | while (std::getline(ss, line, '|')) { | ||
| 285 | const auto title_id = std::strtoul(line.c_str(), nullptr, 16); | ||
| 286 | const auto disabled_list = config->Get("AddOns", "disabled_" + line, ""); | ||
| 287 | |||
| 288 | std::stringstream inner_ss(disabled_list); | ||
| 289 | std::string inner_line; | ||
| 290 | std::vector<std::string> out; | ||
| 291 | while (std::getline(inner_ss, inner_line, '|')) { | ||
| 292 | out.push_back(inner_line); | ||
| 293 | } | ||
| 294 | |||
| 295 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | ||
| 296 | } | ||
| 297 | |||
| 298 | // Web Service | ||
| 299 | ReadSetting("WebService", Settings::values.enable_telemetry); | ||
| 300 | ReadSetting("WebService", Settings::values.web_api_url); | ||
| 301 | ReadSetting("WebService", Settings::values.yuzu_username); | ||
| 302 | ReadSetting("WebService", Settings::values.yuzu_token); | ||
| 303 | |||
| 304 | // Network | ||
| 305 | ReadSetting("Network", Settings::values.network_interface); | ||
| 306 | |||
| 307 | // Android | ||
| 308 | ReadSetting("Android", AndroidSettings::values.picture_in_picture); | ||
| 309 | ReadSetting("Android", AndroidSettings::values.screen_layout); | ||
| 310 | } | ||
| 311 | |||
| 312 | void Config::Initialize(const std::string& config_name) { | ||
| 313 | const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); | ||
| 314 | const auto config_file = fmt::format("{}.ini", config_name); | ||
| 315 | |||
| 316 | switch (type) { | ||
| 317 | case ConfigType::GlobalConfig: | ||
| 318 | config_loc = FS::PathToUTF8String(fs_config_loc / config_file); | ||
| 319 | break; | ||
| 320 | case ConfigType::PerGameConfig: | ||
| 321 | config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); | ||
| 322 | break; | ||
| 323 | case ConfigType::InputProfile: | ||
| 324 | config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); | ||
| 325 | LoadINI(DefaultINI::android_config_file); | ||
| 326 | return; | ||
| 327 | } | ||
| 328 | LoadINI(DefaultINI::android_config_file); | ||
| 329 | ReadValues(); | ||
| 330 | } | ||
diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h deleted file mode 100644 index e1e8f47ed..000000000 --- a/src/android/app/src/main/jni/config.h +++ /dev/null | |||
| @@ -1,47 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <filesystem> | ||
| 7 | #include <memory> | ||
| 8 | #include <optional> | ||
| 9 | #include <string> | ||
| 10 | |||
| 11 | #include "common/settings.h" | ||
| 12 | |||
| 13 | class INIReader; | ||
| 14 | |||
| 15 | class Config { | ||
| 16 | bool LoadINI(const std::string& default_contents = "", bool retry = true); | ||
| 17 | |||
| 18 | public: | ||
| 19 | enum class ConfigType { | ||
| 20 | GlobalConfig, | ||
| 21 | PerGameConfig, | ||
| 22 | InputProfile, | ||
| 23 | }; | ||
| 24 | |||
| 25 | explicit Config(const std::string& config_name = "config", | ||
| 26 | ConfigType config_type = ConfigType::GlobalConfig); | ||
| 27 | ~Config(); | ||
| 28 | |||
| 29 | void Initialize(const std::string& config_name); | ||
| 30 | |||
| 31 | private: | ||
| 32 | /** | ||
| 33 | * Applies a value read from the config to a Setting. | ||
| 34 | * | ||
| 35 | * @param group The name of the INI group | ||
| 36 | * @param setting The yuzu setting to modify | ||
| 37 | */ | ||
| 38 | template <typename Type, bool ranged> | ||
| 39 | void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); | ||
| 40 | |||
| 41 | void ReadValues(); | ||
| 42 | |||
| 43 | const ConfigType type; | ||
| 44 | std::unique_ptr<INIReader> config; | ||
| 45 | std::string config_loc; | ||
| 46 | const bool global; | ||
| 47 | }; | ||
diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h deleted file mode 100644 index d81422a74..000000000 --- a/src/android/app/src/main/jni/default_ini.h +++ /dev/null | |||
| @@ -1,511 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | namespace DefaultINI { | ||
| 7 | |||
| 8 | const char* android_config_file = R"( | ||
| 9 | |||
| 10 | [ControlsP0] | ||
| 11 | # The input devices and parameters for each Switch native input | ||
| 12 | # The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... | ||
| 13 | # It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." | ||
| 14 | # Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values | ||
| 15 | |||
| 16 | # Indicates if this player should be connected at boot | ||
| 17 | connected= | ||
| 18 | |||
| 19 | # for button input, the following devices are available: | ||
| 20 | # - "keyboard" (default) for keyboard input. Required parameters: | ||
| 21 | # - "code": the code of the key to bind | ||
| 22 | # - "sdl" for joystick input using SDL. Required parameters: | ||
| 23 | # - "guid": SDL identification GUID of the joystick | ||
| 24 | # - "port": the index of the joystick to bind | ||
| 25 | # - "button"(optional): the index of the button to bind | ||
| 26 | # - "hat"(optional): the index of the hat to bind as direction buttons | ||
| 27 | # - "axis"(optional): the index of the axis to bind | ||
| 28 | # - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" | ||
| 29 | # - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is | ||
| 30 | # triggered if the axis value crosses | ||
| 31 | # - "direction"(only used for axis): "+" means the button is triggered when the axis value | ||
| 32 | # is greater than the threshold; "-" means the button is triggered when the axis value | ||
| 33 | # is smaller than the threshold | ||
| 34 | button_a= | ||
| 35 | button_b= | ||
| 36 | button_x= | ||
| 37 | button_y= | ||
| 38 | button_lstick= | ||
| 39 | button_rstick= | ||
| 40 | button_l= | ||
| 41 | button_r= | ||
| 42 | button_zl= | ||
| 43 | button_zr= | ||
| 44 | button_plus= | ||
| 45 | button_minus= | ||
| 46 | button_dleft= | ||
| 47 | button_dup= | ||
| 48 | button_dright= | ||
| 49 | button_ddown= | ||
| 50 | button_lstick_left= | ||
| 51 | button_lstick_up= | ||
| 52 | button_lstick_right= | ||
| 53 | button_lstick_down= | ||
| 54 | button_sl= | ||
| 55 | button_sr= | ||
| 56 | button_home= | ||
| 57 | button_screenshot= | ||
| 58 | |||
| 59 | # for analog input, the following devices are available: | ||
| 60 | # - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: | ||
| 61 | # - "up", "down", "left", "right": sub-devices for each direction. | ||
| 62 | # Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" | ||
| 63 | # - "modifier": sub-devices as a modifier. | ||
| 64 | # - "modifier_scale": a float number representing the applied modifier scale to the analog input. | ||
| 65 | # Must be in range of 0.0-1.0. Defaults to 0.5 | ||
| 66 | # - "sdl" for joystick input using SDL. Required parameters: | ||
| 67 | # - "guid": SDL identification GUID of the joystick | ||
| 68 | # - "port": the index of the joystick to bind | ||
| 69 | # - "axis_x": the index of the axis to bind as x-axis (default to 0) | ||
| 70 | # - "axis_y": the index of the axis to bind as y-axis (default to 1) | ||
| 71 | lstick= | ||
| 72 | rstick= | ||
| 73 | |||
| 74 | # for motion input, the following devices are available: | ||
| 75 | # - "keyboard" (default) for emulating random motion input from buttons. Required parameters: | ||
| 76 | # - "code": the code of the key to bind | ||
| 77 | # - "sdl" for motion input using SDL. Required parameters: | ||
| 78 | # - "guid": SDL identification GUID of the joystick | ||
| 79 | # - "port": the index of the joystick to bind | ||
| 80 | # - "motion": the index of the motion sensor to bind | ||
| 81 | # - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters: | ||
| 82 | # - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001" | ||
| 83 | # - "port": the port of the cemu hook server | ||
| 84 | # - "pad": the index of the joystick | ||
| 85 | # - "motion": the index of the motion sensor of the joystick to bind | ||
| 86 | motionleft= | ||
| 87 | motionright= | ||
| 88 | |||
| 89 | [ControlsGeneral] | ||
| 90 | # To use the debug_pad, prepend `debug_pad_` before each button setting above. | ||
| 91 | # i.e. debug_pad_button_a= | ||
| 92 | |||
| 93 | # Enable debug pad inputs to the guest | ||
| 94 | # 0 (default): Disabled, 1: Enabled | ||
| 95 | debug_pad_enabled = | ||
| 96 | |||
| 97 | # Whether to enable or disable vibration | ||
| 98 | # 0: Disabled, 1 (default): Enabled | ||
| 99 | vibration_enabled= | ||
| 100 | |||
| 101 | # Whether to enable or disable accurate vibrations | ||
| 102 | # 0 (default): Disabled, 1: Enabled | ||
| 103 | enable_accurate_vibrations= | ||
| 104 | |||
| 105 | # Enables controller motion inputs | ||
| 106 | # 0: Disabled, 1 (default): Enabled | ||
| 107 | motion_enabled = | ||
| 108 | |||
| 109 | # Defines the udp device's touch screen coordinate system for cemuhookudp devices | ||
| 110 | # - "min_x", "min_y", "max_x", "max_y" | ||
| 111 | touch_device= | ||
| 112 | |||
| 113 | # for mapping buttons to touch inputs. | ||
| 114 | #touch_from_button_map=1 | ||
| 115 | #touch_from_button_maps_0_name=default | ||
| 116 | #touch_from_button_maps_0_count=2 | ||
| 117 | #touch_from_button_maps_0_bind_0=foo | ||
| 118 | #touch_from_button_maps_0_bind_1=bar | ||
| 119 | # etc. | ||
| 120 | |||
| 121 | # List of Cemuhook UDP servers, delimited by ','. | ||
| 122 | # Default: 127.0.0.1:26760 | ||
| 123 | # Example: 127.0.0.1:26760,123.4.5.67:26761 | ||
| 124 | udp_input_servers = | ||
| 125 | |||
| 126 | # Enable controlling an axis via a mouse input. | ||
| 127 | # 0 (default): Off, 1: On | ||
| 128 | mouse_panning = | ||
| 129 | |||
| 130 | # Set mouse sensitivity. | ||
| 131 | # Default: 1.0 | ||
| 132 | mouse_panning_sensitivity = | ||
| 133 | |||
| 134 | # Emulate an analog control stick from keyboard inputs. | ||
| 135 | # 0 (default): Disabled, 1: Enabled | ||
| 136 | emulate_analog_keyboard = | ||
| 137 | |||
| 138 | # Enable mouse inputs to the guest | ||
| 139 | # 0 (default): Disabled, 1: Enabled | ||
| 140 | mouse_enabled = | ||
| 141 | |||
| 142 | # Enable keyboard inputs to the guest | ||
| 143 | # 0 (default): Disabled, 1: Enabled | ||
| 144 | keyboard_enabled = | ||
| 145 | |||
| 146 | [Core] | ||
| 147 | # Whether to use multi-core for CPU emulation | ||
| 148 | # 0: Disabled, 1 (default): Enabled | ||
| 149 | use_multi_core = | ||
| 150 | |||
| 151 | # Enable unsafe extended guest system memory layout (8GB DRAM) | ||
| 152 | # 0 (default): Disabled, 1: Enabled | ||
| 153 | use_unsafe_extended_memory_layout = | ||
| 154 | |||
| 155 | [Cpu] | ||
| 156 | # Adjusts various optimizations. | ||
| 157 | # Auto-select mode enables choice unsafe optimizations. | ||
| 158 | # Accurate enables only safe optimizations. | ||
| 159 | # Unsafe allows any unsafe optimizations. | ||
| 160 | # 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations | ||
| 161 | cpu_accuracy = | ||
| 162 | |||
| 163 | # Allow disabling safe optimizations. | ||
| 164 | # 0 (default): Disabled, 1: Enabled | ||
| 165 | cpu_debug_mode = | ||
| 166 | |||
| 167 | # Enable inline page tables optimization (faster guest memory access) | ||
| 168 | # 0: Disabled, 1 (default): Enabled | ||
| 169 | cpuopt_page_tables = | ||
| 170 | |||
| 171 | # Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps) | ||
| 172 | # 0: Disabled, 1 (default): Enabled | ||
| 173 | cpuopt_block_linking = | ||
| 174 | |||
| 175 | # Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns) | ||
| 176 | # 0: Disabled, 1 (default): Enabled | ||
| 177 | cpuopt_return_stack_buffer = | ||
| 178 | |||
| 179 | # Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture) | ||
| 180 | # 0: Disabled, 1 (default): Enabled | ||
| 181 | cpuopt_fast_dispatcher = | ||
| 182 | |||
| 183 | # Enable context elimination CPU Optimization (reduce host memory use for guest context) | ||
| 184 | # 0: Disabled, 1 (default): Enabled | ||
| 185 | cpuopt_context_elimination = | ||
| 186 | |||
| 187 | # Enable constant propagation CPU optimization (basic IR optimization) | ||
| 188 | # 0: Disabled, 1 (default): Enabled | ||
| 189 | cpuopt_const_prop = | ||
| 190 | |||
| 191 | # Enable miscellaneous CPU optimizations (basic IR optimization) | ||
| 192 | # 0: Disabled, 1 (default): Enabled | ||
| 193 | cpuopt_misc_ir = | ||
| 194 | |||
| 195 | # Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access) | ||
| 196 | # 0: Disabled, 1 (default): Enabled | ||
| 197 | cpuopt_reduce_misalign_checks = | ||
| 198 | |||
| 199 | # Enable Host MMU Emulation (faster guest memory access) | ||
| 200 | # 0: Disabled, 1 (default): Enabled | ||
| 201 | cpuopt_fastmem = | ||
| 202 | |||
| 203 | # Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access) | ||
| 204 | # 0: Disabled, 1 (default): Enabled | ||
| 205 | cpuopt_fastmem_exclusives = | ||
| 206 | |||
| 207 | # Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access) | ||
| 208 | # 0: Disabled, 1 (default): Enabled | ||
| 209 | cpuopt_recompile_exclusives = | ||
| 210 | |||
| 211 | # Enable optimization to ignore invalid memory accesses (faster guest memory access) | ||
| 212 | # 0: Disabled, 1 (default): Enabled | ||
| 213 | cpuopt_ignore_memory_aborts = | ||
| 214 | |||
| 215 | # Enable unfuse FMA (improve performance on CPUs without FMA) | ||
| 216 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | ||
| 217 | # 0: Disabled, 1 (default): Enabled | ||
| 218 | cpuopt_unsafe_unfuse_fma = | ||
| 219 | |||
| 220 | # Enable faster FRSQRTE and FRECPE | ||
| 221 | # Only enabled if cpu_accuracy is set to Unsafe. | ||
| 222 | # 0: Disabled, 1 (default): Enabled | ||
| 223 | cpuopt_unsafe_reduce_fp_error = | ||
| 224 | |||
| 225 | # Enable faster ASIMD instructions (32 bits only) | ||
| 226 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | ||
| 227 | # 0: Disabled, 1 (default): Enabled | ||
| 228 | cpuopt_unsafe_ignore_standard_fpcr = | ||
| 229 | |||
| 230 | # Enable inaccurate NaN handling | ||
| 231 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | ||
| 232 | # 0: Disabled, 1 (default): Enabled | ||
| 233 | cpuopt_unsafe_inaccurate_nan = | ||
| 234 | |||
| 235 | # Disable address space checks (64 bits only) | ||
| 236 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | ||
| 237 | # 0: Disabled, 1 (default): Enabled | ||
| 238 | cpuopt_unsafe_fastmem_check = | ||
| 239 | |||
| 240 | # Enable faster exclusive instructions | ||
| 241 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | ||
| 242 | # 0: Disabled, 1 (default): Enabled | ||
| 243 | cpuopt_unsafe_ignore_global_monitor = | ||
| 244 | |||
| 245 | [Renderer] | ||
| 246 | # Which backend API to use. | ||
| 247 | # 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null | ||
| 248 | backend = | ||
| 249 | |||
| 250 | # Whether to enable asynchronous presentation (Vulkan only) | ||
| 251 | # 0: Off, 1 (default): On | ||
| 252 | async_presentation = | ||
| 253 | |||
| 254 | # Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied). | ||
| 255 | # 0 (default): Disabled, 1: Enabled | ||
| 256 | force_max_clock = | ||
| 257 | |||
| 258 | # Enable graphics API debugging mode. | ||
| 259 | # 0 (default): Disabled, 1: Enabled | ||
| 260 | debug = | ||
| 261 | |||
| 262 | # Enable shader feedback. | ||
| 263 | # 0 (default): Disabled, 1: Enabled | ||
| 264 | renderer_shader_feedback = | ||
| 265 | |||
| 266 | # Enable Nsight Aftermath crash dumps | ||
| 267 | # 0 (default): Disabled, 1: Enabled | ||
| 268 | nsight_aftermath = | ||
| 269 | |||
| 270 | # Disable shader loop safety checks, executing the shader without loop logic changes | ||
| 271 | # 0 (default): Disabled, 1: Enabled | ||
| 272 | disable_shader_loop_safety_checks = | ||
| 273 | |||
| 274 | # Which Vulkan physical device to use (defaults to 0) | ||
| 275 | vulkan_device = | ||
| 276 | |||
| 277 | # 0: 0.5x (360p/540p) [EXPERIMENTAL] | ||
| 278 | # 1: 0.75x (540p/810p) [EXPERIMENTAL] | ||
| 279 | # 2 (default): 1x (720p/1080p) | ||
| 280 | # 3: 2x (1440p/2160p) | ||
| 281 | # 4: 3x (2160p/3240p) | ||
| 282 | # 5: 4x (2880p/4320p) | ||
| 283 | # 6: 5x (3600p/5400p) | ||
| 284 | # 7: 6x (4320p/6480p) | ||
| 285 | resolution_setup = | ||
| 286 | |||
| 287 | # Pixel filter to use when up- or down-sampling rendered frames. | ||
| 288 | # 0: Nearest Neighbor | ||
| 289 | # 1 (default): Bilinear | ||
| 290 | # 2: Bicubic | ||
| 291 | # 3: Gaussian | ||
| 292 | # 4: ScaleForce | ||
| 293 | # 5: AMD FidelityFXâ„¢ï¸ Super Resolution [Vulkan Only] | ||
| 294 | scaling_filter = | ||
| 295 | |||
| 296 | # Anti-Aliasing (AA) | ||
| 297 | # 0 (default): None, 1: FXAA | ||
| 298 | anti_aliasing = | ||
| 299 | |||
| 300 | # Whether to use fullscreen or borderless window mode | ||
| 301 | # 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen | ||
| 302 | fullscreen_mode = | ||
| 303 | |||
| 304 | # Aspect ratio | ||
| 305 | # 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window | ||
| 306 | aspect_ratio = | ||
| 307 | |||
| 308 | # Anisotropic filtering | ||
| 309 | # 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x | ||
| 310 | max_anisotropy = | ||
| 311 | |||
| 312 | # Whether to enable VSync or not. | ||
| 313 | # OpenGL: Values other than 0 enable VSync | ||
| 314 | # Vulkan: FIFO is selected if the requested mode is not supported by the driver. | ||
| 315 | # FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. | ||
| 316 | # FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down. | ||
| 317 | # Mailbox can have lower latency than FIFO and does not tear but may drop frames. | ||
| 318 | # Immediate (no synchronization) just presents whatever is available and can exhibit tearing. | ||
| 319 | # 0: Immediate (Off), 1 (Default): Mailbox (On), 2: FIFO, 3: FIFO Relaxed | ||
| 320 | use_vsync = | ||
| 321 | |||
| 322 | # Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is | ||
| 323 | # not available and GLASM is selected, GLSL will be used. | ||
| 324 | # 0: GLSL, 1 (default): GLASM, 2: SPIR-V | ||
| 325 | shader_backend = | ||
| 326 | |||
| 327 | # Whether to allow asynchronous shader building. | ||
| 328 | # 0 (default): Off, 1: On | ||
| 329 | use_asynchronous_shaders = | ||
| 330 | |||
| 331 | # Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory. | ||
| 332 | # 0 (default): Off, 1: On | ||
| 333 | use_reactive_flushing = | ||
| 334 | |||
| 335 | # NVDEC emulation. | ||
| 336 | # 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding | ||
| 337 | nvdec_emulation = | ||
| 338 | |||
| 339 | # Accelerate ASTC texture decoding. | ||
| 340 | # 0 (default): Off, 1: On | ||
| 341 | accelerate_astc = | ||
| 342 | |||
| 343 | # Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value | ||
| 344 | # 0: Off, 1: On (default) | ||
| 345 | use_speed_limit = | ||
| 346 | |||
| 347 | # Limits the speed of the game to run no faster than this value as a percentage of target speed | ||
| 348 | # 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) | ||
| 349 | speed_limit = | ||
| 350 | |||
| 351 | # Whether to use disk based shader cache | ||
| 352 | # 0: Off, 1 (default): On | ||
| 353 | use_disk_shader_cache = | ||
| 354 | |||
| 355 | # Which gpu accuracy level to use | ||
| 356 | # 0 (default): Normal, 1: High, 2: Extreme (Very slow) | ||
| 357 | gpu_accuracy = | ||
| 358 | |||
| 359 | # Whether to use asynchronous GPU emulation | ||
| 360 | # 0 : Off (slow), 1 (default): On (fast) | ||
| 361 | use_asynchronous_gpu_emulation = | ||
| 362 | |||
| 363 | # Inform the guest that GPU operations completed more quickly than they did. | ||
| 364 | # 0: Off, 1 (default): On | ||
| 365 | use_fast_gpu_time = | ||
| 366 | |||
| 367 | # Force unmodified buffers to be flushed, which can cost performance. | ||
| 368 | # 0: Off (default), 1: On | ||
| 369 | use_pessimistic_flushes = | ||
| 370 | |||
| 371 | # Whether to use garbage collection or not for GPU caches. | ||
| 372 | # 0 (default): Off, 1: On | ||
| 373 | use_caches_gc = | ||
| 374 | |||
| 375 | # The clear color for the renderer. What shows up on the sides of the bottom screen. | ||
| 376 | # Must be in range of 0-255. Defaults to 0 for all. | ||
| 377 | bg_red = | ||
| 378 | bg_blue = | ||
| 379 | bg_green = | ||
| 380 | |||
| 381 | [Audio] | ||
| 382 | # Which audio output engine to use. | ||
| 383 | # auto (default): Auto-select | ||
| 384 | # cubeb: Cubeb audio engine (if available) | ||
| 385 | # sdl2: SDL2 audio engine (if available) | ||
| 386 | # null: No audio output | ||
| 387 | output_engine = | ||
| 388 | |||
| 389 | # Which audio device to use. | ||
| 390 | # auto (default): Auto-select | ||
| 391 | output_device = | ||
| 392 | |||
| 393 | # Output volume. | ||
| 394 | # 100 (default): 100%, 0; mute | ||
| 395 | volume = | ||
| 396 | |||
| 397 | [Data Storage] | ||
| 398 | # Whether to create a virtual SD card. | ||
| 399 | # 1: Yes, 0 (default): No | ||
| 400 | use_virtual_sd = | ||
| 401 | |||
| 402 | # Whether or not to enable gamecard emulation | ||
| 403 | # 1: Yes, 0 (default): No | ||
| 404 | gamecard_inserted = | ||
| 405 | |||
| 406 | # Whether or not the gamecard should be emulated as the current game | ||
| 407 | # If 'gamecard_inserted' is 0 this setting is irrelevant | ||
| 408 | # 1: Yes, 0 (default): No | ||
| 409 | gamecard_current_game = | ||
| 410 | |||
| 411 | # Path to an XCI file to use as the gamecard | ||
| 412 | # If 'gamecard_inserted' is 0 this setting is irrelevant | ||
| 413 | # If 'gamecard_current_game' is 1 this setting is irrelevant | ||
| 414 | gamecard_path = | ||
| 415 | |||
| 416 | [System] | ||
| 417 | # Whether the system is docked | ||
| 418 | # 1 (default): Yes, 0: No | ||
| 419 | use_docked_mode = | ||
| 420 | |||
| 421 | # Sets the seed for the RNG generator built into the switch | ||
| 422 | # rng_seed will be ignored and randomly generated if rng_seed_enabled is false | ||
| 423 | rng_seed_enabled = | ||
| 424 | rng_seed = | ||
| 425 | |||
| 426 | # Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service | ||
| 427 | # This will auto-increment, with the time set being the time the game is started | ||
| 428 | # This override will only occur if custom_rtc_enabled is true, otherwise the current time is used | ||
| 429 | custom_rtc_enabled = | ||
| 430 | custom_rtc = | ||
| 431 | |||
| 432 | # Sets the systems language index | ||
| 433 | # 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, | ||
| 434 | # 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, | ||
| 435 | # 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese | ||
| 436 | language_index = | ||
| 437 | |||
| 438 | # The system region that yuzu will use during emulation | ||
| 439 | # -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan | ||
| 440 | region_index = | ||
| 441 | |||
| 442 | # The system time zone that yuzu will use during emulation | ||
| 443 | # 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone | ||
| 444 | time_zone_index = | ||
| 445 | |||
| 446 | # Sets the sound output mode. | ||
| 447 | # 0: Mono, 1 (default): Stereo, 2: Surround | ||
| 448 | sound_index = | ||
| 449 | |||
| 450 | [Miscellaneous] | ||
| 451 | # A filter which removes logs below a certain logging level. | ||
| 452 | # Examples: *:Debug Kernel.SVC:Trace Service.*:Critical | ||
| 453 | log_filter = *:Trace | ||
| 454 | |||
| 455 | # Use developer keys | ||
| 456 | # 0 (default): Disabled, 1: Enabled | ||
| 457 | use_dev_keys = | ||
| 458 | |||
| 459 | [Debugging] | ||
| 460 | # Record frame time data, can be found in the log directory. Boolean value | ||
| 461 | record_frame_times = | ||
| 462 | # Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them | ||
| 463 | dump_exefs=false | ||
| 464 | # Determines whether or not yuzu will dump all NSOs it attempts to load while loading them | ||
| 465 | dump_nso=false | ||
| 466 | # Determines whether or not yuzu will save the filesystem access log. | ||
| 467 | enable_fs_access_log=false | ||
| 468 | # Enables verbose reporting services | ||
| 469 | reporting_services = | ||
| 470 | # Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode | ||
| 471 | # false: Retail/Normal Mode (default), true: Kiosk Mode | ||
| 472 | quest_flag = | ||
| 473 | # Determines whether debug asserts should be enabled, which will throw an exception on asserts. | ||
| 474 | # false: Disabled (default), true: Enabled | ||
| 475 | use_debug_asserts = | ||
| 476 | # Determines whether unimplemented HLE service calls should be automatically stubbed. | ||
| 477 | # false: Disabled (default), true: Enabled | ||
| 478 | use_auto_stub = | ||
| 479 | # Enables/Disables the macro JIT compiler | ||
| 480 | disable_macro_jit=false | ||
| 481 | # Determines whether to enable the GDB stub and wait for the debugger to attach before running. | ||
| 482 | # false: Disabled (default), true: Enabled | ||
| 483 | use_gdbstub=false | ||
| 484 | # The port to use for the GDB server, if it is enabled. | ||
| 485 | gdbstub_port=6543 | ||
| 486 | |||
| 487 | [WebService] | ||
| 488 | # Whether or not to enable telemetry | ||
| 489 | # 0: No, 1 (default): Yes | ||
| 490 | enable_telemetry = | ||
| 491 | # URL for Web API | ||
| 492 | web_api_url = https://api.yuzu-emu.org | ||
| 493 | # Username and token for yuzu Web Service | ||
| 494 | # See https://profile.yuzu-emu.org/ for more info | ||
| 495 | yuzu_username = | ||
| 496 | yuzu_token = | ||
| 497 | |||
| 498 | [Network] | ||
| 499 | # Name of the network interface device to use with yuzu LAN play. | ||
| 500 | # e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo' | ||
| 501 | # e.g. On Windows: 'Ethernet', 'Wi-Fi' | ||
| 502 | network_interface = | ||
| 503 | |||
| 504 | [AddOns] | ||
| 505 | # Used to disable add-ons | ||
| 506 | # List of title IDs of games that will have add-ons disabled (separated by '|'): | ||
| 507 | title_ids = | ||
| 508 | # For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|') | ||
| 509 | # e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey | ||
| 510 | )"; | ||
| 511 | } // namespace DefaultINI | ||
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp index a7e414b81..c4f631924 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "input_common/drivers/virtual_gamepad.h" | 9 | #include "input_common/drivers/virtual_gamepad.h" |
| 10 | #include "input_common/main.h" | 10 | #include "input_common/main.h" |
| 11 | #include "jni/emu_window/emu_window.h" | 11 | #include "jni/emu_window/emu_window.h" |
| 12 | #include "jni/native.h" | ||
| 12 | 13 | ||
| 13 | void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { | 14 | void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { |
| 14 | m_window_width = ANativeWindow_getWidth(surface); | 15 | m_window_width = ANativeWindow_getWidth(surface); |
| @@ -57,6 +58,13 @@ void EmuWindow_Android::OnRemoveNfcTag() { | |||
| 57 | m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo(); | 58 | m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo(); |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 61 | void EmuWindow_Android::OnFrameDisplayed() { | ||
| 62 | if (!m_first_frame) { | ||
| 63 | EmulationSession::GetInstance().OnEmulationStarted(); | ||
| 64 | m_first_frame = true; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 60 | EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, | 68 | EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, |
| 61 | ANativeWindow* surface, | 69 | ANativeWindow* surface, |
| 62 | std::shared_ptr<Common::DynamicLibrary> driver_library) | 70 | std::shared_ptr<Common::DynamicLibrary> driver_library) |
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.h b/src/android/app/src/main/jni/emu_window/emu_window.h index b38087f73..a34a0e479 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.h +++ b/src/android/app/src/main/jni/emu_window/emu_window.h | |||
| @@ -45,7 +45,7 @@ public: | |||
| 45 | float gyro_z, float accel_x, float accel_y, float accel_z); | 45 | float gyro_z, float accel_x, float accel_y, float accel_z); |
| 46 | void OnReadNfcTag(std::span<u8> data); | 46 | void OnReadNfcTag(std::span<u8> data); |
| 47 | void OnRemoveNfcTag(); | 47 | void OnRemoveNfcTag(); |
| 48 | void OnFrameDisplayed() override {} | 48 | void OnFrameDisplayed() override; |
| 49 | 49 | ||
| 50 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override { | 50 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override { |
| 51 | return {std::make_unique<GraphicsContext_Android>(m_driver_library)}; | 51 | return {std::make_unique<GraphicsContext_Android>(m_driver_library)}; |
| @@ -61,4 +61,6 @@ private: | |||
| 61 | float m_window_height{}; | 61 | float m_window_height{}; |
| 62 | 62 | ||
| 63 | std::shared_ptr<Common::DynamicLibrary> m_driver_library; | 63 | std::shared_ptr<Common::DynamicLibrary> m_driver_library; |
| 64 | |||
| 65 | bool m_first_frame = false; | ||
| 64 | }; | 66 | }; |
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 0e458df38..617288ae4 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -52,8 +52,8 @@ | |||
| 52 | #include "core/hle/service/am/applets/applets.h" | 52 | #include "core/hle/service/am/applets/applets.h" |
| 53 | #include "core/hle/service/filesystem/filesystem.h" | 53 | #include "core/hle/service/filesystem/filesystem.h" |
| 54 | #include "core/loader/loader.h" | 54 | #include "core/loader/loader.h" |
| 55 | #include "frontend_common/config.h" | ||
| 55 | #include "jni/android_common/android_common.h" | 56 | #include "jni/android_common/android_common.h" |
| 56 | #include "jni/config.h" | ||
| 57 | #include "jni/id_cache.h" | 57 | #include "jni/id_cache.h" |
| 58 | #include "jni/native.h" | 58 | #include "jni/native.h" |
| 59 | #include "video_core/renderer_base.h" | 59 | #include "video_core/renderer_base.h" |
| @@ -199,8 +199,8 @@ bool EmulationSession::IsPaused() const { | |||
| 199 | return m_is_running && m_is_paused; | 199 | return m_is_running && m_is_paused; |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | const Core::PerfStatsResults& EmulationSession::PerfStats() const { | 202 | const Core::PerfStatsResults& EmulationSession::PerfStats() { |
| 203 | std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); | 203 | m_perf_stats = m_system.GetAndResetPerfStats(); |
| 204 | return m_perf_stats; | 204 | return m_perf_stats; |
| 205 | } | 205 | } |
| 206 | 206 | ||
| @@ -247,7 +247,14 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) | |||
| 247 | } | 247 | } |
| 248 | } | 248 | } |
| 249 | 249 | ||
| 250 | void EmulationSession::InitializeSystem() { | 250 | void EmulationSession::InitializeSystem(bool reload) { |
| 251 | if (!reload) { | ||
| 252 | // Initialize logging system | ||
| 253 | Common::Log::Initialize(); | ||
| 254 | Common::Log::SetColorConsoleBackendEnabled(true); | ||
| 255 | Common::Log::Start(); | ||
| 256 | } | ||
| 257 | |||
| 251 | // Initialize filesystem. | 258 | // Initialize filesystem. |
| 252 | m_system.SetFilesystem(m_vfs); | 259 | m_system.SetFilesystem(m_vfs); |
| 253 | m_system.GetUserChannel().clear(); | 260 | m_system.GetUserChannel().clear(); |
| @@ -365,8 +372,6 @@ void EmulationSession::RunEmulation() { | |||
| 365 | m_system.InitializeDebugger(); | 372 | m_system.InitializeDebugger(); |
| 366 | } | 373 | } |
| 367 | 374 | ||
| 368 | OnEmulationStarted(); | ||
| 369 | |||
| 370 | while (true) { | 375 | while (true) { |
| 371 | { | 376 | { |
| 372 | [[maybe_unused]] std::unique_lock lock(m_mutex); | 377 | [[maybe_unused]] std::unique_lock lock(m_mutex); |
| @@ -376,11 +381,6 @@ void EmulationSession::RunEmulation() { | |||
| 376 | break; | 381 | break; |
| 377 | } | 382 | } |
| 378 | } | 383 | } |
| 379 | { | ||
| 380 | // Refresh performance stats. | ||
| 381 | std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); | ||
| 382 | m_perf_stats = m_system.GetAndResetPerfStats(); | ||
| 383 | } | ||
| 384 | } | 384 | } |
| 385 | } | 385 | } |
| 386 | 386 | ||
| @@ -462,10 +462,6 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { | |||
| 462 | } | 462 | } |
| 463 | 463 | ||
| 464 | static Core::SystemResultStatus RunEmulation(const std::string& filepath) { | 464 | static Core::SystemResultStatus RunEmulation(const std::string& filepath) { |
| 465 | Common::Log::Initialize(); | ||
| 466 | Common::Log::SetColorConsoleBackendEnabled(true); | ||
| 467 | Common::Log::Start(); | ||
| 468 | |||
| 469 | MicroProfileOnThreadCreate("EmuThread"); | 465 | MicroProfileOnThreadCreate("EmuThread"); |
| 470 | SCOPE_EXIT({ MicroProfileShutdown(); }); | 466 | SCOPE_EXIT({ MicroProfileShutdown(); }); |
| 471 | 467 | ||
| @@ -666,12 +662,13 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c | |||
| 666 | } | 662 | } |
| 667 | } | 663 | } |
| 668 | 664 | ||
| 669 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz) { | 665 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, |
| 670 | // Create the default config.ini. | 666 | jboolean reload) { |
| 671 | Config{}; | ||
| 672 | // Initialize the emulated system. | 667 | // Initialize the emulated system. |
| 673 | EmulationSession::GetInstance().System().Initialize(); | 668 | if (!reload) { |
| 674 | EmulationSession::GetInstance().InitializeSystem(); | 669 | EmulationSession::GetInstance().System().Initialize(); |
| 670 | } | ||
| 671 | EmulationSession::GetInstance().InitializeSystem(reload); | ||
| 675 | } | 672 | } |
| 676 | 673 | ||
| 677 | jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) { | 674 | jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) { |
| @@ -681,17 +678,6 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl | |||
| 681 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( | 678 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( |
| 682 | JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} | 679 | JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} |
| 683 | 680 | ||
| 684 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) { | ||
| 685 | Config{}; | ||
| 686 | } | ||
| 687 | |||
| 688 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz, | ||
| 689 | jstring j_game_id) { | ||
| 690 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); | ||
| 691 | |||
| 692 | env->ReleaseStringUTFChars(j_game_id, game_id.data()); | ||
| 693 | } | ||
| 694 | |||
| 695 | jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { | 681 | jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { |
| 696 | jdoubleArray j_stats = env->NewDoubleArray(4); | 682 | jdoubleArray j_stats = env->NewDoubleArray(4); |
| 697 | 683 | ||
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index 0aa2b085b..78ef96802 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h | |||
| @@ -41,9 +41,9 @@ public: | |||
| 41 | void RunEmulation(); | 41 | void RunEmulation(); |
| 42 | void ShutdownEmulation(); | 42 | void ShutdownEmulation(); |
| 43 | 43 | ||
| 44 | const Core::PerfStatsResults& PerfStats() const; | 44 | const Core::PerfStatsResults& PerfStats(); |
| 45 | void ConfigureFilesystemProvider(const std::string& filepath); | 45 | void ConfigureFilesystemProvider(const std::string& filepath); |
| 46 | void InitializeSystem(); | 46 | void InitializeSystem(bool reload); |
| 47 | Core::SystemResultStatus InitializeEmulation(const std::string& filepath); | 47 | Core::SystemResultStatus InitializeEmulation(const std::string& filepath); |
| 48 | 48 | ||
| 49 | bool IsHandheldOnly(); | 49 | bool IsHandheldOnly(); |
| @@ -52,9 +52,10 @@ public: | |||
| 52 | void OnGamepadDisconnectEvent([[maybe_unused]] int index); | 52 | void OnGamepadDisconnectEvent([[maybe_unused]] int index); |
| 53 | SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); | 53 | SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); |
| 54 | 54 | ||
| 55 | static void OnEmulationStarted(); | ||
| 56 | |||
| 55 | private: | 57 | private: |
| 56 | static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); | 58 | static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); |
| 57 | static void OnEmulationStarted(); | ||
| 58 | static void OnEmulationStopped(Core::SystemResultStatus result); | 59 | static void OnEmulationStopped(Core::SystemResultStatus result); |
| 59 | 60 | ||
| 60 | private: | 61 | private: |
| @@ -80,6 +81,5 @@ private: | |||
| 80 | 81 | ||
| 81 | // Synchronization | 82 | // Synchronization |
| 82 | std::condition_variable_any m_cv; | 83 | std::condition_variable_any m_cv; |
| 83 | mutable std::mutex m_perf_stats_mutex; | ||
| 84 | mutable std::mutex m_mutex; | 84 | mutable std::mutex m_mutex; |
| 85 | }; | 85 | }; |
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp index 8a704960c..8e81816e5 100644 --- a/src/android/app/src/main/jni/native_config.cpp +++ b/src/android/app/src/main/jni/native_config.cpp | |||
| @@ -5,11 +5,14 @@ | |||
| 5 | 5 | ||
| 6 | #include <jni.h> | 6 | #include <jni.h> |
| 7 | 7 | ||
| 8 | #include "android_config.h" | ||
| 9 | #include "android_settings.h" | ||
| 8 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 9 | #include "common/settings.h" | 11 | #include "common/settings.h" |
| 12 | #include "frontend_common/config.h" | ||
| 10 | #include "jni/android_common/android_common.h" | 13 | #include "jni/android_common/android_common.h" |
| 11 | #include "jni/config.h" | 14 | |
| 12 | #include "uisettings.h" | 15 | std::unique_ptr<AndroidConfig> config; |
| 13 | 16 | ||
| 14 | template <typename T> | 17 | template <typename T> |
| 15 | Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { | 18 | Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { |
| @@ -28,6 +31,22 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { | |||
| 28 | 31 | ||
| 29 | extern "C" { | 32 | extern "C" { |
| 30 | 33 | ||
| 34 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) { | ||
| 35 | config = std::make_unique<AndroidConfig>(); | ||
| 36 | } | ||
| 37 | |||
| 38 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) { | ||
| 39 | config.reset(); | ||
| 40 | } | ||
| 41 | |||
| 42 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) { | ||
| 43 | config->AndroidConfig::ReloadAllValues(); | ||
| 44 | } | ||
| 45 | |||
| 46 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) { | ||
| 47 | config->AndroidConfig::SaveAllValues(); | ||
| 48 | } | ||
| 49 | |||
| 31 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, | 50 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, |
| 32 | jstring jkey, jboolean getDefault) { | 51 | jstring jkey, jboolean getDefault) { |
| 33 | auto setting = getSetting<bool>(env, jkey); | 52 | auto setting = getSetting<bool>(env, jkey); |
diff --git a/src/android/app/src/main/jni/native_log.cpp b/src/android/app/src/main/jni/native_log.cpp new file mode 100644 index 000000000..33d691dc8 --- /dev/null +++ b/src/android/app/src/main/jni/native_log.cpp | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <common/logging/log.h> | ||
| 5 | #include <jni.h> | ||
| 6 | |||
| 7 | #include "android_common/android_common.h" | ||
| 8 | |||
| 9 | extern "C" { | ||
| 10 | |||
| 11 | void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) { | ||
| 12 | LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage)); | ||
| 13 | } | ||
| 14 | |||
| 15 | void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) { | ||
| 16 | LOG_WARNING(Frontend, "{}", GetJString(env, jmessage)); | ||
| 17 | } | ||
| 18 | |||
| 19 | void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) { | ||
| 20 | LOG_INFO(Frontend, "{}", GetJString(env, jmessage)); | ||
| 21 | } | ||
| 22 | |||
| 23 | void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) { | ||
| 24 | LOG_ERROR(Frontend, "{}", GetJString(env, jmessage)); | ||
| 25 | } | ||
| 26 | |||
| 27 | void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) { | ||
| 28 | LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage)); | ||
| 29 | } | ||
| 30 | |||
| 31 | } // extern "C" | ||
diff --git a/src/android/app/src/main/res/drawable/ic_audio.xml b/src/android/app/src/main/res/drawable/ic_audio.xml new file mode 100644 index 000000000..e306c3b0c --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_audio.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | android:width="24dp" | ||
| 3 | android:height="24dp" | ||
| 4 | android:viewportHeight="24" | ||
| 5 | android:viewportWidth="24"> | ||
| 6 | <path | ||
| 7 | android:fillColor="?attr/colorControlNormal" | ||
| 8 | android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" /> | ||
| 9 | </vector> | ||
diff --git a/src/android/app/src/main/res/drawable/ic_code.xml b/src/android/app/src/main/res/drawable/ic_code.xml new file mode 100644 index 000000000..26f83b39b --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_code.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | android:width="24dp" | ||
| 3 | android:height="24dp" | ||
| 4 | android:viewportWidth="960" | ||
| 5 | android:viewportHeight="960"> | ||
| 6 | <path | ||
| 7 | android:fillColor="?attr/colorControlNormal" | ||
| 8 | android:pathData="M320,720 L80,480l240,-240 57,57 -184,184 183,183 -56,56ZM640,720 L583,663 767,479 584,296 640,240 880,480 640,720Z"/> | ||
| 9 | </vector> | ||
diff --git a/src/android/app/src/main/res/drawable/ic_graphics.xml b/src/android/app/src/main/res/drawable/ic_graphics.xml new file mode 100644 index 000000000..2fdb5a4d6 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_graphics.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | android:width="24dp" | ||
| 3 | android:height="24dp" | ||
| 4 | android:viewportWidth="960" | ||
| 5 | android:viewportHeight="960"> | ||
| 6 | <path | ||
| 7 | android:fillColor="?attr/colorControlNormal" | ||
| 8 | android:pathData="M160,840q-33,0 -56.5,-23.5T80,760v-560q0,-33 23.5,-56.5T160,120h560q33,0 56.5,23.5T800,200v80h80v80h-80v80h80v80h-80v80h80v80h-80v80q0,33 -23.5,56.5T720,840L160,840ZM160,760h560v-560L160,200v560ZM240,680h200v-160L240,520v160ZM480,400h160v-120L480,280v120ZM240,480h200v-200L240,280v200ZM480,680h160v-240L480,440v240ZM160,200v560,-560Z"/> | ||
| 9 | </vector> | ||
diff --git a/src/android/app/src/main/res/drawable/ic_system_settings.xml b/src/android/app/src/main/res/drawable/ic_system_settings.xml new file mode 100644 index 000000000..7701a2bab --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_system_settings.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | android:width="24dp" | ||
| 3 | android:height="24dp" | ||
| 4 | android:viewportWidth="960" | ||
| 5 | android:viewportHeight="960"> | ||
| 6 | <path | ||
| 7 | android:fillColor="?attr/colorControlNormal" | ||
| 8 | android:pathData="M320,960q-17,0 -28.5,-11.5T280,920q0,-17 11.5,-28.5T320,880q17,0 28.5,11.5T360,920q0,17 -11.5,28.5T320,960ZM480,960q-17,0 -28.5,-11.5T440,920q0,-17 11.5,-28.5T480,880q17,0 28.5,11.5T520,920q0,17 -11.5,28.5T480,960ZM640,960q-17,0 -28.5,-11.5T600,920q0,-17 11.5,-28.5T640,880q17,0 28.5,11.5T680,920q0,17 -11.5,28.5T640,960ZM320,800q-33,0 -56.5,-23.5T240,720v-640q0,-33 23.5,-56.5T320,0h320q33,0 56.5,23.5T720,80v640q0,33 -23.5,56.5T640,800L320,800ZM320,720h320v-40L320,680v40ZM320,600h320v-400L320,200v400ZM320,120h320v-40L320,80v40ZM320,120v-40,40ZM320,720v-40,40Z"/> | ||
| 9 | </vector> | ||
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml new file mode 100644 index 000000000..a26ffbc73 --- /dev/null +++ b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml | |||
| @@ -0,0 +1,233 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| 4 | xmlns:tools="http://schemas.android.com/tools" | ||
| 5 | android:id="@+id/coordinator_about" | ||
| 6 | android:layout_width="match_parent" | ||
| 7 | android:layout_height="match_parent" | ||
| 8 | android:background="?attr/colorSurface"> | ||
| 9 | |||
| 10 | <com.google.android.material.appbar.AppBarLayout | ||
| 11 | android:id="@+id/appbar_about" | ||
| 12 | android:layout_width="match_parent" | ||
| 13 | android:layout_height="wrap_content" | ||
| 14 | android:fitsSystemWindows="true"> | ||
| 15 | |||
| 16 | <com.google.android.material.appbar.MaterialToolbar | ||
| 17 | android:id="@+id/toolbar_about" | ||
| 18 | android:layout_width="match_parent" | ||
| 19 | android:layout_height="?attr/actionBarSize" | ||
| 20 | app:navigationIcon="@drawable/ic_back" | ||
| 21 | app:title="@string/about" /> | ||
| 22 | |||
| 23 | </com.google.android.material.appbar.AppBarLayout> | ||
| 24 | |||
| 25 | <androidx.core.widget.NestedScrollView | ||
| 26 | android:id="@+id/scroll_about" | ||
| 27 | android:layout_width="match_parent" | ||
| 28 | android:layout_height="match_parent" | ||
| 29 | android:fadeScrollbars="false" | ||
| 30 | android:scrollbars="vertical" | ||
| 31 | app:layout_behavior="@string/appbar_scrolling_view_behavior"> | ||
| 32 | |||
| 33 | <LinearLayout | ||
| 34 | android:id="@+id/content_about" | ||
| 35 | android:layout_width="match_parent" | ||
| 36 | android:layout_height="match_parent" | ||
| 37 | android:orientation="horizontal"> | ||
| 38 | |||
| 39 | <ImageView | ||
| 40 | android:id="@+id/image_logo" | ||
| 41 | android:layout_width="200dp" | ||
| 42 | android:layout_height="200dp" | ||
| 43 | android:layout_gravity="center_horizontal" | ||
| 44 | android:padding="20dp" | ||
| 45 | android:src="@drawable/ic_yuzu_title" /> | ||
| 46 | |||
| 47 | <LinearLayout | ||
| 48 | android:layout_width="wrap_content" | ||
| 49 | android:layout_height="wrap_content" | ||
| 50 | android:orientation="vertical"> | ||
| 51 | |||
| 52 | <LinearLayout | ||
| 53 | android:layout_width="match_parent" | ||
| 54 | android:layout_height="wrap_content" | ||
| 55 | android:orientation="vertical" | ||
| 56 | android:paddingHorizontal="16dp" | ||
| 57 | android:paddingVertical="16dp"> | ||
| 58 | |||
| 59 | <com.google.android.material.textview.MaterialTextView | ||
| 60 | style="@style/TextAppearance.Material3.TitleMedium" | ||
| 61 | android:layout_width="match_parent" | ||
| 62 | android:layout_height="wrap_content" | ||
| 63 | android:layout_marginHorizontal="24dp" | ||
| 64 | android:text="@string/about" | ||
| 65 | android:textAlignment="viewStart" /> | ||
| 66 | |||
| 67 | <com.google.android.material.textview.MaterialTextView | ||
| 68 | style="@style/TextAppearance.Material3.BodyMedium" | ||
| 69 | android:layout_width="match_parent" | ||
| 70 | android:layout_height="wrap_content" | ||
| 71 | android:layout_marginHorizontal="24dp" | ||
| 72 | android:layout_marginTop="6dp" | ||
| 73 | android:text="@string/about_app_description" | ||
| 74 | android:textAlignment="viewStart" /> | ||
| 75 | |||
| 76 | </LinearLayout> | ||
| 77 | |||
| 78 | <com.google.android.material.divider.MaterialDivider | ||
| 79 | android:layout_width="match_parent" | ||
| 80 | android:layout_height="wrap_content" | ||
| 81 | android:layout_marginHorizontal="20dp" /> | ||
| 82 | |||
| 83 | <LinearLayout | ||
| 84 | android:id="@+id/button_contributors" | ||
| 85 | android:layout_width="match_parent" | ||
| 86 | android:layout_height="wrap_content" | ||
| 87 | android:background="?attr/selectableItemBackground" | ||
| 88 | android:orientation="vertical" | ||
| 89 | android:paddingHorizontal="16dp" | ||
| 90 | android:paddingVertical="16dp"> | ||
| 91 | |||
| 92 | <com.google.android.material.textview.MaterialTextView | ||
| 93 | style="@style/TextAppearance.Material3.TitleMedium" | ||
| 94 | android:layout_width="match_parent" | ||
| 95 | android:layout_height="wrap_content" | ||
| 96 | android:layout_marginHorizontal="24dp" | ||
| 97 | android:text="@string/contributors" | ||
| 98 | android:textAlignment="viewStart" /> | ||
| 99 | |||
| 100 | <com.google.android.material.textview.MaterialTextView | ||
| 101 | style="@style/TextAppearance.Material3.BodyMedium" | ||
| 102 | android:layout_width="match_parent" | ||
| 103 | android:layout_height="wrap_content" | ||
| 104 | android:layout_marginHorizontal="24dp" | ||
| 105 | android:layout_marginTop="6dp" | ||
| 106 | android:text="@string/contributors_description" | ||
| 107 | android:textAlignment="viewStart" /> | ||
| 108 | |||
| 109 | </LinearLayout> | ||
| 110 | |||
| 111 | <com.google.android.material.divider.MaterialDivider | ||
| 112 | android:layout_width="match_parent" | ||
| 113 | android:layout_height="wrap_content" | ||
| 114 | android:layout_marginHorizontal="20dp" /> | ||
| 115 | |||
| 116 | <LinearLayout | ||
| 117 | android:id="@+id/button_licenses" | ||
| 118 | android:layout_width="match_parent" | ||
| 119 | android:layout_height="wrap_content" | ||
| 120 | android:background="?attr/selectableItemBackground" | ||
| 121 | android:orientation="vertical" | ||
| 122 | android:paddingHorizontal="16dp" | ||
| 123 | android:paddingVertical="16dp"> | ||
| 124 | |||
| 125 | <com.google.android.material.textview.MaterialTextView | ||
| 126 | style="@style/TextAppearance.Material3.TitleMedium" | ||
| 127 | android:layout_width="match_parent" | ||
| 128 | android:layout_height="wrap_content" | ||
| 129 | android:layout_marginHorizontal="24dp" | ||
| 130 | android:text="@string/licenses" | ||
| 131 | android:textAlignment="viewStart" /> | ||
| 132 | |||
| 133 | <com.google.android.material.textview.MaterialTextView | ||
| 134 | style="@style/TextAppearance.Material3.BodyMedium" | ||
| 135 | android:layout_width="match_parent" | ||
| 136 | android:layout_height="wrap_content" | ||
| 137 | android:layout_marginHorizontal="24dp" | ||
| 138 | android:layout_marginTop="6dp" | ||
| 139 | android:text="@string/licenses_description" | ||
| 140 | android:textAlignment="viewStart" /> | ||
| 141 | |||
| 142 | </LinearLayout> | ||
| 143 | |||
| 144 | <com.google.android.material.divider.MaterialDivider | ||
| 145 | android:layout_width="match_parent" | ||
| 146 | android:layout_height="wrap_content" | ||
| 147 | android:layout_marginHorizontal="20dp" /> | ||
| 148 | |||
| 149 | <LinearLayout | ||
| 150 | android:id="@+id/button_build_hash" | ||
| 151 | android:layout_width="match_parent" | ||
| 152 | android:layout_height="wrap_content" | ||
| 153 | android:background="?attr/selectableItemBackground" | ||
| 154 | android:orientation="vertical" | ||
| 155 | android:paddingHorizontal="16dp" | ||
| 156 | android:paddingVertical="16dp"> | ||
| 157 | |||
| 158 | <com.google.android.material.textview.MaterialTextView | ||
| 159 | style="@style/TextAppearance.Material3.TitleMedium" | ||
| 160 | android:layout_width="match_parent" | ||
| 161 | android:layout_height="wrap_content" | ||
| 162 | android:layout_marginHorizontal="24dp" | ||
| 163 | android:text="@string/build" | ||
| 164 | android:textAlignment="viewStart" /> | ||
| 165 | |||
| 166 | <com.google.android.material.textview.MaterialTextView | ||
| 167 | android:id="@+id/text_build_hash" | ||
| 168 | style="@style/TextAppearance.Material3.BodyMedium" | ||
| 169 | android:layout_width="match_parent" | ||
| 170 | android:layout_height="wrap_content" | ||
| 171 | android:layout_marginHorizontal="24dp" | ||
| 172 | android:layout_marginTop="6dp" | ||
| 173 | android:textAlignment="viewStart" | ||
| 174 | tools:text="abc123" /> | ||
| 175 | |||
| 176 | </LinearLayout> | ||
| 177 | |||
| 178 | <com.google.android.material.divider.MaterialDivider | ||
| 179 | android:layout_width="match_parent" | ||
| 180 | android:layout_height="wrap_content" | ||
| 181 | android:layout_marginHorizontal="20dp" /> | ||
| 182 | |||
| 183 | <LinearLayout | ||
| 184 | android:layout_width="match_parent" | ||
| 185 | android:layout_height="wrap_content" | ||
| 186 | android:layout_marginHorizontal="40dp" | ||
| 187 | android:layout_marginTop="12dp" | ||
| 188 | android:layout_marginBottom="16dp" | ||
| 189 | android:gravity="center_horizontal" | ||
| 190 | android:orientation="horizontal"> | ||
| 191 | |||
| 192 | <Button | ||
| 193 | android:id="@+id/button_discord" | ||
| 194 | style="?attr/materialIconButtonStyle" | ||
| 195 | android:layout_width="0dp" | ||
| 196 | android:layout_height="wrap_content" | ||
| 197 | android:layout_weight="1" | ||
| 198 | app:icon="@drawable/ic_discord" | ||
| 199 | app:iconGravity="textEnd" | ||
| 200 | app:iconSize="24dp" | ||
| 201 | app:iconTint="?attr/colorOnSurface" /> | ||
| 202 | |||
| 203 | <Button | ||
| 204 | android:id="@+id/button_website" | ||
| 205 | style="?attr/materialIconButtonStyle" | ||
| 206 | android:layout_width="0dp" | ||
| 207 | android:layout_height="wrap_content" | ||
| 208 | android:layout_weight="1" | ||
| 209 | app:icon="@drawable/ic_website" | ||
| 210 | app:iconGravity="textEnd" | ||
| 211 | app:iconSize="24dp" | ||
| 212 | app:iconTint="?attr/colorOnSurface" /> | ||
| 213 | |||
| 214 | <Button | ||
| 215 | android:id="@+id/button_github" | ||
| 216 | style="?attr/materialIconButtonStyle" | ||
| 217 | android:layout_width="0dp" | ||
| 218 | android:layout_height="wrap_content" | ||
| 219 | android:layout_weight="1" | ||
| 220 | app:icon="@drawable/ic_github" | ||
| 221 | app:iconGravity="textEnd" | ||
| 222 | app:iconSize="24dp" | ||
| 223 | app:iconTint="?attr/colorOnSurface" /> | ||
| 224 | |||
| 225 | </LinearLayout> | ||
| 226 | |||
| 227 | </LinearLayout> | ||
| 228 | |||
| 229 | </LinearLayout> | ||
| 230 | |||
| 231 | </androidx.core.widget.NestedScrollView> | ||
| 232 | |||
| 233 | </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||
diff --git a/src/android/app/src/main/res/layout/card_home_option.xml b/src/android/app/src/main/res/layout/card_home_option.xml index 6e8a232f9..cb667c928 100644 --- a/src/android/app/src/main/res/layout/card_home_option.xml +++ b/src/android/app/src/main/res/layout/card_home_option.xml | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | android:id="@+id/option_card" | 6 | android:id="@+id/option_card" |
| 7 | android:layout_width="match_parent" | 7 | android:layout_width="match_parent" |
| 8 | android:layout_height="wrap_content" | 8 | android:layout_height="wrap_content" |
| 9 | android:layout_marginVertical="12dp" | 9 | android:layout_marginBottom="24dp" |
| 10 | android:layout_marginHorizontal="16dp" | 10 | android:layout_marginHorizontal="12dp" |
| 11 | android:background="?attr/selectableItemBackground" | 11 | android:background="?attr/selectableItemBackground" |
| 12 | android:backgroundTint="?attr/colorSurfaceVariant" | 12 | android:backgroundTint="?attr/colorSurfaceVariant" |
| 13 | android:clickable="true" | 13 | android:clickable="true" |
diff --git a/src/android/app/src/main/res/layout/fragment_about.xml b/src/android/app/src/main/res/layout/fragment_about.xml index 3e1d98451..a24f5230e 100644 --- a/src/android/app/src/main/res/layout/fragment_about.xml +++ b/src/android/app/src/main/res/layout/fragment_about.xml | |||
| @@ -38,17 +38,17 @@ | |||
| 38 | 38 | ||
| 39 | <ImageView | 39 | <ImageView |
| 40 | android:id="@+id/image_logo" | 40 | android:id="@+id/image_logo" |
| 41 | android:layout_width="250dp" | 41 | android:layout_width="150dp" |
| 42 | android:layout_height="250dp" | 42 | android:layout_height="150dp" |
| 43 | android:layout_marginTop="20dp" | 43 | android:layout_marginTop="24dp" |
| 44 | android:layout_marginBottom="28dp" | ||
| 44 | android:layout_gravity="center_horizontal" | 45 | android:layout_gravity="center_horizontal" |
| 45 | android:src="@drawable/ic_yuzu_title" /> | 46 | android:src="@drawable/ic_yuzu_title" /> |
| 46 | 47 | ||
| 47 | <com.google.android.material.divider.MaterialDivider | 48 | <com.google.android.material.divider.MaterialDivider |
| 48 | android:layout_width="match_parent" | 49 | android:layout_width="match_parent" |
| 49 | android:layout_height="wrap_content" | 50 | android:layout_height="wrap_content" |
| 50 | android:layout_marginHorizontal="20dp" | 51 | android:layout_marginHorizontal="20dp" /> |
| 51 | android:layout_marginTop="28dp" /> | ||
| 52 | 52 | ||
| 53 | <LinearLayout | 53 | <LinearLayout |
| 54 | android:layout_width="match_parent" | 54 | android:layout_width="match_parent" |
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 750ce094a..5252adf54 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml | |||
| @@ -134,18 +134,21 @@ | |||
| 134 | <FrameLayout | 134 | <FrameLayout |
| 135 | android:id="@+id/overlay_container" | 135 | android:id="@+id/overlay_container" |
| 136 | android:layout_width="match_parent" | 136 | android:layout_width="match_parent" |
| 137 | android:layout_height="match_parent"> | 137 | android:layout_height="match_parent" |
| 138 | android:fitsSystemWindows="true"> | ||
| 138 | 139 | ||
| 139 | <TextView | 140 | <com.google.android.material.textview.MaterialTextView |
| 140 | android:id="@+id/show_fps_text" | 141 | android:id="@+id/show_fps_text" |
| 142 | style="@style/TextAppearance.Material3.BodySmall" | ||
| 141 | android:layout_width="wrap_content" | 143 | android:layout_width="wrap_content" |
| 142 | android:layout_height="wrap_content" | 144 | android:layout_height="wrap_content" |
| 143 | android:layout_gravity="left" | 145 | android:layout_gravity="left" |
| 144 | android:clickable="false" | 146 | android:clickable="false" |
| 145 | android:focusable="false" | 147 | android:focusable="false" |
| 146 | android:shadowColor="@android:color/black" | 148 | android:paddingHorizontal="20dp" |
| 147 | android:textColor="@android:color/white" | 149 | android:textColor="@android:color/white" |
| 148 | android:textSize="12sp" | 150 | android:shadowColor="@android:color/black" |
| 151 | android:shadowRadius="3" | ||
| 149 | tools:ignore="RtlHardcoded" /> | 152 | tools:ignore="RtlHardcoded" /> |
| 150 | 153 | ||
| 151 | </FrameLayout> | 154 | </FrameLayout> |
diff --git a/src/android/app/src/main/res/layout/fragment_home_settings.xml b/src/android/app/src/main/res/layout/fragment_home_settings.xml index 1cb421dcb..d84093ba3 100644 --- a/src/android/app/src/main/res/layout/fragment_home_settings.xml +++ b/src/android/app/src/main/res/layout/fragment_home_settings.xml | |||
| @@ -14,13 +14,14 @@ | |||
| 14 | android:layout_width="match_parent" | 14 | android:layout_width="match_parent" |
| 15 | android:layout_height="match_parent" | 15 | android:layout_height="match_parent" |
| 16 | android:orientation="vertical" | 16 | android:orientation="vertical" |
| 17 | android:background="?attr/colorSurface"> | 17 | android:background="?attr/colorSurface" |
| 18 | android:paddingHorizontal="8dp"> | ||
| 18 | 19 | ||
| 19 | <ImageView | 20 | <ImageView |
| 20 | android:id="@+id/logo_image" | 21 | android:id="@+id/logo_image" |
| 21 | android:layout_width="128dp" | 22 | android:layout_width="96dp" |
| 22 | android:layout_height="128dp" | 23 | android:layout_height="96dp" |
| 23 | android:layout_margin="64dp" | 24 | android:layout_marginVertical="32dp" |
| 24 | android:layout_gravity="center_horizontal" | 25 | android:layout_gravity="center_horizontal" |
| 25 | android:src="@drawable/ic_yuzu_full" /> | 26 | android:src="@drawable/ic_yuzu_full" /> |
| 26 | 27 | ||
diff --git a/src/android/app/src/main/res/layout/fragment_search.xml b/src/android/app/src/main/res/layout/fragment_search.xml index b8d54d947..efdfd7047 100644 --- a/src/android/app/src/main/res/layout/fragment_search.xml +++ b/src/android/app/src/main/res/layout/fragment_search.xml | |||
| @@ -127,6 +127,7 @@ | |||
| 127 | android:layout_height="wrap_content" | 127 | android:layout_height="wrap_content" |
| 128 | android:clipToPadding="false" | 128 | android:clipToPadding="false" |
| 129 | android:paddingVertical="4dp" | 129 | android:paddingVertical="4dp" |
| 130 | app:checkedChip="@id/chip_recently_played" | ||
| 130 | app:chipSpacingHorizontal="12dp" | 131 | app:chipSpacingHorizontal="12dp" |
| 131 | app:singleLine="true" | 132 | app:singleLine="true" |
| 132 | app:singleSelection="true"> | 133 | app:singleSelection="true"> |
diff --git a/src/android/app/src/main/res/layout/list_item_setting.xml b/src/android/app/src/main/res/layout/list_item_setting.xml index f1037a740..544280e75 100644 --- a/src/android/app/src/main/res/layout/list_item_setting.xml +++ b/src/android/app/src/main/res/layout/list_item_setting.xml | |||
| @@ -10,41 +10,59 @@ | |||
| 10 | android:focusable="true" | 10 | android:focusable="true" |
| 11 | android:gravity="center_vertical" | 11 | android:gravity="center_vertical" |
| 12 | android:minHeight="72dp" | 12 | android:minHeight="72dp" |
| 13 | android:padding="@dimen/spacing_large"> | 13 | android:padding="16dp"> |
| 14 | 14 | ||
| 15 | <LinearLayout | 15 | <LinearLayout |
| 16 | android:layout_width="match_parent" | 16 | android:layout_width="match_parent" |
| 17 | android:layout_height="wrap_content" | 17 | android:layout_height="wrap_content" |
| 18 | android:orientation="vertical"> | 18 | android:orientation="horizontal"> |
| 19 | 19 | ||
| 20 | <com.google.android.material.textview.MaterialTextView | 20 | <ImageView |
| 21 | android:id="@+id/text_setting_name" | 21 | android:id="@+id/icon" |
| 22 | style="@style/TextAppearance.Material3.HeadlineMedium" | 22 | android:layout_width="24dp" |
| 23 | android:layout_width="match_parent" | 23 | android:layout_height="24dp" |
| 24 | android:layout_height="wrap_content" | 24 | android:layout_marginStart="8dp" |
| 25 | android:textAlignment="viewStart" | 25 | android:layout_marginEnd="24dp" |
| 26 | android:textSize="16sp" | 26 | android:layout_gravity="center_vertical" |
| 27 | app:lineHeight="22dp" | 27 | android:visibility="gone" |
| 28 | tools:text="Setting Name" /> | 28 | app:tint="?attr/colorOnSurface" /> |
| 29 | |||
| 30 | <com.google.android.material.textview.MaterialTextView | ||
| 31 | android:id="@+id/text_setting_description" | ||
| 32 | style="@style/TextAppearance.Material3.BodySmall" | ||
| 33 | android:layout_width="match_parent" | ||
| 34 | android:layout_height="wrap_content" | ||
| 35 | android:layout_marginTop="@dimen/spacing_small" | ||
| 36 | android:textAlignment="viewStart" | ||
| 37 | tools:text="@string/app_disclaimer" /> | ||
| 38 | 29 | ||
| 39 | <com.google.android.material.textview.MaterialTextView | 30 | <LinearLayout |
| 40 | android:id="@+id/text_setting_value" | ||
| 41 | style="@style/TextAppearance.Material3.LabelMedium" | ||
| 42 | android:layout_width="match_parent" | 31 | android:layout_width="match_parent" |
| 43 | android:layout_height="wrap_content" | 32 | android:layout_height="wrap_content" |
| 44 | android:layout_marginTop="@dimen/spacing_small" | 33 | android:orientation="vertical"> |
| 45 | android:textAlignment="viewStart" | 34 | |
| 46 | android:textStyle="bold" | 35 | <com.google.android.material.textview.MaterialTextView |
| 47 | tools:text="1x" /> | 36 | android:id="@+id/text_setting_name" |
| 37 | style="@style/TextAppearance.Material3.HeadlineMedium" | ||
| 38 | android:layout_width="match_parent" | ||
| 39 | android:layout_height="wrap_content" | ||
| 40 | android:textAlignment="viewStart" | ||
| 41 | android:textSize="17sp" | ||
| 42 | app:lineHeight="22dp" | ||
| 43 | tools:text="Setting Name" /> | ||
| 44 | |||
| 45 | <com.google.android.material.textview.MaterialTextView | ||
| 46 | android:id="@+id/text_setting_description" | ||
| 47 | style="@style/TextAppearance.Material3.BodySmall" | ||
| 48 | android:layout_width="match_parent" | ||
| 49 | android:layout_height="wrap_content" | ||
| 50 | android:layout_marginTop="@dimen/spacing_small" | ||
| 51 | android:textAlignment="viewStart" | ||
| 52 | tools:text="@string/app_disclaimer" /> | ||
| 53 | |||
| 54 | <com.google.android.material.textview.MaterialTextView | ||
| 55 | android:id="@+id/text_setting_value" | ||
| 56 | style="@style/TextAppearance.Material3.LabelMedium" | ||
| 57 | android:layout_width="match_parent" | ||
| 58 | android:layout_height="wrap_content" | ||
| 59 | android:layout_marginTop="@dimen/spacing_small" | ||
| 60 | android:textAlignment="viewStart" | ||
| 61 | android:textStyle="bold" | ||
| 62 | android:textSize="13sp" | ||
| 63 | tools:text="1x" /> | ||
| 64 | |||
| 65 | </LinearLayout> | ||
| 48 | 66 | ||
| 49 | </LinearLayout> | 67 | </LinearLayout> |
| 50 | 68 | ||
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 a5767adee..a8f5aff78 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 | |||
| @@ -8,9 +8,7 @@ | |||
| 8 | android:clickable="true" | 8 | android:clickable="true" |
| 9 | android:focusable="true" | 9 | android:focusable="true" |
| 10 | android:minHeight="72dp" | 10 | android:minHeight="72dp" |
| 11 | android:paddingVertical="@dimen/spacing_large" | 11 | android:padding="16dp"> |
| 12 | android:paddingStart="@dimen/spacing_large" | ||
| 13 | android:paddingEnd="24dp"> | ||
| 14 | 12 | ||
| 15 | <com.google.android.material.materialswitch.MaterialSwitch | 13 | <com.google.android.material.materialswitch.MaterialSwitch |
| 16 | android:id="@+id/switch_widget" | 14 | android:id="@+id/switch_widget" |
| @@ -24,7 +22,7 @@ | |||
| 24 | android:layout_height="wrap_content" | 22 | android:layout_height="wrap_content" |
| 25 | android:layout_alignParentTop="true" | 23 | android:layout_alignParentTop="true" |
| 26 | android:layout_centerVertical="true" | 24 | android:layout_centerVertical="true" |
| 27 | android:layout_marginEnd="@dimen/spacing_large" | 25 | android:layout_marginEnd="24dp" |
| 28 | android:layout_toStartOf="@+id/switch_widget" | 26 | android:layout_toStartOf="@+id/switch_widget" |
| 29 | android:gravity="center_vertical" | 27 | android:gravity="center_vertical" |
| 30 | android:orientation="vertical"> | 28 | android:orientation="vertical"> |
| @@ -35,7 +33,7 @@ | |||
| 35 | android:layout_width="wrap_content" | 33 | android:layout_width="wrap_content" |
| 36 | android:layout_height="wrap_content" | 34 | android:layout_height="wrap_content" |
| 37 | android:textAlignment="viewStart" | 35 | android:textAlignment="viewStart" |
| 38 | android:textSize="16sp" | 36 | android:textSize="17sp" |
| 39 | app:lineHeight="28dp" | 37 | app:lineHeight="28dp" |
| 40 | tools:text="@string/frame_limit_enable" /> | 38 | tools:text="@string/frame_limit_enable" /> |
| 41 | 39 | ||
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 cf85bc0da..21276b19e 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 | |||
| @@ -7,7 +7,8 @@ | |||
| 7 | android:layout_height="wrap_content" | 7 | android:layout_height="wrap_content" |
| 8 | android:layout_gravity="start|center_vertical" | 8 | android:layout_gravity="start|center_vertical" |
| 9 | android:paddingHorizontal="@dimen/spacing_large" | 9 | android:paddingHorizontal="@dimen/spacing_large" |
| 10 | android:paddingVertical="16dp" | 10 | android:paddingTop="16dp" |
| 11 | android:paddingBottom="8dp" | ||
| 11 | android:textAlignment="viewStart" | 12 | android:textAlignment="viewStart" |
| 12 | android:textColor="?attr/colorPrimary" | 13 | android:textColor="?attr/colorPrimary" |
| 13 | android:textStyle="bold" | 14 | android:textStyle="bold" |
diff --git a/src/android/app/src/main/res/resources.properties b/src/android/app/src/main/res/resources.properties new file mode 100644 index 000000000..467b3efec --- /dev/null +++ b/src/android/app/src/main/res/resources.properties | |||
| @@ -0,0 +1 @@ | |||
| unqualifiedResLocale=en-US | |||
diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml new file mode 100644 index 000000000..07dffffe8 --- /dev/null +++ b/src/android/app/src/main/res/values-ar/strings.xml | |||
| @@ -0,0 +1,385 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | ||
| 3 | |||
| 4 | <string name="emulation_notification_channel_name">Ø§Ù„Ù…ØØ§ÙƒÙŠ Ù†Ø´Ø·</string> | ||
| 5 | <string name="emulation_notification_channel_description">اظهار اشعار دائم عندما يكون Ø§Ù„Ù…ØØ§ÙƒÙŠ Ù†Ø´Ø·Ø§Ù‹</string> | ||
| 6 | <string name="emulation_notification_running">يوزو يعمل</string> | ||
| 7 | <string name="notice_notification_channel_name">الإشعارات والأخطاء</string> | ||
| 8 | <string name="notice_notification_channel_description">اظهار اشعار عند ØØµÙˆÙ„ اي مشكلة.</string> | ||
| 9 | <string name="notification_permission_not_granted">لم يتم Ù…Ù†Ø Ø¥Ø°Ù† الإشعار</string> | ||
| 10 | |||
| 11 | <!-- Setup strings --> | ||
| 12 | <string name="welcome">Ù…Ø±ØØ¨Ù‹Ø§</string> | ||
| 13 | <string name="welcome_description">والانتقال إلى Ø§Ù„Ù…ØØ§ÙƒØ§Ø© <b>يوزو</b> تعر٠على كيÙية إعداد.</string> | ||
| 14 | <string name="get_started">لنبدأ</string> | ||
| 15 | <string name="keys">Ø§Ù„Ù…ÙØ§ØªÙŠØ</string> | ||
| 16 | <string name="keys_description">اختر مل٠<b>prod.keys</b> من الزر ادناه</string> | ||
| 17 | <string name="select_keys">إختيار Ø§Ù„Ù…ÙØ§ØªÙŠØ</string> | ||
| 18 | <string name="games">الألعاب</string> | ||
| 19 | <string name="games_description">اختر مجلد <b>العابك</b> من الزر ادناه.</string> | ||
| 20 | <string name="done">إنهاء</string> | ||
| 21 | <string name="done_description">كل شيء جاهز./n استمتع بألعابك!</string> | ||
| 22 | <string name="text_continue">استمر</string> | ||
| 23 | <string name="next">التالي</string> | ||
| 24 | <string name="back">عودة</string> | ||
| 25 | <string name="add_games">Ø¥Ø¶Ø§ÙØ© ألعاب</string> | ||
| 26 | <string name="add_games_description">إختار مجلد ألعابك</string> | ||
| 27 | <string name="step_complete">مكتمل</string> | ||
| 28 | |||
| 29 | <!-- Home strings --> | ||
| 30 | <string name="home_games">الألعاب</string> | ||
| 31 | <string name="home_search">Ø§Ù„Ø¨ØØ«</string> | ||
| 32 | <string name="home_settings">الإعدادات</string> | ||
| 33 | <string name="empty_gamelist">لم يتم العثور على Ù…Ù„ÙØ§Øª او لم يتم ØªØØ¯ÙŠØ¯ مسار العاب.</string> | ||
| 34 | <string name="search_and_filter_games">Ø¨ØØ« وتصÙية الألعاب</string> | ||
| 35 | <string name="select_games_folder">ØªØØ¯ÙŠØ¯ مجلد الألعاب</string> | ||
| 36 | <string name="select_games_folder_description">ÙŠØ³Ù…Ø Ù„Ù€ يوزو بملء قائمة الألعاب</string> | ||
| 37 | <string name="add_games_warning">تخط٠اختيار مجلد الالعاب؟</string> | ||
| 38 | <string name="add_games_warning_description">لن يتم عرض الألعاب ÙÙŠ قائمة الألعاب إذا لم يتم ØªØØ¯ÙŠØ¯ مجلد</string> | ||
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | ||
| 40 | <string name="home_search_games">Ø§Ù„Ø¨ØØ« عن ألعاب</string> | ||
| 41 | <string name="search_settings">إعدادات Ø§Ù„Ø¨ØØ«</string> | ||
| 42 | <string name="games_dir_selected">تم ØªØØ¯ÙŠØ¯ مجلد الألعاب</string> | ||
| 43 | <string name="install_prod_keys">تثبيت prod.keys</string> | ||
| 44 | <string name="install_prod_keys_description">مطلوب Ù„ÙÙƒ تشÙير ألعاب البيع بالتجزئة</string> | ||
| 45 | <string name="install_prod_keys_warning">تخطي Ø¥Ø¶Ø§ÙØ© Ø§Ù„Ù…ÙØ§ØªÙŠØØŸ</string> | ||
| 46 | <string name="install_prod_keys_warning_description">مطلوب Ù…ÙØ§ØªÙŠØ ØµØ§Ù„ØØ© Ù„Ù…ØØ§ÙƒØ§Ø© ألعاب البيع بالتجزئة. ستعمل تطبيقات البيرة المنزلية Ùقط إذا تابعت</string> | ||
| 47 | <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> | ||
| 48 | <string name="notifications">التنبيهات</string> | ||
| 49 | <string name="notifications_description">Ø§Ù…Ù†Ø Ø¥Ø°Ù† الإشعار باستخدام الزر أدناه</string> | ||
| 50 | <string name="give_permission">Ù…Ù†Ø Ø§Ù„Ø¥Ø°Ù†</string> | ||
| 51 | <string name="notification_warning">تخطي Ù…Ù†Ø Ø¥Ø°Ù† الإشعارات؟</string> | ||
| 52 | <string name="notification_warning_description">لن يتمكن يوزو من إشعارك بالمعلومات المهمة</string> | ||
| 53 | <string name="permission_denied">تم Ø±ÙØ¶ الإذن</string> | ||
| 54 | <string name="permission_denied_description">لقد Ø±ÙØ¶Øª هذا الإذن عدة مرات ويتعين عليك الآن منØÙ‡ يدويًا ÙÙŠ إعدادات النظام</string> | ||
| 55 | <string name="about">ØÙˆÙ„</string> | ||
| 56 | <string name="about_description">بناء الإصدار، والاعتمادات، وأكثر من ذلك</string> | ||
| 57 | <string name="warning_help">مساعدة</string> | ||
| 58 | <string name="warning_skip">تخطي</string> | ||
| 59 | <string name="warning_cancel">إلغاء</string> | ||
| 60 | <string name="install_amiibo_keys">تثبيت Ù…ÙØ§ØªÙŠØ أميبو</string> | ||
| 61 | <string name="install_amiibo_keys_description">مطلوب لاستخدام أميبو ÙÙŠ اللعبة</string> | ||
| 62 | <string name="invalid_keys_file">تم ØªØØ¯ÙŠØ¯ Ù…Ù„Ù Ù…ÙØ§ØªÙŠØ غير صالØ</string> | ||
| 63 | <string name="install_keys_success">تم تثبيت Ø§Ù„Ù…ÙØ§ØªÙŠØ بنجاØ</string> | ||
| 64 | <string name="reading_keys_failure">خطأ ÙÙŠ قراءة Ù…ÙØ§ØªÙŠØ التشÙير</string> | ||
| 65 | <string name="invalid_keys_error">Ù…ÙØ§ØªÙŠØ التشÙير غير ØµØ§Ù„ØØ©</string> | ||
| 66 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | ||
| 67 | <string name="install_keys_failure_description">Ø§Ù„Ù…Ù„Ù Ø§Ù„Ù…ØØ¯Ø¯ غير صØÙŠØ أو تالÙ. يرجى إعادة Ø§Ù„Ù…ÙØ§ØªÙŠØ الخاصة بك</string> | ||
| 68 | <string name="install_gpu_driver">GPU تثبيت برنامج تشغيل</string> | ||
| 69 | <string name="install_gpu_driver_description">قم بتثبيت برامج تشغيل بديلة Ù„Ù„ØØµÙˆÙ„ على أداء أو دقة Ø£ÙØ¶Ù„</string> | ||
| 70 | <string name="advanced_settings">إعدادات متقدمة</string> | ||
| 71 | <string name="advanced_settings_game">إعدادات متقدمة: %1$s</string> | ||
| 72 | <string name="settings_description">تكوين إعدادات Ø§Ù„Ù…ØØ§ÙƒÙŠ</string> | ||
| 73 | <string name="search_recently_played">لعبت مؤخرا</string> | ||
| 74 | <string name="search_recently_added">أضي٠مؤخرا</string> | ||
| 75 | <string name="search_retail">بيع بالتجزئة</string> | ||
| 76 | <string name="search_homebrew">البيرة المنزلية</string> | ||
| 77 | <string name="open_user_folder">ÙØªØ مجلد يوزو</string> | ||
| 78 | <string name="open_user_folder_description">إدارة Ù…Ù„ÙØ§Øª يوزو الداخلية</string> | ||
| 79 | <string name="theme_and_color_description">تعديل مظهر التطبيق</string> | ||
| 80 | <string name="no_file_manager">لم يتم العثور على مدير Ø§Ù„Ù…Ù„ÙØ§Øª</string> | ||
| 81 | <string name="notification_no_directory_link">لا يمكن ÙØªØ مجلد يوزو</string> | ||
| 82 | <string name="notification_no_directory_link_description">الرجاء ØªØØ¯ÙŠØ¯ موقع مجلد المستخدم باستخدام Ø§Ù„Ù„ÙˆØØ© الجانبية لمدير Ø§Ù„Ù…Ù„ÙØ§Øª يدويًا</string> | ||
| 83 | <string name="manage_save_data">إدارة ØÙظ البيانات</string> | ||
| 84 | <string name="manage_save_data_description">ØÙظ البيانات التي تم العثور عليها. يرجى اختيار Ø£ØØ¯ الخيارات التالية</string> | ||
| 85 | <string name="import_export_saves_description">استيراد أو تصدير Ù…Ù„ÙØ§Øª الØÙظ</string> | ||
| 86 | <string name="save_file_imported_success">تم الاستيراد بنجاØ</string> | ||
| 87 | <string name="save_file_invalid_zip_structure">بنية مجلد الØÙظ غير ØµØ§Ù„ØØ©</string> | ||
| 88 | <string name="save_file_invalid_zip_structure_description">يجب أن يكون اسم المجلد Ø§Ù„ÙØ±Ø¹ÙŠ Ø§Ù„Ø£ÙˆÙ„ هو معر٠عنوان اللعبة.</string> | ||
| 89 | <string name="import_saves">استيراد</string> | ||
| 90 | <string name="export_saves">تصدير</string> | ||
| 91 | <string name="install_firmware">تثبيت البرامج الثابتة</string> | ||
| 92 | <string name="firmware_installing">تثبيت البرامج الثابتة</string> | ||
| 93 | <string name="firmware_installed_success">تم تثبيت البرامج الثابتة بنجاØ</string> | ||
| 94 | <string name="firmware_installed_failure">ÙØ´Ù„ تثبيت البرامج الثابتة</string> | ||
| 95 | <string name="share_log">مشاركة سجلات التصØÙŠØ</string> | ||
| 96 | <string name="share_log_description">مشاركة مل٠سجل يوزو لتصØÙŠØ المشكلات</string> | ||
| 97 | <string name="share_log_missing">لم يتم العثور على مل٠السجل</string> | ||
| 98 | <string name="install_game_content">تثبيت Ù…ØØªÙˆÙ‰ اللعبة</string> | ||
| 99 | <string name="install_game_content_description">DLC قم بتثبيت ØªØØ¯ÙŠØ«Ø§Øª اللعبة أو</string> | ||
| 100 | <string name="installing_game_content">جار٠تثبيت Ø§Ù„Ù…ØØªÙˆÙ‰</string> | ||
| 101 | <string name="install_game_content_failure_base">لا ÙŠÙØ³Ù…Ø Ø¨ØªØ«Ø¨ÙŠØª الألعاب الأساسية لتجنب التعارضات Ø§Ù„Ù…ØØªÙ…لة.</string> | ||
| 102 | <string name="install_game_content_success_install">%1$d تم التثبيت بنجاØ</string> | ||
| 103 | <string name="install_game_content_success_overwrite">%1$d تمت الكتابة Ùوقه بنجاØ</string> | ||
| 104 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 105 | <string name="custom_driver_not_supported">برامج التشغيل المخصصة غير مدعومة</string> | ||
| 106 | <string name="custom_driver_not_supported_description">تØÙ…يل برنامج التشغيل المخصص غير معتمد ØØ§Ù„يًا لهذا الجهاز.\nØØ¯Ø¯ هذا الخيار مرة أخرى ÙÙŠ المستقبل Ù„Ù…Ø¹Ø±ÙØ© ما إذا تمت Ø¥Ø¶Ø§ÙØ© الدعم!</string> | ||
| 107 | <string name="manage_yuzu_data">إدارة بيانات يوزو</string> | ||
| 108 | <string name="manage_yuzu_data_description">استيراد/تصدير البرامج الثابتة ÙˆØ§Ù„Ù…ÙØ§ØªÙŠØ وبيانات المستخدم والمزيد!</string> | ||
| 109 | <string name="share_save_file">مشاركة مل٠الØÙظ</string> | ||
| 110 | <string name="export_save_failed">ÙØ´Ù„ تصدير الØÙظ</string> | ||
| 111 | |||
| 112 | <string name="copied_to_clipboard">نسخ إلى Ø§Ù„ØØ§Ùظة</string> | ||
| 113 | <string name="about_app_description">Ù…ØØ§ÙƒÙŠ Ø³ÙˆÙŠØªØ´ Ù…ÙØªÙˆØ المصدر</string> | ||
| 114 | <string name="contributors">المساهمين</string> | ||
| 115 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | ||
| 116 | <string name="licenses_description">المشاريع التي تجعل تطبيق يوزو لنظام أندرويد ممكنًا</string> | ||
| 117 | <string name="build">البناء</string> | ||
| 118 | <string name="user_data">بيانات المستخدم</string> | ||
| 119 | <string name="exporting_user_data">جار٠تصدير بيانات المستخدم</string> | ||
| 120 | <string name="importing_user_data">جار٠استيراد بيانات المستخدم</string> | ||
| 121 | <string name="import_user_data">استيراد بيانات المستخدم</string> | ||
| 122 | <string name="invalid_yuzu_backup">نسخة Ø§ØØªÙŠØ§Ø·ÙŠØ© يوزو غير ØµØ§Ù„ØØ©</string> | ||
| 123 | <string name="user_data_export_success">تم تصدير بيانات المستخدم بنجاØ</string> | ||
| 124 | <string name="user_data_import_success">تم استيراد بيانات المستخدم بنجاØ</string> | ||
| 125 | <string name="user_data_export_cancelled">تم إلغاء التصدير</string> | ||
| 126 | <string name="support_link">https://discord.gg/u77vRWY</string> | ||
| 127 | <string name="website_link">https://yuzu-emu.org/</string> | ||
| 128 | <string name="github_link">https://github.com/yuzu-emu</string> | ||
| 129 | |||
| 130 | <!-- Early access upgrade strings --> | ||
| 131 | <string name="early_access">الوصول المبكر</string> | ||
| 132 | <string name="get_early_access">Ø§ØØµÙ„ على الوصول المبكر</string> | ||
| 133 | <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> | ||
| 134 | <string name="get_early_access_description">الميزات المتطورة، والوصول المبكر إلى Ø§Ù„ØªØØ¯ÙŠØ«Ø§ØªØŒ وأكثر من ذلك</string> | ||
| 135 | <string name="early_access_benefits">مزايا الوصول المبكر</string> | ||
| 136 | <string name="cutting_edge_features">ميزات متطورة</string> | ||
| 137 | <string name="early_access_updates">الوصول المبكر إلى Ø§Ù„ØªØØ¯ÙŠØ«Ø§Øª</string> | ||
| 138 | <string name="no_manual_installation">لا يوجد التثبيت اليدوي</string> | ||
| 139 | <string name="prioritized_support">الدعم ذو الأولوية</string> | ||
| 140 | <string name="helping_game_preservation">المساعدة ÙÙŠ الØÙاظ على اللعبة</string> | ||
| 141 | <string name="our_eternal_gratitude">امتناننا الأبدي</string> | ||
| 142 | <string name="are_you_interested">هل انت مهتم؟</string> | ||
| 143 | |||
| 144 | <!-- General settings strings --> | ||
| 145 | <string name="frame_limit_enable">Ø§Ù„ØØ¯ من السرعة</string> | ||
| 146 | <string name="frame_limit_enable_description">ÙŠØØ¯ من سرعة Ø§Ù„Ù…ØØ§ÙƒØ§Ø© بنسبة Ù…ØØ¯Ø¯Ø© من السرعة العادية</string> | ||
| 147 | <string name="frame_limit_slider">Ø§Ù„ØØ¯ من السرعة ÙÙŠ المئة</string> | ||
| 148 | <string name="frame_limit_slider_description">ÙŠØØ¯Ø¯ النسبة المئوية Ù„Ù„ØØ¯ من سرعة Ø§Ù„Ù…ØØ§ÙƒØ§Ø©. 100% هي السرعة الطبيعية. ستؤدي القيم الأعلى أو الأدنى إلى زيادة أو تقليل ØØ¯ السرعة.</string> | ||
| 149 | <string name="cpu_accuracy">دقة ÙˆØØ¯Ø© المعالجة المركزية</string> | ||
| 150 | <string name="value_with_units">%1$s%2$s</string> | ||
| 151 | |||
| 152 | <!-- System settings strings --> | ||
| 153 | <string name="use_docked_mode">وضع الإرساء</string> | ||
| 154 | <string name="use_docked_mode_description">زيادة الدقة، ÙˆØ§Ù†Ø®ÙØ§Ø¶ الأداء. يتم استخدام الوضع المØÙ…ول عند تعطيله، مما يؤدي إلى Ø®ÙØ¶ الدقة وزيادة الأداء.</string> | ||
| 155 | <string name="emulated_region">المنطقة التي تمت Ù…ØØ§ÙƒØ§ØªÙ‡Ø§</string> | ||
| 156 | <string name="emulated_language">لغة Ø§Ù„Ù…ØØ§ÙƒØ§Ù‡</string> | ||
| 157 | <string name="select_rtc_date">ØØ¯Ø¯ التاريخ Ùˆ الساعة ÙÙŠ الوقت الØÙ‚يقي</string> | ||
| 158 | <string name="select_rtc_time">ØØ¯Ø¯ وقت الساعة ÙÙŠ الوقت Ø§Ù„ÙØ¹Ù„ÙŠ</string> | ||
| 159 | <string name="use_custom_rtc">ساعة مخصصة ÙÙŠ الوقت الØÙ‚يقي</string> | ||
| 160 | <string name="use_custom_rtc_description">ÙŠØ³Ù…Ø Ù„Ùƒ بتعيين ساعة مخصصة ÙÙŠ الوقت Ø§Ù„ÙØ¹Ù„ÙŠ Ù…Ù†ÙØµÙ„Ø© عن وقت النظام Ø§Ù„ØØ§Ù„ÙŠ لديك</string> | ||
| 161 | <string name="set_custom_rtc">تعيين ساعة مخصصة ÙÙŠ الوقت الØÙ‚يقي</string> | ||
| 162 | |||
| 163 | <!-- Graphics settings strings --> | ||
| 164 | <string name="renderer_accuracy">مستوى الدقة</string> | ||
| 165 | <string name="renderer_resolution">(Handheld/Docked) الدقة</string> | ||
| 166 | <string name="renderer_vsync">VSync وضع</string> | ||
| 167 | <string name="renderer_screen_layout">الاتجاه</string> | ||
| 168 | <string name="renderer_aspect_ratio">تناسب الابعاد</string> | ||
| 169 | <string name="renderer_anti_aliasing">طريقة Ù…ÙƒØ§ÙØØ© التعرج</string> | ||
| 170 | <string name="renderer_asynchronous_shaders">استخدم تظليل غير متزامن</string> | ||
| 171 | <string name="renderer_asynchronous_shaders_description">يجمع التظليل بشكل غير متزامن، مما يقلل من التأتأة ولكنه قد يؤدي إلى ØØ¯ÙˆØ« بعض الأخطاء.</string> | ||
| 172 | <string name="renderer_reactive_flushing">استخدم Ø§Ù„ØªÙ†Ø¸ÙŠÙ Ø§Ù„ØªÙØ§Ø¹Ù„ÙŠ</string> | ||
| 173 | <string name="renderer_reactive_flushing_description">ØªØØ³ÙŠÙ† دقة العرض ÙÙŠ بعض الألعاب على ØØ³Ø§Ø¨ الأداء</string> | ||
| 174 | <string name="use_disk_shader_cache_description">يقلل من التأتأة عن طريق تخزين وتØÙ…يل التظليلات التي تم إنشاؤها Ù…ØÙ„يًا.</string> | ||
| 175 | |||
| 176 | <!-- Debug settings strings --> | ||
| 177 | <string name="cpu">ÙˆØØ¯Ø© المعالج المركزية</string> | ||
| 178 | <string name="cpu_debug_mode">تصØÙŠØ أخطاء ÙˆØØ¯Ø© المعالجة المركزية</string> | ||
| 179 | <string name="cpu_debug_mode_description">يضع ÙˆØØ¯Ø© المعالجة المركزية ÙÙŠ وضع التصØÙŠØ البطيء.</string> | ||
| 180 | <string name="gpu">GPU</string> | ||
| 181 | <string name="renderer_api">API</string> | ||
| 182 | <string name="renderer_debug">تصØÙŠØ الأخطاء الرسومية</string> | ||
| 183 | <string name="renderer_debug_description">يضبط واجهة برمجة تطبيقات الرسومات على وضع تصØÙŠØ الأخطاء البطيء.</string> | ||
| 184 | <string name="fastmem">Fastmem</string> | ||
| 185 | |||
| 186 | <!-- Audio settings strings --> | ||
| 187 | <string name="audio_output_engine">Ù…ØØ±Ùƒ الإخراج</string> | ||
| 188 | <string name="audio_volume">ØØ¬Ù…</string> | ||
| 189 | <string name="audio_volume_description">ÙŠØØ¯Ø¯ ØØ¬Ù… إخراج الصوت</string> | ||
| 190 | |||
| 191 | <!-- Miscellaneous --> | ||
| 192 | <string name="slider_default">Ø§ÙØªØ±Ø§Ø¶ÙŠ</string> | ||
| 193 | <string name="ini_saved">الإعدادات المØÙوظة</string> | ||
| 194 | <string name="gameid_saved">الإعدادات المØÙوظة لـ %1$s</string> | ||
| 195 | <string name="unimplemented_menu">القائمة غير Ø§Ù„Ù…Ù†ÙØ°Ø©</string> | ||
| 196 | <string name="loading">جاري تØÙ…يل</string> | ||
| 197 | <string name="shutting_down">إيقا٠تشغيل</string> | ||
| 198 | <string name="reset_setting_confirmation">هل تريد إعادة تعيين هذا الإعداد مرة أخرى إلى قيمته Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠØ©ØŸ</string> | ||
| 199 | <string name="reset_to_default">إعادة تعيين إلى Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠ</string> | ||
| 200 | <string name="reset_all_settings">إعادة تعيين جميع الإعدادات؟</string> | ||
| 201 | <string name="reset_all_settings_description">سيتم إعادة تعيين ÙƒØ§ÙØ© الإعدادات المتقدمة إلى تكوينها Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠ. هذا لا يمكن التراجع عنها.</string> | ||
| 202 | <string name="settings_reset">إعادة تعيين الأعدادات</string> | ||
| 203 | <string name="close">إغلاق</string> | ||
| 204 | <string name="learn_more">Ù…Ø¹Ø±ÙØ© المزيد</string> | ||
| 205 | <string name="auto">تلقائي</string> | ||
| 206 | <string name="submit">إرسال</string> | ||
| 207 | <string name="string_null">قيمه خاليه</string> | ||
| 208 | <string name="string_import">استيراد</string> | ||
| 209 | <string name="export">تصدير</string> | ||
| 210 | <string name="export_failed">ÙØ´Ù„ التصدير</string> | ||
| 211 | <string name="import_failed">ÙØ´Ù„ الاستيراد</string> | ||
| 212 | <string name="cancelling">إلغاء</string> | ||
| 213 | |||
| 214 | <!-- GPU driver installation --> | ||
| 215 | <string name="select_gpu_driver">GPU ØØ¯Ø¯ برنامج تشغيل</string> | ||
| 216 | <string name="select_gpu_driver_title">Ø§Ù„ØØ§Ù„ÙŠ الخاص بك؟ GPU هل ترغب ÙÙŠ استبدال برنامج تشغيل</string> | ||
| 217 | <string name="select_gpu_driver_install">تثبيت</string> | ||
| 218 | <string name="select_gpu_driver_default">Ø§ÙØªØ±Ø§Ø¶ÙŠ</string> | ||
| 219 | <string name="select_gpu_driver_use_default">يستخدم تعري٠معالج الرسوميات Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠ</string> | ||
| 220 | <string name="select_gpu_driver_error">تم ØªØØ¯ÙŠØ¯ برنامج تشغيل غير ØµØ§Ù„Ø ØŒ باستخدام النظام Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠ</string> | ||
| 221 | <string name="system_gpu_driver">تعري٠معالج الرسوميات الخاص بالنظام</string> | ||
| 222 | <string name="installing_driver">جار٠تثبيت برنامج التشغيل…</string> | ||
| 223 | |||
| 224 | <!-- Preferences Screen --> | ||
| 225 | <string name="preferences_settings">إعدادات</string> | ||
| 226 | <string name="preferences_general">عام</string> | ||
| 227 | <string name="preferences_system">النظام</string> | ||
| 228 | <string name="preferences_graphics">الرسوميات</string> | ||
| 229 | <string name="preferences_audio">الصوت</string> | ||
| 230 | <string name="preferences_theme">السمة واللون</string> | ||
| 231 | <string name="preferences_debug">تصØÙŠØ الأخطاء</string> | ||
| 232 | |||
| 233 | <!-- ROM loading errors --> | ||
| 234 | <string name="loader_error_encrypted">الخاص بك ROM تم تشÙير</string> | ||
| 235 | <string name="loader_error_video_core">ØØ¯Ø« خطأ أثناء تهيئة مركز الÙيديو</string> | ||
| 236 | <string name="loader_error_invalid_format">ROM غير قادر على تØÙ…يل</string> | ||
| 237 | <string name="loader_error_file_not_found">غير موجود ROM ملÙ</string> | ||
| 238 | |||
| 239 | <!-- Emulation Menu --> | ||
| 240 | <string name="emulation_exit">الخروج من Ø§Ù„Ù…ØØ§ÙƒØ§Ø©</string> | ||
| 241 | <string name="emulation_done">منجز</string> | ||
| 242 | <string name="emulation_fps_counter">عداد إطار/ثانية</string> | ||
| 243 | <string name="emulation_toggle_controls">تبديل عناصر التØÙƒÙ…</string> | ||
| 244 | <string name="emulation_rel_stick_center">مركز العصا النسبي</string> | ||
| 245 | <string name="emulation_dpad_slide">مزلاق أزرار الاتجاهات</string> | ||
| 246 | <string name="emulation_haptics">الاهتزازات الديناميكية</string> | ||
| 247 | <string name="emulation_show_overlay">عرض التراكب</string> | ||
| 248 | <string name="emulation_toggle_all">تبديل الكل</string> | ||
| 249 | <string name="emulation_control_adjust">ضبط التراكب</string> | ||
| 250 | <string name="emulation_control_scale">ØØ¬Ù…</string> | ||
| 251 | <string name="emulation_control_opacity">العتامه</string> | ||
| 252 | <string name="emulation_touch_overlay_reset">إعادة تعيين التراكب</string> | ||
| 253 | <string name="emulation_touch_overlay_edit">ØªØØ±ÙŠØ± التراكب</string> | ||
| 254 | <string name="emulation_pause">Ø¥ÙŠÙ‚Ø§Ù Ø§Ù„Ù…ØØ§ÙƒØ§Ø© مؤقتًا</string> | ||
| 255 | <string name="emulation_unpause">إلغاء الإيقا٠المؤقت للمضاهاة</string> | ||
| 256 | <string name="emulation_input_overlay">خيارات التراكب</string> | ||
| 257 | |||
| 258 | <string name="load_settings">جار٠تØÙ…يل الإعدادات</string> | ||
| 259 | |||
| 260 | <!-- Software keyboard --> | ||
| 261 | <string name="software_keyboard">Ù„ÙˆØØ© Ø§Ù„Ù…ÙØ§ØªÙŠØ البرمجية</string> | ||
| 262 | |||
| 263 | <!-- Errors and warnings --> | ||
| 264 | <string name="abort_button">إلغاء</string> | ||
| 265 | <string name="continue_button">استمر</string> | ||
| 266 | <string name="system_archive_not_found">لم يتم العثور على أرشي٠النظام</string> | ||
| 267 | <string name="system_archive_general">أرشي٠النظام</string> | ||
| 268 | <string name="save_load_error">خطأ ÙÙŠ الØÙظ/التØÙ…يل</string> | ||
| 269 | <string name="fatal_error">خطا ÙØ§Ø¯Ø</string> | ||
| 270 | <string name="performance_warning">سيؤدي إيقا٠تشغيل هذا الإعداد إلى تقليل أداء Ø§Ù„Ù…ØØ§ÙƒØ§Ø© بشكل ملØÙˆØ¸! Ù„Ù„ØØµÙˆÙ„ على Ø£ÙØ¶Ù„ تجربة، يوصى بترك هذا الإعداد ممكنًا.</string> | ||
| 271 | <string name="memory_formatted">%1$s %2$s</string> | ||
| 272 | <string name="no_game_present">لا توجد لعبة قابلة للتمهيد</string> | ||
| 273 | |||
| 274 | <!-- Region Names --> | ||
| 275 | <string name="region_japan">اليابان</string> | ||
| 276 | <string name="region_usa">الولايات Ø§Ù„Ù…ØªØØ¯Ø© الأمريكية</string> | ||
| 277 | <string name="region_europe">أوروبا</string> | ||
| 278 | <string name="region_australia">أستراليا</string> | ||
| 279 | <string name="region_china">الصين</string> | ||
| 280 | <string name="region_korea">كوريا</string> | ||
| 281 | <string name="region_taiwan">تايوان</string> | ||
| 282 | |||
| 283 | <!-- Memory Sizes --> | ||
| 284 | <string name="memory_byte">Byte</string> | ||
| 285 | <string name="memory_kilobyte">KB</string> | ||
| 286 | <string name="memory_megabyte">MB</string> | ||
| 287 | <string name="memory_gigabyte">GB</string> | ||
| 288 | <string name="memory_terabyte">TB</string> | ||
| 289 | <string name="memory_petabyte">PB</string> | ||
| 290 | <string name="memory_exabyte">EB</string> | ||
| 291 | |||
| 292 | <!-- Renderer APIs --> | ||
| 293 | <string name="renderer_vulkan">Vulkan</string> | ||
| 294 | <string name="renderer_none">لاشيء</string> | ||
| 295 | |||
| 296 | <!-- Renderer Accuracy --> | ||
| 297 | <string name="renderer_accuracy_normal">عادي</string> | ||
| 298 | <string name="renderer_accuracy_high">عالي</string> | ||
| 299 | <string name="renderer_accuracy_extreme">Extreme (بطيء)</string> | ||
| 300 | |||
| 301 | <!-- Resolutions --> | ||
| 302 | <string name="resolution_half">0.5X (360p/540p)</string> | ||
| 303 | <string name="resolution_three_quarter">0.75X (540p/810p)</string> | ||
| 304 | <string name="resolution_one">1X (720p/1080p)</string> | ||
| 305 | <string name="resolution_two">2X (1440p/2160p) (بطيء)</string> | ||
| 306 | <string name="resolution_three">3X (2160p/3240p) (بطيء)</string> | ||
| 307 | <string name="resolution_four">4X (2880p/4320p) (بطيء)</string> | ||
| 308 | |||
| 309 | <!-- Renderer VSync --> | ||
| 310 | <string name="renderer_vsync_immediate">Immediate (Off)</string> | ||
| 311 | <string name="renderer_vsync_mailbox">Mailbox</string> | ||
| 312 | <string name="renderer_vsync_fifo">FIFO (On)</string> | ||
| 313 | <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string> | ||
| 314 | |||
| 315 | <!-- Scaling Filters --> | ||
| 316 | <string name="scaling_filter_nearest_neighbor">Nearest Neighbor</string> | ||
| 317 | <string name="scaling_filter_bilinear">Bilinear</string> | ||
| 318 | <string name="scaling_filter_bicubic">Bicubic</string> | ||
| 319 | <string name="scaling_filter_gaussian">Gaussian</string> | ||
| 320 | <string name="scaling_filter_scale_force">ScaleForce</string> | ||
| 321 | <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ Super Resolution</string> | ||
| 322 | |||
| 323 | <!-- Anti-Aliasing --> | ||
| 324 | <string name="anti_aliasing_none">لا شيء</string> | ||
| 325 | <string name="anti_aliasing_fxaa">FXAA</string> | ||
| 326 | <string name="anti_aliasing_smaa">SMAA</string> | ||
| 327 | |||
| 328 | <!-- Screen Layouts --> | ||
| 329 | <string name="screen_layout_landscape">اÙقي</string> | ||
| 330 | <string name="screen_layout_portrait">عمودي</string> | ||
| 331 | <string name="screen_layout_auto">تلقائي</string> | ||
| 332 | |||
| 333 | <!-- Aspect Ratios --> | ||
| 334 | <string name="ratio_default">(16:9) Ø§ÙØªØ±Ø§Ø¶ÙŠ</string> | ||
| 335 | <string name="ratio_force_four_three">4:3 ÙØ±Ø¶</string> | ||
| 336 | <string name="ratio_force_twenty_one_nine">21:9 ÙØ±Ø¶</string> | ||
| 337 | <string name="ratio_force_sixteen_ten">16:10 ÙØ±Ø¶</string> | ||
| 338 | <string name="ratio_stretch">تمتد إلى Ø§Ù„Ù†Ø§ÙØ°Ø©</string> | ||
| 339 | |||
| 340 | <!-- CPU Accuracy --> | ||
| 341 | <string name="cpu_accuracy_accurate">دقه</string> | ||
| 342 | <string name="cpu_accuracy_unsafe">غير آمن</string> | ||
| 343 | <string name="cpu_accuracy_paranoid">Paranoid (Slow)</string> | ||
| 344 | |||
| 345 | <!-- Gamepad Buttons --> | ||
| 346 | <string name="gamepad_d_pad">أزرار الاتجاهات</string> | ||
| 347 | <string name="gamepad_left_stick">العصا اليسرى</string> | ||
| 348 | <string name="gamepad_right_stick">العصا اليمنى</string> | ||
| 349 | <string name="gamepad_home">شاشة الإستقبال</string> | ||
| 350 | <string name="gamepad_screenshot">لقطة شاشة</string> | ||
| 351 | |||
| 352 | <!-- Disk shader cache --> | ||
| 353 | <string name="preparing_shaders">ØªØØ¶ÙŠØ± التظليل</string> | ||
| 354 | <string name="building_shaders">بناء التظليل</string> | ||
| 355 | |||
| 356 | <!-- Theme options --> | ||
| 357 | <string name="change_app_theme">تغيير سمة التطبيق</string> | ||
| 358 | <string name="theme_default">Ø§ÙØªØ±Ø§Ø¶ÙŠ</string> | ||
| 359 | <string name="theme_material_you">Material You</string> | ||
| 360 | |||
| 361 | <!-- Theme Modes --> | ||
| 362 | <string name="change_theme_mode">تغيير وضع السمة</string> | ||
| 363 | <string name="theme_mode_follow_system">اتبع النظام</string> | ||
| 364 | <string name="theme_mode_light">ÙØ§ØªØ</string> | ||
| 365 | <string name="theme_mode_dark">غامق</string> | ||
| 366 | |||
| 367 | <!-- Audio output engines --> | ||
| 368 | <string name="cubeb">cubeb</string> | ||
| 369 | |||
| 370 | <!-- Black backgrounds theme --> | ||
| 371 | <string name="use_black_backgrounds">خلÙيات سوداء</string> | ||
| 372 | <string name="use_black_backgrounds_description">عند استخدام المظهر الداكن، قم بتطبيق خلÙيات سوداء.</string> | ||
| 373 | |||
| 374 | <!-- Picture-In-Picture --> | ||
| 375 | <string name="picture_in_picture">صورة داخل صورة</string> | ||
| 376 | <string name="picture_in_picture_description">تصغير Ø§Ù„Ù†Ø§ÙØ°Ø© عند وضعها ÙÙŠ الخلÙية</string> | ||
| 377 | <string name="pause">توقÙ</string> | ||
| 378 | <string name="play">تشغيل</string> | ||
| 379 | <string name="mute">كتم</string> | ||
| 380 | <string name="unmute">إلغاء الكتم</string> | ||
| 381 | |||
| 382 | <!-- Licenses screen strings --> | ||
| 383 | <string name="licenses">التراخيص</string> | ||
| 384 | <string name="license_fidelityfx_fsr_description">AMD ترقية عالية الجودة من</string> | ||
| 385 | </resources> | ||
diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml new file mode 100644 index 000000000..d2e5fee19 --- /dev/null +++ b/src/android/app/src/main/res/values-ckb/strings.xml | |||
| @@ -0,0 +1,336 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | ||
| 3 | |||
| 4 | <string name="app_disclaimer">ئەم نەرمەکاڵایە یارییەکانی کۆنسۆلی نینتێندۆ سویچ کارپێدەکات. هیچ ناونیشانێکی یاری Ùˆ کلیلی تێدا نییە..<br /><br />Ù¾ÛŽØ´ ئەوەی دەست Ù¾ÛŽ بکەیت، تکایە شوێنی ÙØ§ÛŒÙ„ÛŒ <![CDATA[<b> prod.keys </b>]]> دیاریبکە Ù„Û• Ù†ÛŽÙˆ کۆگای ئامێرەکەت.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">زیاتر Ùێربە</a>]]></string> | ||
| 5 | <string name="emulation_notification_channel_name">ئیمولەیشن کارایە</string> | ||
| 6 | <string name="emulation_notification_channel_description">ئاگادارکردنەوەیەکی بەردەوام نیشان دەدات کاتێک ئیمولەیشن کاردەکات.</string> | ||
| 7 | <string name="emulation_notification_running">یوزو کاردەکات</string> | ||
| 8 | <string name="notice_notification_channel_name">ئاگاداری و هەڵەکان</string> | ||
| 9 | <string name="notice_notification_channel_description">ئاگادارکردنەوەکان پیشان دەدات کاتێک شتێک بە هەڵەدا دەچێت.</string> | ||
| 10 | <string name="notification_permission_not_granted">مۆڵەتی ئاگادارکردنەوە نەدراوە!</string> | ||
| 11 | |||
| 12 | <!-- Setup strings --> | ||
| 13 | <string name="welcome">بەخێربێیت!</string> | ||
| 14 | <string name="welcome_description">Ùێربە Ú†Û†Ù† <b>yuzu</b> ڕێکبخەیت Ùˆ بچییە ناو ئیمولەیشن.</string> | ||
| 15 | <string name="get_started">دەست پێبکە</string> | ||
| 16 | <string name="keys">کلیلەکان</string> | ||
| 17 | <string name="keys_description">ÙØ§ÛŒÙ„ÛŒ <b>prod.keys</b> هەڵبژێرە بە دوگمەی خوارەوە.</string> | ||
| 18 | <string name="select_keys">کلیلەکان هەڵبژێرە</string> | ||
| 19 | <string name="games">یاریەکان</string> | ||
| 20 | <string name="games_description">Ùۆڵدەری <b>Games</b> هەڵبژێرە بە دوگمەی خوارەوە.</string> | ||
| 21 | <string name="done">تەواو</string> | ||
| 22 | <string name="done_description">تۆ تەواو ئامادەیت.\nچێژ لە یارییەکانت وەربگرە!</string> | ||
| 23 | <string name="text_continue">بەردەوام بوون</string> | ||
| 24 | <string name="next">دواتر</string> | ||
| 25 | <string name="back">گەڕانەوە</string> | ||
| 26 | <string name="add_games">زیادکردنی یاری</string> | ||
| 27 | <string name="add_games_description">Ùۆڵدەری یارییەکانت هەڵبژێرە</string> | ||
| 28 | <!-- Home strings --> | ||
| 29 | <string name="home_games">یاریەکان</string> | ||
| 30 | <string name="home_search">گەڕان</string> | ||
| 31 | <string name="home_settings">ڕێکخستنەکان</string> | ||
| 32 | <string name="empty_gamelist">تا ئێستا هیچ ÙØ§ÛŒÙ„ÛŽÚ© نەدۆزراوەتەوە یان هیچ ناونیشانێکی یاری هەڵنەبژێردراوە.</string> | ||
| 33 | <string name="search_and_filter_games">گەڕان Ùˆ Ùلتەرکردنی یارییەکان</string> | ||
| 34 | <string name="select_games_folder">Ùۆڵدەری یارییەکان هەڵبژێرە</string> | ||
| 35 | <string name="select_games_folder_description">ڕێگە بە یوزو دەدات بۆ پڕکردنەوەی لیستی یارییەکان</string> | ||
| 36 | <string name="add_games_warning">هەڵبژاردنی Ùۆڵدەری یارییەکان تێپەڕدەکەیت؟</string> | ||
| 37 | <string name="add_games_warning_description">یارییەکان Ù„Û• لیستی یارییەکاندا پیشان نادرێن ئەگەر Ùۆڵدەرێک هەڵنەبژێردرێت.</string> | ||
| 38 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | ||
| 39 | <string name="home_search_games">گەڕان بەدوای یارییەکاندا</string> | ||
| 40 | <string name="games_dir_selected">ناونیشانی یارییەکان هەڵبژێردرا</string> | ||
| 41 | <string name="install_prod_keys">دابمەزرێنە prod.keys</string> | ||
| 42 | <string name="install_prod_keys_description">پێویستە بۆ کۆدکردنەوەى یارییە تاکەکەسییەکان</string> | ||
| 43 | <string name="install_prod_keys_warning">زیادکردنی کلیلەکان تێپەڕدەکەیت؟</string> | ||
| 44 | <string name="install_prod_keys_warning_description">کلیلی دروست پێویستە بۆ وەرگرتنی یارییەکانی تاکەکەسی. تەنها ئەپەکانی homebrew کاردەکەن ئەگەر بەردەوام بیت.</string> | ||
| 45 | <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> | ||
| 46 | <string name="notifications">ئاگادارکردنەوەکان</string> | ||
| 47 | <string name="notifications_description">بە دوگمەی خوارەوە مۆڵەتی ئاگادارکردنەوەکە بدە.</string> | ||
| 48 | <string name="give_permission">مۆڵەت بدە</string> | ||
| 49 | <string name="notification_warning">پێدانی مۆڵەتی ئاگادارکردنەوە تێپەڕدەکەیت؟</string> | ||
| 50 | <string name="notification_warning_description">یوزو ناتوانێت لە زانیاری گرنگ ئاگادارت بکاتەوە.</string> | ||
| 51 | <string name="permission_denied">مۆڵەت پێدان ڕەتکرایەوە</string> | ||
| 52 | <string name="permission_denied_description">زۆر جار ئەم مۆڵەتەت ڕەتکردۆتەوە و ئێستا دەبێت بە دەستی ڕێگەپێدان بکەیت لە ڕێکخستنەکانی سیستەمدا.</string> | ||
| 53 | <string name="about">دەربارە</string> | ||
| 54 | <string name="about_description">وەشانی دروستکردن، بیتبێن و زۆر شتیتر</string> | ||
| 55 | <string name="warning_help">یارمەتی</string> | ||
| 56 | <string name="warning_skip">پەڕاندن</string> | ||
| 57 | <string name="warning_cancel">ڕەتکردنەوە</string> | ||
| 58 | <string name="install_amiibo_keys">دامەزراندنی کلیلی Amiibo</string> | ||
| 59 | <string name="install_amiibo_keys_description">پێویستە بۆ بەکارهێنانی Amiibo لە یاریدا</string> | ||
| 60 | <string name="invalid_keys_file">ÙØ§ÛŒÙ„ÛŒ کلیلێکی نادروست هەڵبژێردرا</string> | ||
| 61 | <string name="install_keys_success">کلیلەکان بە سەرکەوتوویی دامەزران</string> | ||
| 62 | <string name="reading_keys_failure">هەڵە لە خوێندنەوەی کۆدکردنی کلیل</string> | ||
| 63 | <string name="install_prod_keys_failure_extension_description">دڵنیابەوە Ú©Û• ÙØ§ÛŒÙ„ÛŒ کلیلەکانت درێژکراوەی .keys ÛŒ هەیە Ùˆ دووبارە هەوڵبدەرەوە.</string> | ||
| 64 | <string name="install_amiibo_keys_failure_extension_description">دڵنیابە Ú©Û• ÙØ§ÛŒÙ„ÛŒ کلیلەکانت درێژکراوەی .bin ÛŒ هەیە Ùˆ دووبارە هەوڵبدەرەوە.</string> | ||
| 65 | <string name="invalid_keys_error">کلیلی کۆدکردنی نادروستە</string> | ||
| 66 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | ||
| 67 | <string name="install_keys_failure_description">ÙØ§ÛŒÙ„Û• هەڵبژێردراوەکە هەڵەیە یان تێکچووە. تکایە دووبارە کلیلەکانت دەربێنەوە.</string> | ||
| 68 | <string name="install_gpu_driver">دامەزراندنی وەگەڕخەری GPU</string> | ||
| 69 | <string name="install_gpu_driver_description">دامەزراندنی وەگەڕخەری بەدیل بۆ ئەوەی بە ئەگەرێکی زۆرەوە کارایی باشتر یان وردبینی هەبێت</string> | ||
| 70 | <string name="advanced_settings">ڕێکخستنە پێشکەوتووەکان</string> | ||
| 71 | <string name="settings_description">سازدانی ڕێکخستنەکانی ئیمولەیتەر</string> | ||
| 72 | <string name="search_recently_played">بەم دواییە یاری کردووە</string> | ||
| 73 | <string name="search_recently_added">بەم دواییە زیادکرا</string> | ||
| 74 | <string name="search_retail">بەتاک</string> | ||
| 75 | <string name="search_homebrew">هۆم بریو</string> | ||
| 76 | <string name="open_user_folder">کردنەوەی Ùۆڵدەری یوزو</string> | ||
| 77 | <string name="open_user_folder_description">بەڕێوەبردنی ÙØ§ÛŒÙ„Û• ناوخۆییەکانی یوزو</string> | ||
| 78 | <string name="theme_and_color_description">دەستکاری کردنی شێوازی ئەپەکە</string> | ||
| 79 | <string name="no_file_manager">هیچ ÙØ§ÛŒÙ„ بەڕێوەبەرێک نەدۆزرایەوە</string> | ||
| 80 | <string name="notification_no_directory_link">نەتوانرا ناونیشانی یوزو بکرێتەوە</string> | ||
| 81 | <string name="notification_no_directory_link_description">تکایە شوێنی Ùۆڵدەری بەکارهێنەر Ù„Û•Ú¯Û•Úµ پانێڵی لایەنی ÙØ§ÛŒÙ„ بەڕێوەبارەکان بە دەست بدۆزەرەوە.</string> | ||
| 82 | <string name="manage_save_data">بەڕێوەبردنی داتای پاشەکەوتکراو</string> | ||
| 83 | <string name="manage_save_data_description">داتای پاشەکەوتکراو دۆزراوە. تکایە لە خوارەوە بژاردەیەک هەڵبژێرە.</string> | ||
| 84 | <string name="import_export_saves_description">هاوردەکردن یان هەناردەکردنی ÙØ§ÛŒÙ„ÛŒ پاشەکەوتکراو</string> | ||
| 85 | <string name="save_file_imported_success">بە سەرکەوتوویی هاوردە کرا</string> | ||
| 86 | <string name="save_file_invalid_zip_structure">پێکهاتەی شوێنی پاشەکەوتکراو نادروستە</string> | ||
| 87 | <string name="save_file_invalid_zip_structure_description">ناوی یەکەمی Ùۆڵدەر دەبێت ناسنامەی ناونیشانی یارییەکە بێت.</string> | ||
| 88 | <string name="import_saves">هاوردەکردن</string> | ||
| 89 | <string name="export_saves">هەناردەکردن</string> | ||
| 90 | <string name="install_firmware">دامەزراندنی پتەوواڵا</string> | ||
| 91 | <string name="install_firmware_description">پتەوواڵا دەبێت Ù„Û• ئەرشیÙÛŒ زیپدا بێت Ùˆ پێویستە بۆ بووتکردنی هەندێک یاری</string> | ||
| 92 | <string name="firmware_installing">دامەزرانی پتەوواڵا</string> | ||
| 93 | <string name="firmware_installed_success">پتەوواڵا بە سەرکەوتوویی دامەزرا</string> | ||
| 94 | <string name="firmware_installed_failure">دامەزراندنی پتەوواڵا شکستی هێنا</string> | ||
| 95 | <string name="share_log">هاوبەشی پێکردنی لۆگەکانی چاککردنەوە</string> | ||
| 96 | <string name="share_log_description">ÙØ§ÛŒÙ„Û• Ù„Û†Ú¯Û•Ú©Û•ÛŒ یوزو هاوبەش بکە بۆ چاککردنی کێشەکان</string> | ||
| 97 | <string name="share_log_missing">هیچ ÙØ§ÛŒÙ„ÛŽÚ©ÛŒ Ù„Û†Ú¯ نەدۆزراوە</string> | ||
| 98 | <string name="install_game_content">دامەزراندنی ناوەڕۆکی یاری</string> | ||
| 99 | <string name="install_game_content_description">دامەزراندنی نوێکاری یارییەکان یان DLC</string> | ||
| 100 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 101 | <!-- About screen strings --> | ||
| 102 | <string name="gaia_is_not_real">گایا ڕاستەقینە نییە</string> | ||
| 103 | <string name="copied_to_clipboard">کۆپی کرا بۆ تەختەی نووسین</string> | ||
| 104 | <string name="about_app_description">ئیمۆلیتەرێکی سەرچاوە-کراوەی سویچ</string> | ||
| 105 | <string name="contributors">بەشداربووان</string> | ||
| 106 | <string name="contributors_description">دروستکراوە لەگەڵ \u2764 لەلایەن تیمەکەی یوزو</string> | ||
| 107 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | ||
| 108 | <string name="licenses_description">ئەو پڕۆژانەی کە یوزوی بۆ ئەندرۆید ڕەخساند</string> | ||
| 109 | <string name="build">بونیات</string> | ||
| 110 | <string name="support_link">https://discord.gg/u77vRWY</string> | ||
| 111 | <string name="website_link">https://yuzu-emu.org/</string> | ||
| 112 | <string name="github_link">https://github.com/yuzu-emu</string> | ||
| 113 | |||
| 114 | <!-- Early access upgrade strings --> | ||
| 115 | <string name="early_access">بەزوویی دەسپێگەشتن</string> | ||
| 116 | <string name="get_early_access">بەدەستهێنانی بەزوویی دەسپێگەشتن</string> | ||
| 117 | <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> | ||
| 118 | <string name="get_early_access_description">تایبەتمەندییە پێشکەوتووەکان، بەزوویی دەستگەیشتن بە نوێکارییەکان و زۆر شتی تر</string> | ||
| 119 | <string name="early_access_benefits">سوودەکانی بەزوویی دەسپێگەشتن</string> | ||
| 120 | <string name="cutting_edge_features">تایبەتمەندییە پێشکەوتووەکان</string> | ||
| 121 | <string name="early_access_updates">زوو دەستگەیشتن بە نوێکارییەکان</string> | ||
| 122 | <string name="no_manual_installation">چیتر دامەزراندنی دەستی نییە</string> | ||
| 123 | <string name="prioritized_support">پشتگیری لە پێشینە</string> | ||
| 124 | <string name="helping_game_preservation">یارمەتیدانی پاراستنی یارییەکان</string> | ||
| 125 | <string name="our_eternal_gratitude">سوپاس و پێزانینی هەمیشەییمان</string> | ||
| 126 | <string name="are_you_interested">ئایا تۆ خوازیاریت؟</string> | ||
| 127 | |||
| 128 | <!-- General settings strings --> | ||
| 129 | <string name="frame_limit_enable">سنووردارکردنی خێرایی</string> | ||
| 130 | <string name="frame_limit_enable_description">خێرایی ئیمولەیشن سنووردار دەکات بۆ ڕێژەیەکی دیاریکراو لە خێرایی ئاسایی.</string> | ||
| 131 | <string name="frame_limit_slider">سنووردارکردنی لەسەدای خێرایی</string> | ||
| 132 | <string name="frame_limit_slider_description">ڕێژەی سەدی دیاری دەکات بۆ سنووردارکردنی خێرایی ئیمولەیشن. 100% خێرایی ئاساییە. بەهایی بەرزتر یان نزمتر دەبێتە هۆی زیاد یان کەمکردنەوەی سنووری خێرایی.</string> | ||
| 133 | <string name="cpu_accuracy">وردی CPU</string> | ||
| 134 | <!-- System settings strings --> | ||
| 135 | <string name="use_docked_mode">دۆخی دۆککراو</string> | ||
| 136 | <string name="use_docked_mode_description">ڕوونی زیاد دەکات، کارایی کەم دەکاتەوە. دۆخی دەستی بەکاردێت کاتێک لەکاردەخرێت، ئەمەش ڕوونی دادەبەزێنێت و کارایی زیاد دەکات.</string> | ||
| 137 | <string name="emulated_region">ناوچەی ئیمولەیشن</string> | ||
| 138 | <string name="emulated_language">زمانی ئیمولەیتەر</string> | ||
| 139 | <string name="select_rtc_date">هەڵبژاردنی بەرواری RTC</string> | ||
| 140 | <string name="select_rtc_time">هەڵبژاردنی کاتی RTC</string> | ||
| 141 | <string name="use_custom_rtc">RTCی تایبەتمەند</string> | ||
| 142 | <string name="use_custom_rtc_description">ڕێگەت پێدەدات کاتژمێرێکی کاتی ڕاستەقینەی تایبەتمەند دابنێیت کە جیاوازە لە کاتی ئێستای سیستەمەکەت.</string> | ||
| 143 | <string name="set_custom_rtc">دانانی RTCی تایبەتمەند</string> | ||
| 144 | |||
| 145 | <!-- Graphics settings strings --> | ||
| 146 | <string name="renderer_accuracy">ئاستی وردبینی</string> | ||
| 147 | <string name="renderer_resolution">ڕوونی (دۆخی دەستی/دۆخی دۆک)</string> | ||
| 148 | <string name="renderer_vsync">دۆخی VSync</string> | ||
| 149 | <string name="renderer_aspect_ratio">ڕێژەی ڕووبەری شاشە</string> | ||
| 150 | <string name="renderer_scaling_filter">Ùلتەری گونجاندنی پەنجەرە</string> | ||
| 151 | <string name="renderer_anti_aliasing">شێوازی دژە-خاوڕۆیی</string> | ||
| 152 | <string name="renderer_force_max_clock">ناچاریکردن بۆ زۆرترین کاتژمێر (تەنها ئەدرینۆ)</string> | ||
| 153 | <string name="renderer_force_max_clock_description">GPU ناچار دەکات بە زۆرترین کاتژمێر کاربکات (هێشتا سنووردارکردنی گەرمی جێبەجێ دەکرێت).</string> | ||
| 154 | <string name="renderer_asynchronous_shaders">بەکارهێنانی سێبەری ناهاوسەنگ</string> | ||
| 155 | <string name="renderer_asynchronous_shaders_description">سێبەرەکان بە شێوەیەکی ناهاوسەنگ کۆدەکاتەوە، پچڕپچڕی کەمدەکاتەوە بەڵام لەوانەیە گلێچ دروستکا.</string> | ||
| 156 | <string name="renderer_reactive_flushing">بەکارهێنانی بەرپێچدەرەوە</string> | ||
| 157 | <string name="renderer_reactive_flushing_description">وردی ڕێندەرکردن لە هەندێک یاریدا باشتر دەکات لەسەر تێچووی کارایی.</string> | ||
| 158 | <string name="use_disk_shader_cache">بیرگەخێرای سێبەری دیسک</string> | ||
| 159 | <string name="use_disk_shader_cache_description">پچڕپچڕی کەمدەکاتەوە بە هەڵگرتن و بارکردنی سێبەری دروستکراو لە ناوخۆدا.</string> | ||
| 160 | |||
| 161 | <!-- Debug settings strings --> | ||
| 162 | <string name="cpu">CPU</string> | ||
| 163 | <string name="renderer_api">API گراÙیک</string> | ||
| 164 | <string name="renderer_debug">چاککردنەوەی گراÙیک</string> | ||
| 165 | <string name="renderer_debug_description">API ÛŒ گراÙیکەکان ڕێکدەخات بۆ دۆخی چاککردنی خاو.</string> | ||
| 166 | <string name="audio_volume">قەبارەی دەنگی</string> | ||
| 167 | <string name="audio_volume_description">دیاریکردنی قەبارەی دەنگی دەرچووی بیستۆک و بزوێنەری دەنگی دەرەکی.</string> | ||
| 168 | |||
| 169 | <!-- Miscellaneous --> | ||
| 170 | <string name="slider_default">بنەڕەت</string> | ||
| 171 | <string name="ini_saved">ڕێکخستنە پاشەکەوتکراوەکان</string> | ||
| 172 | <string name="gameid_saved">ڕێکخستنە پاشەکەوتکراوەکان بۆ %1$s</string> | ||
| 173 | <string name="error_saving">هەڵە لە پاشەکەوتکردن %1$s.ini: %2$s</string> | ||
| 174 | <string name="loading">بارکردن...</string> | ||
| 175 | <string name="reset_setting_confirmation">ئایا دەتەوێت ئەم ڕێکخستنە بگەڕێنیتەوە بۆ بەهای بنەڕەتی خۆی؟</string> | ||
| 176 | <string name="reset_to_default">دوبارە ڕێکخستنەوەی بۆ بنەڕەت</string> | ||
| 177 | <string name="reset_all_settings">هەموو ڕێکخستنەکان دوبارە ڕێک دەخاتەوە؟</string> | ||
| 178 | <string name="reset_all_settings_description">هەموو ڕێکخستنە پێشکەوتووەکان دەگەڕێنەوە بۆ ڕێکخستنی بنەڕەتی خۆیان. پاشگەز بوونەوەی نییه.</string> | ||
| 179 | <string name="settings_reset">دوبارە ڕێککردنەوەی ڕێکخستنەکان</string> | ||
| 180 | <string name="close">داخستن</string> | ||
| 181 | <string name="learn_more">زیاتر Ùێربە</string> | ||
| 182 | <string name="auto">خودکار</string> | ||
| 183 | <string name="submit">پێشکەشکردن</string> | ||
| 184 | <string name="string_import">هاوردەکردن</string> | ||
| 185 | <string name="export">هەناردەکردن</string> | ||
| 186 | <!-- GPU driver installation --> | ||
| 187 | <string name="select_gpu_driver">هەڵبژاردنی وەگەڕخەری GPU</string> | ||
| 188 | <string name="select_gpu_driver_title">ØÛ•ز دەکەیت وەگەڕخەری GPU ÛŒ ئێستات بگۆڕیت؟</string> | ||
| 189 | <string name="select_gpu_driver_install">دامەزراندن</string> | ||
| 190 | <string name="select_gpu_driver_default">بنەڕەت</string> | ||
| 191 | <string name="select_gpu_driver_use_default">بەکارهێنانی وەگەڕخەری GPU ی بنەڕەت</string> | ||
| 192 | <string name="select_gpu_driver_error">وەگەڕخەری نادروست هەڵبژێردرا، بە بەکارهێنانی بنەڕەتی سیستەم!</string> | ||
| 193 | <string name="system_gpu_driver">وەگەڕخەری GPU ی سیستەم</string> | ||
| 194 | <string name="installing_driver">دامەزراندنی وەگەڕخەر...</string> | ||
| 195 | |||
| 196 | <!-- Preferences Screen --> | ||
| 197 | <string name="preferences_settings">ڕێکخستنەکان</string> | ||
| 198 | <string name="preferences_general">گشتی</string> | ||
| 199 | <string name="preferences_system">سیستەم</string> | ||
| 200 | <string name="preferences_graphics">گراÙیک</string> | ||
| 201 | <string name="preferences_audio">دەنگ</string> | ||
| 202 | <string name="preferences_theme">ڕەنگ و ڕووکار</string> | ||
| 203 | <string name="preferences_debug">چاککردنەوە</string> | ||
| 204 | |||
| 205 | <!-- ROM loading errors --> | ||
| 206 | <string name="loader_error_encrypted">ڕۆمەکەت کۆدکراوە</string> | ||
| 207 | <string name="loader_error_encrypted_keys_description"><![CDATA[تکایە دڵنیابەوە لەدامەزراوی <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> ÙØ§ÛŒÙ„ەکەت بۆ ئەوەی بتوانرێت یارییەکان کۆد بکرێنەوە.]]></string> | ||
| 208 | <string name="loader_error_video_core">هەڵەیەک لە دەستپێکردنی ناوەکی ڤیدیۆکەدا ڕوویدا</string> | ||
| 209 | <string name="loader_error_video_core_description">ئەمەش بەزۆری بەهۆی وەگەڕخەرێکی ناتەبای GPU ەوەیە. دامەزراندنی وەگەڕخەری GPU ی تایبەتمەندکراو لەوانەیە ئەم کێشەیە چارەسەر بکات.</string> | ||
| 210 | <string name="loader_error_invalid_format">ناتوانرێت ڕۆم باربکرێت</string> | ||
| 211 | <string name="loader_error_file_not_found">ÙØ§ÛŒÙ„ÛŒ Ú•Û†Ù… بوونی نییە</string> | ||
| 212 | |||
| 213 | <!-- Emulation Menu --> | ||
| 214 | <string name="emulation_exit">دەرچوون لە ئیمولەیشن</string> | ||
| 215 | <string name="emulation_done">تەواو</string> | ||
| 216 | <string name="emulation_fps_counter">FPS ژمێر</string> | ||
| 217 | <string name="emulation_toggle_controls">گۆڕینی کۆنتڕۆڵ</string> | ||
| 218 | <string name="emulation_rel_stick_center">ناوەندی گێڕ بەنزیکەیی</string> | ||
| 219 | <string name="emulation_dpad_slide">خلیسکانی 4 دوگمەکە</string> | ||
| 220 | <string name="emulation_haptics">لەرینەوەی پەنجەلێدان</string> | ||
| 221 | <string name="emulation_show_overlay">نیشاندانی داپۆشەر</string> | ||
| 222 | <string name="emulation_toggle_all">گۆڕینی سەرجەم</string> | ||
| 223 | <string name="emulation_control_adjust">ڕێکخستنی داپۆشەر</string> | ||
| 224 | <string name="emulation_control_scale">پێوەر</string> | ||
| 225 | <string name="emulation_control_opacity">ڕوونی</string> | ||
| 226 | <string name="emulation_touch_overlay_reset">دووبارە ڕێکخستنەوەی داپۆشەر</string> | ||
| 227 | <string name="emulation_touch_overlay_edit">دەستکاریکردنی داپۆشەر</string> | ||
| 228 | <string name="emulation_pause">وەستاندنی ئیمولەیشن</string> | ||
| 229 | <string name="emulation_unpause">لادانی وەستاندنی ئیمولەیشن</string> | ||
| 230 | <string name="emulation_input_overlay">هەڵبژاردەکانی داپۆشەر</string> | ||
| 231 | |||
| 232 | <string name="load_settings">بارکردنی ڕێکخستنەکان...</string> | ||
| 233 | |||
| 234 | <!-- Software keyboard --> | ||
| 235 | <string name="software_keyboard">کیبۆردی نەرمەکاڵا</string> | ||
| 236 | |||
| 237 | <!-- Errors and warnings --> | ||
| 238 | <string name="abort_button">دەربارە</string> | ||
| 239 | <string name="continue_button">بەردەوام بوون</string> | ||
| 240 | <string name="system_archive_not_found">ئەرشیÙÛŒ سیستەم نەدۆزراوە</string> | ||
| 241 | <string name="system_archive_not_found_message">%s دیار نییە. تکایە ئەرشیÙÛŒ سیستەمەکەت ÙÚ•ÛŽ بدە.\nبەردەوامی ئیمولەیشن لەوانەیە ببێتە Ù‡Û†ÛŒ تێکچوون Ùˆ Ùڕێدانەدەرەوە.</string> | ||
| 242 | <string name="system_archive_general">ئەرشیÙÛŽÚ©ÛŒ سیستەم</string> | ||
| 243 | <string name="save_load_error">هەڵەی پاشەکەوتکردن/بارکردن</string> | ||
| 244 | <string name="fatal_error">هەڵەی کوشندە</string> | ||
| 245 | <string name="fatal_error_message">هەڵەیەکی کوشندە ڕوویدا. بۆ وردەکارییەکان Ù„Û†Ú¯Û•Ú©Û• بپشکنە.\nبەردەوامی ئیمولەیشن لەوانەیە ببێتە Ù‡Û†ÛŒ تێکچوون Ùˆ Ùڕێدانەدەرەوە.</string> | ||
| 246 | <string name="performance_warning">کوژاندنەوەی ئەم ڕێکخستنە دەبێتە هۆی کەمکردنەوەی کارایی ئیمولەیشن! بۆ باشترین ئەزموون، باشترە ئەم ڕێکخستنە چالاک بهێڵیتەوە.</string> | ||
| 247 | <!-- Region Names --> | ||
| 248 | <string name="region_japan">ژاپۆن</string> | ||
| 249 | <string name="region_usa">ئەمریکا</string> | ||
| 250 | <string name="region_europe">ئەورووپا</string> | ||
| 251 | <string name="region_australia">ئوسترالیا</string> | ||
| 252 | <string name="region_china">چین</string> | ||
| 253 | <string name="region_korea">کۆریا</string> | ||
| 254 | <string name="region_taiwan">تایوان</string> | ||
| 255 | |||
| 256 | <string name="memory_gigabyte">GB</string> | ||
| 257 | <!-- Renderer APIs --> | ||
| 258 | <string name="renderer_vulkan">ڤوڵکان</string> | ||
| 259 | <string name="renderer_none">هیچ</string> | ||
| 260 | |||
| 261 | <!-- Renderer Accuracy --> | ||
| 262 | <string name="renderer_accuracy_normal">ئاسایی</string> | ||
| 263 | <string name="renderer_accuracy_high">بەرز</string> | ||
| 264 | <string name="renderer_accuracy_extreme">ئەوپەڕ (خاو)</string> | ||
| 265 | |||
| 266 | <!-- Resolutions --> | ||
| 267 | <string name="resolution_half">0.5X (360p/540p)</string> | ||
| 268 | <string name="resolution_three_quarter">0.75X (540p/810p)</string> | ||
| 269 | <string name="resolution_one">1X (720p/1080p)</string> | ||
| 270 | <string name="resolution_two">2X (1440p/2160p) (خاو)</string> | ||
| 271 | <string name="resolution_three">3X (2160p/3240p) (خاو)</string> | ||
| 272 | <string name="resolution_four">4X (2880p/4320p) (خاو)</string> | ||
| 273 | |||
| 274 | <!-- Renderer VSync --> | ||
| 275 | <string name="renderer_vsync_immediate">دەستبەجێ (کوژاوە)</string> | ||
| 276 | <string name="renderer_vsync_mailbox">سندوقی پۆستە</string> | ||
| 277 | <string name="renderer_vsync_fifo">FIFO (پێکراو)</string> | ||
| 278 | <string name="renderer_vsync_fifo_relaxed">FIFO ئارام</string> | ||
| 279 | |||
| 280 | <!-- Scaling Filters --> | ||
| 281 | <string name="scaling_filter_nearest_neighbor">نزیکترین دراوسێ</string> | ||
| 282 | <string name="scaling_filter_bilinear">دوو هێڵی</string> | ||
| 283 | <string name="scaling_filter_bicubic">دووخشتەکی</string> | ||
| 284 | <string name="scaling_filter_gaussian">گاوسی</string> | ||
| 285 | <string name="scaling_filter_scale_force">پێوەرهێز</string> | ||
| 286 | <string name="scaling_filter_fsr">AMD FidelityFX™ سوپەر ووردبینی</string> | ||
| 287 | |||
| 288 | <!-- Anti-Aliasing --> | ||
| 289 | <string name="anti_aliasing_none">هیچ</string> | ||
| 290 | <string name="anti_aliasing_fxaa">FXAA</string> | ||
| 291 | <string name="anti_aliasing_smaa">SMAA</string> | ||
| 292 | |||
| 293 | <string name="screen_layout_auto">خودکار</string> | ||
| 294 | |||
| 295 | <!-- Aspect Ratios --> | ||
| 296 | <string name="ratio_default">بنەڕەت (16:9)</string> | ||
| 297 | <string name="ratio_force_four_three">ڕووبەری 4:3</string> | ||
| 298 | <string name="ratio_force_twenty_one_nine">ڕووبەری 21:9</string> | ||
| 299 | <string name="ratio_force_sixteen_ten">ڕووبەری 16:10</string> | ||
| 300 | <string name="ratio_stretch">کشانی پڕ بەشاشە</string> | ||
| 301 | |||
| 302 | <!-- CPU Accuracy --> | ||
| 303 | <string name="cpu_accuracy_accurate">وورد</string> | ||
| 304 | <string name="cpu_accuracy_unsafe">ناسەقامگیر</string> | ||
| 305 | <string name="cpu_accuracy_paranoid">بەگومان (خاو)</string> | ||
| 306 | |||
| 307 | <!-- Gamepad Buttons --> | ||
| 308 | <string name="gamepad_d_pad">4 دوگمەکە</string> | ||
| 309 | <string name="gamepad_left_stick">گێڕی چەپ</string> | ||
| 310 | <string name="gamepad_right_stick">گێڕی ڕاست</string> | ||
| 311 | <string name="gamepad_home">ماڵەوە</string> | ||
| 312 | <string name="gamepad_screenshot">وێنەگرتنی شاشە</string> | ||
| 313 | |||
| 314 | <!-- Disk shader cache --> | ||
| 315 | <string name="preparing_shaders">ئامادەکردنی سێبەرەکان</string> | ||
| 316 | <string name="building_shaders">دروستکردنی سێبەرەکان</string> | ||
| 317 | |||
| 318 | <!-- Theme options --> | ||
| 319 | <string name="change_app_theme">گۆڕینی ڕووکاری ئەپەکە</string> | ||
| 320 | <string name="theme_default">بنەڕەت</string> | ||
| 321 | <string name="theme_material_you">کەرەستەی تۆ</string> | ||
| 322 | |||
| 323 | <!-- Theme Modes --> | ||
| 324 | <string name="change_theme_mode">گۆڕینی دۆخی ڕووکار</string> | ||
| 325 | <string name="theme_mode_follow_system">پەیڕەوی کردنی سیستەم</string> | ||
| 326 | <string name="theme_mode_light">ڕوناکی</string> | ||
| 327 | <string name="theme_mode_dark">تاریک</string> | ||
| 328 | |||
| 329 | <!-- Black backgrounds theme --> | ||
| 330 | <string name="use_black_backgrounds">پاشبنەمای ڕەش</string> | ||
| 331 | <string name="use_black_backgrounds_description">لە کاتی بەکارهێنانی ڕووکاری تاریکدا، پاشبنەمای ڕەش دادەنێ.</string> | ||
| 332 | |||
| 333 | <!-- Licenses screen strings --> | ||
| 334 | <string name="licenses">مۆڵەتەکان</string> | ||
| 335 | <string name="license_fidelityfx_fsr_description">بەرزکردنەوەی کوالێتی بەرز لە کۆمپانیای AMD</string> | ||
| 336 | </resources> | ||
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index 72a47fbdb..9c6590b5e 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.<br /><br />Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string> | 4 | <string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.<br /><br />Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emulation ist aktiv</string> | 5 | <string name="emulation_notification_channel_name">Emulation ist aktiv</string> |
| @@ -25,6 +25,7 @@ | |||
| 25 | <string name="back">Zurück</string> | 25 | <string name="back">Zurück</string> |
| 26 | <string name="add_games">Spiele hinzufügen</string> | 26 | <string name="add_games">Spiele hinzufügen</string> |
| 27 | <string name="add_games_description">Spieleverzeichnis auswählen</string> | 27 | <string name="add_games_description">Spieleverzeichnis auswählen</string> |
| 28 | <string name="step_complete">Fertig!</string> | ||
| 28 | 29 | ||
| 29 | <!-- Home strings --> | 30 | <!-- Home strings --> |
| 30 | <string name="home_games">Spiele</string> | 31 | <string name="home_games">Spiele</string> |
| @@ -38,6 +39,7 @@ | |||
| 38 | <string name="add_games_warning_description">Spiele werden in der Spieleliste nicht angezeigt, wenn kein Ordner ausgewählt ist.</string> | 39 | <string name="add_games_warning_description">Spiele werden in der Spieleliste nicht angezeigt, wenn kein Ordner ausgewählt ist.</string> |
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 40 | <string name="home_search_games">Spiele suchen</string> | 41 | <string name="home_search_games">Spiele suchen</string> |
| 42 | <string name="search_settings">Einstellungen suchen</string> | ||
| 41 | <string name="games_dir_selected">Spieleverzeichnis ausgewählt</string> | 43 | <string name="games_dir_selected">Spieleverzeichnis ausgewählt</string> |
| 42 | <string name="install_prod_keys">prod.keys installieren</string> | 44 | <string name="install_prod_keys">prod.keys installieren</string> |
| 43 | <string name="install_prod_keys_description">Zum Entschlüsseln von Spielen benötigt</string> | 45 | <string name="install_prod_keys_description">Zum Entschlüsseln von Spielen benötigt</string> |
| @@ -60,8 +62,11 @@ | |||
| 60 | <string name="invalid_keys_file">Ungültige Schlüsseldatei ausgewählt</string> | 62 | <string name="invalid_keys_file">Ungültige Schlüsseldatei ausgewählt</string> |
| 61 | <string name="install_keys_success">Schlüssel erfolgreich installiert</string> | 63 | <string name="install_keys_success">Schlüssel erfolgreich installiert</string> |
| 62 | <string name="reading_keys_failure">Fehler beim Lesen der Schlüssel</string> | 64 | <string name="reading_keys_failure">Fehler beim Lesen der Schlüssel</string> |
| 65 | <string name="install_prod_keys_failure_extension_description">Überprüfen Sie, ob Ihre Schlüsseldatei die Erweiterung \".keys\" hat, und versuchen Sie es erneut.</string> | ||
| 66 | <string name="install_amiibo_keys_failure_extension_description">Überprüfen Sie, ob Ihre Schlüsseldatei die Erweiterung \".bin\" hat, und versuchen Sie es erneut.</string> | ||
| 63 | <string name="invalid_keys_error">Ungültige Schlüssel</string> | 67 | <string name="invalid_keys_error">Ungültige Schlüssel</string> |
| 64 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 68 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 69 | <string name="install_keys_failure_description">Die ausgewählte Datei ist falsch oder beschädigt. Bitte kopieren Sie Ihre Schlüssel erneut.</string> | ||
| 65 | <string name="install_gpu_driver">GPU-Treiber installieren</string> | 70 | <string name="install_gpu_driver">GPU-Treiber installieren</string> |
| 66 | <string name="install_gpu_driver_description">Alternative Treiber für eventuell bessere Leistung oder Genauigkeit installieren</string> | 71 | <string name="install_gpu_driver_description">Alternative Treiber für eventuell bessere Leistung oder Genauigkeit installieren</string> |
| 67 | <string name="advanced_settings">Erweiterte Einstellungen</string> | 72 | <string name="advanced_settings">Erweiterte Einstellungen</string> |
| @@ -84,7 +89,17 @@ | |||
| 84 | <string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string> | 89 | <string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string> |
| 85 | <string name="import_saves">Importieren</string> | 90 | <string name="import_saves">Importieren</string> |
| 86 | <string name="export_saves">Exportieren</string> | 91 | <string name="export_saves">Exportieren</string> |
| 87 | 92 | <string name="install_firmware">Firmware installieren</string> | |
| 93 | <string name="install_firmware_description">Die Firmware muss in einem ZIP-Archiv vorliegen und wird zum Booten einiger Spiele benötigt</string> | ||
| 94 | <string name="firmware_installing">Firmware wird installiert</string> | ||
| 95 | <string name="firmware_installed_success">Die Firmware wurde erfolgreich installiert!</string> | ||
| 96 | <string name="firmware_installed_failure">Bei der Firmware installation ist etwas fehlgeschlagen.</string> | ||
| 97 | <string name="share_log">Debug-Logs teilen</string> | ||
| 98 | <string name="share_log_description">Debug-Logs an yuzu zur Untersuchung absenden</string> | ||
| 99 | <string name="share_log_missing">Keine Log-Datei gefunden</string> | ||
| 100 | <string name="install_game_content">Spiel installieren</string> | ||
| 101 | <string name="install_game_content_description">Spiel Update oder DLC installieren</string> | ||
| 102 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 88 | <!-- About screen strings --> | 103 | <!-- About screen strings --> |
| 89 | <string name="gaia_is_not_real">Gaia ist nicht real</string> | 104 | <string name="gaia_is_not_real">Gaia ist nicht real</string> |
| 90 | <string name="copied_to_clipboard">In die Zwischenablage kopiert</string> | 105 | <string name="copied_to_clipboard">In die Zwischenablage kopiert</string> |
| @@ -92,7 +107,10 @@ | |||
| 92 | <string name="contributors">Beitragende</string> | 107 | <string name="contributors">Beitragende</string> |
| 93 | <string name="contributors_description">Gemacht mit \u2764 vom yuzu Team</string> | 108 | <string name="contributors_description">Gemacht mit \u2764 vom yuzu Team</string> |
| 94 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 109 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 110 | <string name="licenses_description">Projekte, die yuzu für Android möglich machen </string> | ||
| 95 | <string name="build">Build</string> | 111 | <string name="build">Build</string> |
| 112 | <string name="user_data">Nutzerdaten</string> | ||
| 113 | <string name="user_data_export_cancelled">Export abgebrochen</string> | ||
| 96 | <string name="support_link">https://discord.gg/u77vRWY</string> | 114 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 97 | <string name="website_link">https://yuzu-emu.org/</string> | 115 | <string name="website_link">https://yuzu-emu.org/</string> |
| 98 | <string name="github_link">https://github.com/yuzu-emu</string> | 116 | <string name="github_link">https://github.com/yuzu-emu</string> |
| @@ -107,45 +125,39 @@ | |||
| 107 | <string name="early_access_updates">Früherer Zugriff auf Updates</string> | 125 | <string name="early_access_updates">Früherer Zugriff auf Updates</string> |
| 108 | <string name="no_manual_installation">Keine manuelle Installation</string> | 126 | <string name="no_manual_installation">Keine manuelle Installation</string> |
| 109 | <string name="prioritized_support">Priorisierte Unterstützung</string> | 127 | <string name="prioritized_support">Priorisierte Unterstützung</string> |
| 128 | <string name="helping_game_preservation">Beitrag zur Erhaltung der Spiele</string> | ||
| 110 | <string name="our_eternal_gratitude">Unsere ewige Dankbarkeit</string> | 129 | <string name="our_eternal_gratitude">Unsere ewige Dankbarkeit</string> |
| 111 | <string name="are_you_interested">Bist du interessiert?</string> | 130 | <string name="are_you_interested">Bist du interessiert?</string> |
| 112 | 131 | ||
| 113 | <!-- General settings strings --> | 132 | <!-- General settings strings --> |
| 114 | <string name="frame_limit_enable">Geschwindigkeitsbegrenzung aktivieren</string> | 133 | <string name="frame_limit_enable">Limitierte Geschwindigkeit</string> |
| 115 | <string name="frame_limit_enable_description">Wenn aktiviert, wird die Emulationsgeschwindigkeit auf einen Prozentsatz der normalen Geschwindigkeit begrenzt.</string> | 134 | <string name="frame_limit_enable_description">Limitiert die Geschwindigkeit auf einen von dir festgelegten Prozentsatz.</string> |
| 116 | <string name="frame_limit_slider">Geschwindkeitsbegrenzung in Prozent</string> | 135 | <string name="frame_limit_slider">Geschwindkeitsbegrenzung in Prozent</string> |
| 117 | <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> | 136 | <string name="frame_limit_slider_description">Gibt die prozentuale Geschwindigkeit der Emulation an. 100% sind normal. Werte darüber oder drunter werden die Geschwindigkeit entsprechend verändern.</string> |
| 118 | <string name="cpu_accuracy">CPU-Genauigkeit</string> | 137 | <string name="cpu_accuracy">CPU-Genauigkeit</string> |
| 119 | |||
| 120 | <!-- System settings strings --> | 138 | <!-- System settings strings --> |
| 121 | <string name="use_docked_mode">Dock-Modus</string> | 139 | <string name="use_docked_mode">Gedockter Modus</string> |
| 122 | <string name="use_docked_mode_description">Emuliert im Dock-Modus, was die Auflösung verbessert, aber die Leistung senkt.</string> | 140 | <string name="use_docked_mode_description">Der Docked Modus erhöht die Auflösung, verringert die aber die Leistung. Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die Leistung.</string> |
| 123 | <string name="emulated_region">Emulierte Region</string> | 141 | <string name="emulated_region">Emulierte Region</string> |
| 124 | <string name="emulated_language">Emulierte Sprache</string> | 142 | <string name="emulated_language">Emulierte Sprache</string> |
| 125 | <string name="select_rtc_date">RTC-Datum auswählen</string> | 143 | <string name="select_rtc_date">RTC-Datum auswählen</string> |
| 126 | <string name="select_rtc_time">RTC-Zeit auswählen</string> | 144 | <string name="select_rtc_time">RTC-Zeit auswählen</string> |
| 127 | <string name="use_custom_rtc">Benutzerdefinierte RTC aktivieren</string> | 145 | <string name="use_custom_rtc">Benutzerdefinierte Echtzeituhr</string> |
| 128 | <string name="use_custom_rtc_description">Mit dieser Einstellung kann eine benutzerdefinierte Echtzeituhr unabhängig von der aktuellen Systemzeit verwendet werden.</string> | ||
| 129 | <string name="set_custom_rtc">Benutzerdefinierte RTC einstellen</string> | ||
| 130 | |||
| 131 | <!-- Graphics settings strings --> | 146 | <!-- Graphics settings strings --> |
| 132 | <string name="renderer_api">API</string> | ||
| 133 | <string name="renderer_accuracy">Genauigkeitsstufe</string> | 147 | <string name="renderer_accuracy">Genauigkeitsstufe</string> |
| 134 | <string name="renderer_resolution">Auflösung</string> | ||
| 135 | <string name="renderer_vsync">VSync-Modus</string> | 148 | <string name="renderer_vsync">VSync-Modus</string> |
| 149 | <string name="renderer_screen_layout">Orientierung</string> | ||
| 136 | <string name="renderer_aspect_ratio">Seitenverhältnis</string> | 150 | <string name="renderer_aspect_ratio">Seitenverhältnis</string> |
| 137 | <string name="renderer_scaling_filter">Fensteranpassungsfilter</string> | 151 | <string name="renderer_scaling_filter">Fensteranpassungsfilter</string> |
| 138 | <string name="renderer_anti_aliasing">Kantenglättungs-Methode</string> | ||
| 139 | <string name="renderer_force_max_clock">Maximale Taktfrequenz erzwingen (nur Adreno)</string> | 152 | <string name="renderer_force_max_clock">Maximale Taktfrequenz erzwingen (nur Adreno)</string> |
| 140 | <string name="renderer_force_max_clock_description">Erzwingt den Betrieb der GPU mit der maximal möglichen Taktfrequenz (Temperaturbeschränkungen werden weiterhin angewendet).</string> | 153 | <string name="renderer_force_max_clock_description">Erzwingt den Betrieb der GPU mit der maximal möglichen Taktfrequenz (Temperaturbeschränkungen werden weiterhin angewendet).</string> |
| 141 | <string name="renderer_asynchronous_shaders">Asynchrone Shader nutzen</string> | 154 | <string name="renderer_asynchronous_shaders">Asynchrone Shader nutzen</string> |
| 142 | <string name="renderer_asynchronous_shaders_description">Kompiliert Shader asynchron, was Ruckler reduziert, aber zu Glitches führen kann.</string> | 155 | <!-- Debug settings strings --> |
| 143 | <string name="renderer_debug">Grafik-Debugging aktivieren</string> | 156 | <string name="cpu">CPU</string> |
| 144 | <string name="renderer_debug_description">Wenn aktiviert, schaltet die Grafik-API in einen langsameren Debugging-Modus.</string> | 157 | <string name="cpu_debug_mode">CPU Debugging</string> |
| 145 | <string name="use_disk_shader_cache">Nutze Festplatten-Shader-Cache</string> | 158 | <string name="gpu">GPU</string> |
| 146 | <string name="use_disk_shader_cache_description">Ruckeln wird durch das Speichern und Laden von generierten Shadern auf der Festplatte reduziert.</string> | 159 | <string name="renderer_api">API</string> |
| 147 | 160 | <string name="renderer_debug">Graphik-Debugging</string> | |
| 148 | <!-- Audio settings strings --> | ||
| 149 | <string name="audio_volume">Lautstärke</string> | 161 | <string name="audio_volume">Lautstärke</string> |
| 150 | <string name="audio_volume_description">Legt die Lautstärke der Audioausgabe fest.</string> | 162 | <string name="audio_volume_description">Legt die Lautstärke der Audioausgabe fest.</string> |
| 151 | 163 | ||
| @@ -154,14 +166,22 @@ | |||
| 154 | <string name="ini_saved">Einstellungen gespeichert</string> | 166 | <string name="ini_saved">Einstellungen gespeichert</string> |
| 155 | <string name="gameid_saved">Einstellungen für %1$s gespeichert</string> | 167 | <string name="gameid_saved">Einstellungen für %1$s gespeichert</string> |
| 156 | <string name="error_saving">Fehler beim Speichern von %1$s.ini: %2$s</string> | 168 | <string name="error_saving">Fehler beim Speichern von %1$s.ini: %2$s</string> |
| 169 | <string name="unimplemented_menu">Unimplementiertes Menü</string> | ||
| 157 | <string name="loading">Lädt...</string> | 170 | <string name="loading">Lädt...</string> |
| 158 | <string name="reset_setting_confirmation">Möchtest du diese Einstellung auf den Standardwert zurücksetzen?</string> | 171 | <string name="reset_setting_confirmation">Möchtest du diese Einstellung auf den Standardwert zurücksetzen?</string> |
| 159 | <string name="reset_to_default">Auf Standard zurücksetzen</string> | 172 | <string name="reset_to_default">Auf Standard zurücksetzen</string> |
| 160 | <string name="reset_all_settings">Alle Einstellungen zurücksetzen?</string> | 173 | <string name="reset_all_settings">Alle Einstellungen zurücksetzen?</string> |
| 161 | <string name="reset_all_settings_description">Alle erweiterten Einstellungen werden auf ihren Standardwert zurückgesetzt. Dies kann nicht rückgängig gemacht werden.</string> | ||
| 162 | <string name="settings_reset">Einstellungen zurückgesetzt</string> | 174 | <string name="settings_reset">Einstellungen zurückgesetzt</string> |
| 163 | <string name="close">Schließen</string> | 175 | <string name="close">Schließen</string> |
| 164 | <string name="learn_more">Mehr erfahren</string> | 176 | <string name="learn_more">Mehr erfahren</string> |
| 177 | <string name="auto">Auto</string> | ||
| 178 | <string name="submit">Absenden</string> | ||
| 179 | <string name="string_null">Null</string> | ||
| 180 | <string name="string_import">Importieren</string> | ||
| 181 | <string name="export">Exportieren</string> | ||
| 182 | <string name="export_failed">Export fehlgeschlagen</string> | ||
| 183 | <string name="import_failed">Import fehlgeschlagen</string> | ||
| 184 | <string name="cancelling">Abbrechen</string> | ||
| 165 | 185 | ||
| 166 | <!-- GPU driver installation --> | 186 | <!-- GPU driver installation --> |
| 167 | <string name="select_gpu_driver">GPU-Treiber auswählen</string> | 187 | <string name="select_gpu_driver">GPU-Treiber auswählen</string> |
| @@ -169,6 +189,7 @@ | |||
| 169 | <string name="select_gpu_driver_install">Installieren</string> | 189 | <string name="select_gpu_driver_install">Installieren</string> |
| 170 | <string name="select_gpu_driver_default">Standard</string> | 190 | <string name="select_gpu_driver_default">Standard</string> |
| 171 | <string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string> | 191 | <string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string> |
| 192 | <string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string> | ||
| 172 | <string name="system_gpu_driver">System GPU-Treiber</string> | 193 | <string name="system_gpu_driver">System GPU-Treiber</string> |
| 173 | <string name="installing_driver">Treiber wird installiert...</string> | 194 | <string name="installing_driver">Treiber wird installiert...</string> |
| 174 | 195 | ||
| @@ -179,6 +200,7 @@ | |||
| 179 | <string name="preferences_graphics">Grafik</string> | 200 | <string name="preferences_graphics">Grafik</string> |
| 180 | <string name="preferences_audio">Audio</string> | 201 | <string name="preferences_audio">Audio</string> |
| 181 | <string name="preferences_theme">Theme und Farbe</string> | 202 | <string name="preferences_theme">Theme und Farbe</string> |
| 203 | <string name="preferences_debug">Debug</string> | ||
| 182 | 204 | ||
| 183 | <!-- ROM loading errors --> | 205 | <!-- ROM loading errors --> |
| 184 | <string name="loader_error_encrypted">Das ROM ist verschlüsselt</string> | 206 | <string name="loader_error_encrypted">Das ROM ist verschlüsselt</string> |
| @@ -192,22 +214,15 @@ | |||
| 192 | <string name="emulation_exit">Emulation beenden</string> | 214 | <string name="emulation_exit">Emulation beenden</string> |
| 193 | <string name="emulation_done">Fertig</string> | 215 | <string name="emulation_done">Fertig</string> |
| 194 | <string name="emulation_fps_counter">FPS Zähler</string> | 216 | <string name="emulation_fps_counter">FPS Zähler</string> |
| 195 | <string name="emulation_toggle_controls">Steuerung umschalten</string> | ||
| 196 | <string name="emulation_rel_stick_center">Relative Stick-Mitte</string> | ||
| 197 | <string name="emulation_dpad_slide">DPad Slide</string> | ||
| 198 | <string name="emulation_haptics">Haptik</string> | ||
| 199 | <string name="emulation_show_overlay">Overlay anzeigen</string> | ||
| 200 | <string name="emulation_toggle_all">Alle umschalten</string> | 217 | <string name="emulation_toggle_all">Alle umschalten</string> |
| 201 | <string name="emulation_control_adjust">Overlay anpassen</string> | 218 | <string name="emulation_control_adjust">Overlay anpassen</string> |
| 202 | <string name="emulation_control_scale">Größe</string> | 219 | <string name="emulation_control_scale">Größe</string> |
| 203 | <string name="emulation_control_opacity">Transparenz</string> | 220 | <string name="emulation_control_opacity">Transparenz</string> |
| 204 | <string name="emulation_touch_overlay_reset">Overlay zurücksetzen</string> | 221 | <string name="emulation_touch_overlay_reset">Overlay zurücksetzen</string> |
| 205 | <string name="emulation_touch_overlay_edit">Overlay bearbeiten</string> | 222 | <string name="emulation_touch_overlay_edit">Overlay bearbeiten</string> |
| 206 | <string name="emulation_pause">Emulation pausieren</string> | ||
| 207 | <string name="emulation_unpause">Emulation fortsetzen</string> | ||
| 208 | <string name="emulation_input_overlay">Overlay-Optionen</string> | 223 | <string name="emulation_input_overlay">Overlay-Optionen</string> |
| 209 | 224 | ||
| 210 | <string name="load_settings">Lädt Einstellungen...</string> | 225 | <string name="load_settings">Lade Einstellungen...</string> |
| 211 | 226 | ||
| 212 | <!-- Software keyboard --> | 227 | <!-- Software keyboard --> |
| 213 | <string name="software_keyboard">Software-Tastatur</string> | 228 | <string name="software_keyboard">Software-Tastatur</string> |
| @@ -221,7 +236,7 @@ | |||
| 221 | <string name="fatal_error">Schwerwiegender Fehler</string> | 236 | <string name="fatal_error">Schwerwiegender Fehler</string> |
| 222 | <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> | 237 | <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> |
| 223 | <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> | 238 | <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> |
| 224 | 239 | <string name="memory_formatted">%1$s %2$s</string> | |
| 225 | <!-- Region Names --> | 240 | <!-- Region Names --> |
| 226 | <string name="region_japan">Japan</string> | 241 | <string name="region_japan">Japan</string> |
| 227 | <string name="region_usa">USA</string> | 242 | <string name="region_usa">USA</string> |
| @@ -231,6 +246,15 @@ | |||
| 231 | <string name="region_korea">Korea</string> | 246 | <string name="region_korea">Korea</string> |
| 232 | <string name="region_taiwan">Taiwan</string> | 247 | <string name="region_taiwan">Taiwan</string> |
| 233 | 248 | ||
| 249 | <!-- Memory Sizes --> | ||
| 250 | <string name="memory_byte">Byte</string> | ||
| 251 | <string name="memory_kilobyte">KB</string> | ||
| 252 | <string name="memory_megabyte">MB</string> | ||
| 253 | <string name="memory_gigabyte">GB</string> | ||
| 254 | <string name="memory_terabyte">TB</string> | ||
| 255 | <string name="memory_petabyte">PB</string> | ||
| 256 | <string name="memory_exabyte">EB</string> | ||
| 257 | |||
| 234 | <!-- Renderer APIs --> | 258 | <!-- Renderer APIs --> |
| 235 | <string name="renderer_vulkan">Vulkan</string> | 259 | <string name="renderer_vulkan">Vulkan</string> |
| 236 | <string name="renderer_none">Keiner</string> | 260 | <string name="renderer_none">Keiner</string> |
| @@ -267,12 +291,15 @@ | |||
| 267 | <string name="anti_aliasing_fxaa">FXAA</string> | 291 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 268 | <string name="anti_aliasing_smaa">SMAA</string> | 292 | <string name="anti_aliasing_smaa">SMAA</string> |
| 269 | 293 | ||
| 294 | <string name="screen_layout_portrait">Portrait</string> | ||
| 295 | <string name="screen_layout_auto">Auto</string> | ||
| 296 | |||
| 270 | <!-- Aspect Ratios --> | 297 | <!-- Aspect Ratios --> |
| 271 | <string name="ratio_default">Standard (16:9)</string> | 298 | <string name="ratio_default">Standard (16:9)</string> |
| 272 | <string name="ratio_force_four_three">4:3 erzwingen</string> | 299 | <string name="ratio_force_four_three">4:3 erzwingen</string> |
| 273 | <string name="ratio_force_twenty_one_nine">21:9 erzwingen</string> | 300 | <string name="ratio_force_twenty_one_nine">21:9 erzwingen</string> |
| 274 | <string name="ratio_force_sixteen_ten">Erzwinge 16:10</string> | 301 | <string name="ratio_force_sixteen_ten">Erzwinge 16:10</string> |
| 275 | <string name="ratio_stretch">Auf Fenster anpassen</string> | 302 | <string name="ratio_stretch">Auf Bildschirmgröße anpsassen</string> |
| 276 | 303 | ||
| 277 | <!-- CPU Accuracy --> | 304 | <!-- CPU Accuracy --> |
| 278 | <string name="cpu_accuracy_accurate">Akkurat</string> | 305 | <string name="cpu_accuracy_accurate">Akkurat</string> |
| @@ -280,9 +307,9 @@ | |||
| 280 | <string name="cpu_accuracy_paranoid">Paranoid (Langsam)</string> | 307 | <string name="cpu_accuracy_paranoid">Paranoid (Langsam)</string> |
| 281 | 308 | ||
| 282 | <!-- Gamepad Buttons --> | 309 | <!-- Gamepad Buttons --> |
| 283 | <string name="gamepad_d_pad">Steuerkreuz</string> | 310 | <string name="gamepad_d_pad">D-Pad</string> |
| 284 | <string name="gamepad_left_stick">Linker Analogstick</string> | 311 | <string name="gamepad_left_stick">Linker Stick</string> |
| 285 | <string name="gamepad_right_stick">Rechter Analogstick</string> | 312 | <string name="gamepad_right_stick">Rechter Stick</string> |
| 286 | <string name="gamepad_home">Home</string> | 313 | <string name="gamepad_home">Home</string> |
| 287 | <string name="gamepad_screenshot">Screenshot</string> | 314 | <string name="gamepad_screenshot">Screenshot</string> |
| 288 | 315 | ||
| @@ -291,18 +318,30 @@ | |||
| 291 | <string name="building_shaders">Shader werden erstellt</string> | 318 | <string name="building_shaders">Shader werden erstellt</string> |
| 292 | 319 | ||
| 293 | <!-- Theme options --> | 320 | <!-- Theme options --> |
| 294 | <string name="change_app_theme">App-Theme ändern</string> | 321 | <string name="change_app_theme">App-Thema ändern</string> |
| 295 | <string name="theme_default">Standard</string> | 322 | <string name="theme_default">Standard</string> |
| 296 | <string name="theme_material_you">Material You</string> | 323 | <string name="theme_material_you">Material You</string> |
| 297 | 324 | ||
| 298 | <!-- Theme Modes --> | 325 | <!-- Theme Modes --> |
| 299 | <string name="change_theme_mode">Theme-Modus ändern</string> | 326 | <string name="change_theme_mode">Themen-Modus ändern</string> |
| 300 | <string name="theme_mode_follow_system">System folgen</string> | 327 | <string name="theme_mode_follow_system">System folgen</string> |
| 301 | <string name="theme_mode_light">Hell</string> | 328 | <string name="theme_mode_light">Hell</string> |
| 302 | <string name="theme_mode_dark">Dunkel</string> | 329 | <string name="theme_mode_dark">Dunkel</string> |
| 303 | 330 | ||
| 331 | <!-- Audio output engines --> | ||
| 332 | <string name="cubeb">cubeb</string> | ||
| 333 | |||
| 304 | <!-- Black backgrounds theme --> | 334 | <!-- Black backgrounds theme --> |
| 305 | <string name="use_black_backgrounds">Schwarze Hintergünde verwenden</string> | 335 | <string name="use_black_backgrounds">Schwarze Hintergründe</string> |
| 306 | <string name="use_black_backgrounds_description">Bei Verwendung des dunklen Themes, schwarze Hintergründe verwenden.</string> | 336 | <string name="use_black_backgrounds_description">Bei Verwendung des dunklen Themes, schwarze Hintergründe verwenden.</string> |
| 307 | 337 | ||
| 308 | </resources> | 338 | <!-- Picture-In-Picture --> |
| 339 | <string name="picture_in_picture">Bild im Bild</string> | ||
| 340 | <string name="pause">Pause</string> | ||
| 341 | <string name="mute">Stummschalten</string> | ||
| 342 | <string name="unmute">Ton aktivieren</string> | ||
| 343 | |||
| 344 | <!-- Licenses screen strings --> | ||
| 345 | <string name="licenses">Lizenzen</string> | ||
| 346 | <string name="license_fidelityfx_fsr_description">Hochwertiges Upscaling von AMD</string> | ||
| 347 | </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 index e5bdd5889..103ac6e65 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o keys no vienen incluidos.<br /><br />Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string> | 4 | <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o claves no vienen incluidos.<br /><br />Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emulación activa</string> | 5 | <string name="emulation_notification_channel_name">Emulación activa</string> |
| 6 | <string name="emulation_notification_channel_description">Muestra una notificación persistente cuando la emulación está activa.</string> | 6 | <string name="emulation_notification_channel_description">Muestra una notificación persistente cuando la emulación está activa.</string> |
| 7 | <string name="emulation_notification_running">yuzu esta ejecutándose</string> | 7 | <string name="emulation_notification_running">yuzu esta ejecutándose</string> |
| @@ -25,6 +25,7 @@ | |||
| 25 | <string name="back">Atrás</string> | 25 | <string name="back">Atrás</string> |
| 26 | <string name="add_games">Añadir Juegos</string> | 26 | <string name="add_games">Añadir Juegos</string> |
| 27 | <string name="add_games_description">Selecciona la carpeta de juegos</string> | 27 | <string name="add_games_description">Selecciona la carpeta de juegos</string> |
| 28 | <string name="step_complete">¡Completado!</string> | ||
| 28 | 29 | ||
| 29 | <!-- Home strings --> | 30 | <!-- Home strings --> |
| 30 | <string name="home_games">Juegos</string> | 31 | <string name="home_games">Juegos</string> |
| @@ -37,7 +38,8 @@ | |||
| 37 | <string name="add_games_warning">¿Omitir la selección de la carpeta de juegos?</string> | 38 | <string name="add_games_warning">¿Omitir la selección de la carpeta de juegos?</string> |
| 38 | <string name="add_games_warning_description">No se mostrará ningún juego si no se ha seleccionado una carpeta de juegos.</string> | 39 | <string name="add_games_warning_description">No se mostrará ningún juego si no se ha seleccionado una carpeta de juegos.</string> |
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 40 | <string name="home_search_games">Buscar Juegos</string> | 41 | <string name="home_search_games">Buscar juegos</string> |
| 42 | <string name="search_settings">Buscar configuración</string> | ||
| 41 | <string name="games_dir_selected">Directorio de juegos seleccionado</string> | 43 | <string name="games_dir_selected">Directorio de juegos seleccionado</string> |
| 42 | <string name="install_prod_keys">Instalar prod.keys</string> | 44 | <string name="install_prod_keys">Instalar prod.keys</string> |
| 43 | <string name="install_prod_keys_description">Requerido para descifrar juegos</string> | 45 | <string name="install_prod_keys_description">Requerido para descifrar juegos</string> |
| @@ -58,15 +60,18 @@ | |||
| 58 | <string name="warning_cancel">Cancelar</string> | 60 | <string name="warning_cancel">Cancelar</string> |
| 59 | <string name="install_amiibo_keys">Instalar clave de Amiiboo</string> | 61 | <string name="install_amiibo_keys">Instalar clave de Amiiboo</string> |
| 60 | <string name="install_amiibo_keys_description">Necesario para usar Amiibo en el juego</string> | 62 | <string name="install_amiibo_keys_description">Necesario para usar Amiibo en el juego</string> |
| 61 | <string name="invalid_keys_file">Archivo de claves inválido seleccionado</string> | 63 | <string name="invalid_keys_file">Archivo de claves seleccionado inválido</string> |
| 62 | <string name="install_keys_success">Claves instaladas correctamente</string> | 64 | <string name="install_keys_success">Claves instaladas correctamente</string> |
| 63 | <string name="reading_keys_failure">Error al leer las claves de cifrado</string> | 65 | <string name="reading_keys_failure">Error al leer las claves de cifrado</string> |
| 66 | <string name="install_prod_keys_failure_extension_description">Compruebe que el archivo de claves tenga una extensión .keys y pruebe otra vez.</string> | ||
| 67 | <string name="install_amiibo_keys_failure_extension_description">Compruebe que el archivo de claves tenga una extensión .bin y pruebe otra vez.</string> | ||
| 64 | <string name="invalid_keys_error">Claves de cifrado no válidas</string> | 68 | <string name="invalid_keys_error">Claves de cifrado no válidas</string> |
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 69 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">El archivo seleccionado es incorrecto o está corrupto. Vuelva a redumpear sus claves.</string> | 70 | <string name="install_keys_failure_description">El archivo seleccionado es incorrecto o está corrupto. Vuelva a redumpear sus claves.</string> |
| 67 | <string name="install_gpu_driver">Instalar driver de GPU</string> | 71 | <string name="install_gpu_driver">Instalar driver de GPU</string> |
| 68 | <string name="install_gpu_driver_description">Instale drivers alternativos para obtener un rendimiento o una precisión potencialmente mejores</string> | 72 | <string name="install_gpu_driver_description">Instale drivers alternativos para obtener un rendimiento o una precisión potencialmente mejores</string> |
| 69 | <string name="advanced_settings">Opciones avanzadas</string> | 73 | <string name="advanced_settings">Opciones avanzadas</string> |
| 74 | <string name="advanced_settings_game">Configuración avanzada: %1$s</string> | ||
| 70 | <string name="settings_description">Configurar las opciones del emulador</string> | 75 | <string name="settings_description">Configurar las opciones del emulador</string> |
| 71 | <string name="search_recently_played">Jugado recientemente</string> | 76 | <string name="search_recently_played">Jugado recientemente</string> |
| 72 | <string name="search_recently_added">Añadido recientemente</string> | 77 | <string name="search_recently_added">Añadido recientemente</string> |
| @@ -86,6 +91,33 @@ | |||
| 86 | <string name="save_file_invalid_zip_structure_description">El nombre de la primera subcarpeta debe ser el Title ID del juego.</string> | 91 | <string name="save_file_invalid_zip_structure_description">El nombre de la primera subcarpeta debe ser el Title ID del juego.</string> |
| 87 | <string name="import_saves">Importar</string> | 92 | <string name="import_saves">Importar</string> |
| 88 | <string name="export_saves">Exportar</string> | 93 | <string name="export_saves">Exportar</string> |
| 94 | <string name="install_firmware">Instalar firmware</string> | ||
| 95 | <string name="install_firmware_description">El firmware debe estar en un archivo ZIP y es necesario para ejecutar algunos juegos</string> | ||
| 96 | <string name="firmware_installing">Instalando firmware</string> | ||
| 97 | <string name="firmware_installed_success">Firmware instalado con éxito</string> | ||
| 98 | <string name="firmware_installed_failure">Falló la instalación de firmware</string> | ||
| 99 | <string name="firmware_installed_failure_description">Asegúrese de que los archivos nca del firmware estén en la raÃz del zip e inténtelo de nuevo.</string> | ||
| 100 | <string name="share_log">Compartir registros de depuración</string> | ||
| 101 | <string name="share_log_description">Comparta el archivo de registro de yuzu para depurar problemas</string> | ||
| 102 | <string name="share_log_missing">No se encontró ningún archivo de registro</string> | ||
| 103 | <string name="install_game_content">Instalar contenido de juego</string> | ||
| 104 | <string name="install_game_content_description">Instalar actualizaciones o DLC</string> | ||
| 105 | <string name="installing_game_content">Instalando contenido...</string> | ||
| 106 | <string name="install_game_content_failure">Error instalando archivo(s) a la NAND</string> | ||
| 107 | <string name="install_game_content_failure_description">Asegúrese de que el/los contenido(s) son válidos y que el archivo prod.keys esté instalado.</string> | ||
| 108 | <string name="install_game_content_failure_base">La instalación de los juegos base no está permitida para asà evitar posibles conflictos.</string> | ||
| 109 | <string name="install_game_content_failure_file_extension">Sólo hay soporte para el contenido en NSP y XCI. Asegúrese de que el/los contenido(s) son válidos.</string> | ||
| 110 | <string name="install_game_content_failed_count">%1$d error(es) de instalación</string> | ||
| 111 | <string name="install_game_content_success">Contenido(s) de juego instalado/s con éxito</string> | ||
| 112 | <string name="install_game_content_success_install">%1$d instalado con éxito</string> | ||
| 113 | <string name="install_game_content_success_overwrite">%1$d sobreescrito con éxito</string> | ||
| 114 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 115 | <string name="custom_driver_not_supported">Drivers personalizados no soportados</string> | ||
| 116 | <string name="custom_driver_not_supported_description">En estos momentos, la carga de drivers personalizados no está disponible para este dispositivo..\n¡Comprueba esta opción en el futuro para ver si ya está añadido el soporte a ese dispositivo!</string> | ||
| 117 | <string name="manage_yuzu_data">Administrar datos de yuzu</string> | ||
| 118 | <string name="manage_yuzu_data_description">Importa/exporta el firmware, las keys, los datos de usuario, ¡y más!</string> | ||
| 119 | <string name="share_save_file">Compartir archivo de guardado</string> | ||
| 120 | <string name="export_save_failed">La exportación del guardado falló</string> | ||
| 89 | 121 | ||
| 90 | <!-- About screen strings --> | 122 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">Gaia no es real</string> | 123 | <string name="gaia_is_not_real">Gaia no es real</string> |
| @@ -94,7 +126,18 @@ | |||
| 94 | <string name="contributors">Contribuidores</string> | 126 | <string name="contributors">Contribuidores</string> |
| 95 | <string name="contributors_description">Hecho con \u2764 del equipo yuzu</string> | 127 | <string name="contributors_description">Hecho con \u2764 del equipo yuzu</string> |
| 96 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 128 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 129 | <string name="licenses_description">Proyectos que hacen que yuzu para Android sea una realidad</string> | ||
| 97 | <string name="build">Versión</string> | 130 | <string name="build">Versión</string> |
| 131 | <string name="user_data">Datos de usuario</string> | ||
| 132 | <string name="user_data_description">Importa/exporta todos los datos de usuario.\n\nCuando se importen los datos de usuario, ¡los demás datos de usuario existentes serán borrados!</string> | ||
| 133 | <string name="exporting_user_data">Exportando datos de usuario...</string> | ||
| 134 | <string name="importing_user_data">Importando datos de usuario...</string> | ||
| 135 | <string name="import_user_data">Importar datos de usuario</string> | ||
| 136 | <string name="invalid_yuzu_backup">Backup de válido</string> | ||
| 137 | <string name="user_data_export_success">Datos de usuario exportados con éxito</string> | ||
| 138 | <string name="user_data_import_success">Datos de usuario importados con éxito</string> | ||
| 139 | <string name="user_data_export_cancelled">Exportación cancelada</string> | ||
| 140 | <string name="user_data_import_failed_description">Asegúrese de que las carpetas de datos de usuario estén en la raÃz de la carpeta del zip y contengan un archivo config en config/config.ini e inténtelo de nuevo.</string> | ||
| 98 | <string name="support_link">https://discord.gg/u77vRWY</string> | 141 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 99 | <string name="website_link">https://yuzu-emu.org/</string> | 142 | <string name="website_link">https://yuzu-emu.org/</string> |
| 100 | <string name="github_link">https://github.com/yuzu-emu</string> | 143 | <string name="github_link">https://github.com/yuzu-emu</string> |
| @@ -114,41 +157,53 @@ | |||
| 114 | <string name="are_you_interested">¿Estás interesado?</string> | 157 | <string name="are_you_interested">¿Estás interesado?</string> |
| 115 | 158 | ||
| 116 | <!-- General settings strings --> | 159 | <!-- General settings strings --> |
| 117 | <string name="frame_limit_enable">Activar limite de velocidad</string> | 160 | <string name="frame_limit_enable">Limitar velocidad</string> |
| 118 | <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> | 161 | <string name="frame_limit_enable_description">Limita la velocidad de emulación a un porcentaje especÃfico de la velocidad normal.</string> |
| 119 | <string name="frame_limit_slider">Limitar porcentaje de velocidad</string> | 162 | <string name="frame_limit_slider">Limitar porcentaje de velocidad</string> |
| 120 | <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> | 163 | <string name="frame_limit_slider_description">Especifica el porcentaje para limitar la velocidad de emulación. 100% es la velocidad normal. Valores más altos o bajos incrementarán o disminuirán el lÃmite de velocidad.</string> |
| 121 | <string name="cpu_accuracy">Precisión de CPU</string> | 164 | <string name="cpu_accuracy">Precisión de CPU</string> |
| 165 | <string name="value_with_units">%1$s%2$s</string> | ||
| 122 | 166 | ||
| 123 | <!-- System settings strings --> | 167 | <!-- System settings strings --> |
| 124 | <string name="use_docked_mode">Modo sobremesa</string> | 168 | <string name="use_docked_mode">Modo Sobremesa</string> |
| 125 | <string name="use_docked_mode_description">Emula en modo sobremesa, lo que aumenta la resolución perjudicando el rendimiento.</string> | 169 | <string name="use_docked_mode_description">Incrementa la resolución al coste de reducir el rendimiento. El Modo Portátil es usado cuando está desactivado, reduciendo la resolución y mejorando asà el rendimiento.</string> |
| 126 | <string name="emulated_region">Región emulada</string> | 170 | <string name="emulated_region">Región emulada</string> |
| 127 | <string name="emulated_language">Idioma emulado</string> | 171 | <string name="emulated_language">Idioma emulado</string> |
| 128 | <string name="select_rtc_date">Seleccionar Fecha RTC</string> | 172 | <string name="select_rtc_date">Seleccionar fecha RTC</string> |
| 129 | <string name="select_rtc_time">Seleccionar Tiempo RTC</string> | 173 | <string name="select_rtc_time">Seleccionar tiempo RTC</string> |
| 130 | <string name="use_custom_rtc">Habilitar RTC Personalizado</string> | 174 | <string name="use_custom_rtc">RTC personalizado</string> |
| 131 | <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> | 175 | <string name="use_custom_rtc_description">Te permite tener un reloj personalizado en tiempo real diferente del tiempo del propio sistema.</string> |
| 132 | <string name="set_custom_rtc">Establecer RTC Personalizado</string> | 176 | <string name="set_custom_rtc">Configurar RTC personalizado</string> |
| 133 | 177 | ||
| 134 | <!-- Graphics settings strings --> | 178 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">API</string> | ||
| 136 | <string name="renderer_accuracy">Nivel de precisión</string> | 179 | <string name="renderer_accuracy">Nivel de precisión</string> |
| 137 | <string name="renderer_resolution">Resolución</string> | 180 | <string name="renderer_resolution">Resolución (Portátil/Sobremesa)</string> |
| 138 | <string name="renderer_vsync">Modo VSync</string> | 181 | <string name="renderer_vsync">Modo VSync</string> |
| 182 | <string name="renderer_screen_layout">Orientación</string> | ||
| 139 | <string name="renderer_aspect_ratio">Relación de aspecto</string> | 183 | <string name="renderer_aspect_ratio">Relación de aspecto</string> |
| 140 | <string name="renderer_scaling_filter">Filtro de adaptación de ventana</string> | 184 | <string name="renderer_scaling_filter">Filtro de adaptación de ventana</string> |
| 141 | <string name="renderer_anti_aliasing">Metodo Anti Aliasing</string> | 185 | <string name="renderer_anti_aliasing">Método anti-aliasing</string> |
| 142 | <string name="renderer_force_max_clock">Forzar velocidad al máximo (solo Adreno)</string> | 186 | <string name="renderer_force_max_clock">Forzar velocidad al máximo (solo Adreno)</string> |
| 143 | <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> | 187 | <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> |
| 144 | <string name="renderer_asynchronous_shaders">Usar shaders asÃncronos</string> | 188 | <string name="renderer_asynchronous_shaders">Usar shaders asÃncronos</string> |
| 145 | <string name="renderer_asynchronous_shaders_description">Compila shaders de forma asincrónica, lo que reducirá los parones pero puede introducir fallos.</string> | 189 | <string name="renderer_asynchronous_shaders_description">Compila shaders de manera asÃncrona, reduciendo los parones, pero puede introducir fallos.</string> |
| 146 | <string name="renderer_debug">Habilitar la depuración de gráficos</string> | 190 | <string name="renderer_reactive_flushing">Usar limpieza reactiva</string> |
| 147 | <string name="renderer_debug_description">Cuando esté marcado, la API de gráficos entra en un modo de depuración más lento.</string> | 191 | <string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string> |
| 148 | <string name="use_disk_shader_cache">Usar caché de shaders en disco</string> | 192 | <string name="use_disk_shader_cache">Caché de shaders en disco</string> |
| 149 | <string name="use_disk_shader_cache_description">Reduzca los parones almacenando y cargando shaders generados en el disco.</string> | 193 | <string name="use_disk_shader_cache_description">Reduce los parones almacenando y cargando shaders generados.</string> |
| 194 | |||
| 195 | <!-- Debug settings strings --> | ||
| 196 | <string name="cpu">CPU</string> | ||
| 197 | <string name="cpu_debug_mode">Depuración de CPU</string> | ||
| 198 | <string name="cpu_debug_mode_description">Pone la CPU en un modo de depuración lento.</string> | ||
| 199 | <string name="gpu">GPU</string> | ||
| 200 | <string name="renderer_api">API</string> | ||
| 201 | <string name="renderer_debug">Depuración de gráficos</string> | ||
| 202 | <string name="renderer_debug_description">Configura la API gráfica a un modo de depuración lento.</string> | ||
| 203 | <string name="fastmem">Fastmem</string> | ||
| 150 | 204 | ||
| 151 | <!-- Audio settings strings --> | 205 | <!-- Audio settings strings --> |
| 206 | <string name="audio_output_engine">Motor de salida</string> | ||
| 152 | <string name="audio_volume">Volumen</string> | 207 | <string name="audio_volume">Volumen</string> |
| 153 | <string name="audio_volume_description">Especifica el volumen de la salida de audio.</string> | 208 | <string name="audio_volume_description">Especifica el volumen de la salida de audio.</string> |
| 154 | 209 | ||
| @@ -157,14 +212,24 @@ | |||
| 157 | <string name="ini_saved">Configuración guardada</string> | 212 | <string name="ini_saved">Configuración guardada</string> |
| 158 | <string name="gameid_saved">Configuración guardada para %1$s</string> | 213 | <string name="gameid_saved">Configuración guardada para %1$s</string> |
| 159 | <string name="error_saving">Error guardando %1$s.ini: %2$s</string> | 214 | <string name="error_saving">Error guardando %1$s.ini: %2$s</string> |
| 215 | <string name="unimplemented_menu">Menú sin implementar</string> | ||
| 160 | <string name="loading">Cargando...</string> | 216 | <string name="loading">Cargando...</string> |
| 217 | <string name="shutting_down">Saliendo...</string> | ||
| 161 | <string name="reset_setting_confirmation">¿Desea restablecer esta configuración a su valor predeterminado?</string> | 218 | <string name="reset_setting_confirmation">¿Desea restablecer esta configuración a su valor predeterminado?</string> |
| 162 | <string name="reset_to_default">Restablecer a predeterminado</string> | 219 | <string name="reset_to_default">Restablecer a predeterminado</string> |
| 163 | <string name="reset_all_settings">¿Restablecer todas las configuraciones?</string> | 220 | <string name="reset_all_settings">¿Restablecer todas las configuraciones?</string> |
| 164 | <string name="reset_all_settings_description">Todas las configuraciones avanzadas se restablecerán a su configuración predeterminada. Esto no se puede deshacer.</string> | 221 | <string name="reset_all_settings_description">Todas las opciones avanzadas se restablecerán a su configuración predeterminada. Esta acción no se puede deshacer.</string> |
| 165 | <string name="settings_reset">Reiniciar la configuracion</string> | 222 | <string name="settings_reset">Reiniciar la configuracion</string> |
| 166 | <string name="close">Cerrar</string> | 223 | <string name="close">Cerrar</string> |
| 167 | <string name="learn_more">Más información</string> | 224 | <string name="learn_more">Saber más</string> |
| 225 | <string name="auto">Auto</string> | ||
| 226 | <string name="submit">Enviar</string> | ||
| 227 | <string name="string_null">Null</string> | ||
| 228 | <string name="string_import">Importar</string> | ||
| 229 | <string name="export">Exportar</string> | ||
| 230 | <string name="export_failed">La exportación falló</string> | ||
| 231 | <string name="import_failed">La importación falló</string> | ||
| 232 | <string name="cancelling">Cancelando</string> | ||
| 168 | 233 | ||
| 169 | <!-- GPU driver installation --> | 234 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">Seleccionar driver GPU</string> | 235 | <string name="select_gpu_driver">Seleccionar driver GPU</string> |
| @@ -172,6 +237,7 @@ | |||
| 172 | <string name="select_gpu_driver_install">Instalar</string> | 237 | <string name="select_gpu_driver_install">Instalar</string> |
| 173 | <string name="select_gpu_driver_default">Predeterminado</string> | 238 | <string name="select_gpu_driver_default">Predeterminado</string> |
| 174 | <string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string> | 239 | <string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string> |
| 240 | <string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string> | ||
| 175 | <string name="system_gpu_driver">Driver GPU del sistema</string> | 241 | <string name="system_gpu_driver">Driver GPU del sistema</string> |
| 176 | <string name="installing_driver">Instalando driver...</string> | 242 | <string name="installing_driver">Instalando driver...</string> |
| 177 | 243 | ||
| @@ -182,10 +248,11 @@ | |||
| 182 | <string name="preferences_graphics">Gráficos</string> | 248 | <string name="preferences_graphics">Gráficos</string> |
| 183 | <string name="preferences_audio">Audio</string> | 249 | <string name="preferences_audio">Audio</string> |
| 184 | <string name="preferences_theme">Tema y color</string> | 250 | <string name="preferences_theme">Tema y color</string> |
| 251 | <string name="preferences_debug">Depuración</string> | ||
| 185 | 252 | ||
| 186 | <!-- ROM loading errors --> | 253 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">Su ROM está encriptada</string> | 254 | <string name="loader_error_encrypted">Su ROM está encriptada</string> |
| 188 | <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> | 255 | <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-physical-titles-game-cards\">cartuchos de juegos</a> o <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">tÃtulos instalados</a>.]]></string> |
| 189 | <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> | 256 | <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> |
| 190 | <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> | 257 | <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> |
| 191 | <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> | 258 | <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> |
| @@ -196,25 +263,25 @@ | |||
| 196 | <string name="emulation_exit">Salir de la emulación</string> | 263 | <string name="emulation_exit">Salir de la emulación</string> |
| 197 | <string name="emulation_done">Hecho</string> | 264 | <string name="emulation_done">Hecho</string> |
| 198 | <string name="emulation_fps_counter">Contador de FPS</string> | 265 | <string name="emulation_fps_counter">Contador de FPS</string> |
| 199 | <string name="emulation_toggle_controls">Alternar Controles</string> | 266 | <string name="emulation_toggle_controls">Alternar controles</string> |
| 200 | <string name="emulation_rel_stick_center">Centro Relativo del Stick</string> | 267 | <string name="emulation_rel_stick_center">Centro relativo del stick</string> |
| 201 | <string name="emulation_dpad_slide">Deslizamiento de la Cruceta</string> | 268 | <string name="emulation_dpad_slide">Deslizamiento de la cruceta</string> |
| 202 | <string name="emulation_haptics">Hápticos</string> | 269 | <string name="emulation_haptics">Toques hápticos</string> |
| 203 | <string name="emulation_show_overlay">Mostrar pantalla</string> | 270 | <string name="emulation_show_overlay">Mostrar overlay</string> |
| 204 | <string name="emulation_toggle_all">Alternar Todo</string> | 271 | <string name="emulation_toggle_all">Alternar todo</string> |
| 205 | <string name="emulation_control_adjust">Ajustar pantalla</string> | 272 | <string name="emulation_control_adjust">Ajustar overlay</string> |
| 206 | <string name="emulation_control_scale">Escala</string> | 273 | <string name="emulation_control_scale">Escala</string> |
| 207 | <string name="emulation_control_opacity">Opacidad</string> | 274 | <string name="emulation_control_opacity">Opacidad</string> |
| 208 | <string name="emulation_touch_overlay_reset">Reiniciar pantalla</string> | 275 | <string name="emulation_touch_overlay_reset">Reiniciar overlay</string> |
| 209 | <string name="emulation_touch_overlay_edit">Editar pantalla</string> | 276 | <string name="emulation_touch_overlay_edit">Editar overlay</string> |
| 210 | <string name="emulation_pause">Pausar Emulación</string> | 277 | <string name="emulation_pause">Pausar emulación</string> |
| 211 | <string name="emulation_unpause">Reanudar Emulación</string> | 278 | <string name="emulation_unpause">Despausar emulación</string> |
| 212 | <string name="emulation_input_overlay">Opciones de pantalla </string> | 279 | <string name="emulation_input_overlay">Opciones de overlay</string> |
| 213 | 280 | ||
| 214 | <string name="load_settings">Cargando configuración...</string> | 281 | <string name="load_settings">Cargando configuración...</string> |
| 215 | 282 | ||
| 216 | <!-- Software keyboard --> | 283 | <!-- Software keyboard --> |
| 217 | <string name="software_keyboard">Software del teclado</string> | 284 | <string name="software_keyboard">Teclado de software</string> |
| 218 | 285 | ||
| 219 | <!-- Errors and warnings --> | 286 | <!-- Errors and warnings --> |
| 220 | <string name="abort_button">Abortar</string> | 287 | <string name="abort_button">Abortar</string> |
| @@ -226,6 +293,9 @@ | |||
| 226 | <string name="fatal_error">Error fatal</string> | 293 | <string name="fatal_error">Error fatal</string> |
| 227 | <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> | 294 | <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> |
| 228 | <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> | 295 | <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> |
| 296 | <string name="device_memory_inadequate">RAM de dispositivo: %1$s\nRecomendado: %2$s</string> | ||
| 297 | <string name="memory_formatted">%1$s %2$s</string> | ||
| 298 | <string name="no_game_present">¡No hay ningún juego ejecutable presente!</string> | ||
| 229 | 299 | ||
| 230 | <!-- Region Names --> | 300 | <!-- Region Names --> |
| 231 | <string name="region_japan">Japón</string> | 301 | <string name="region_japan">Japón</string> |
| @@ -236,7 +306,14 @@ | |||
| 236 | <string name="region_korea">Corea</string> | 306 | <string name="region_korea">Corea</string> |
| 237 | <string name="region_taiwan">Taiwán</string> | 307 | <string name="region_taiwan">Taiwán</string> |
| 238 | 308 | ||
| 239 | <!-- Language Names --> | 309 | <!-- Memory Sizes --> |
| 310 | <string name="memory_byte">Byte</string> | ||
| 311 | <string name="memory_kilobyte">KB</string> | ||
| 312 | <string name="memory_megabyte">MB</string> | ||
| 313 | <string name="memory_gigabyte">GB</string> | ||
| 314 | <string name="memory_terabyte">TB</string> | ||
| 315 | <string name="memory_petabyte">PB</string> | ||
| 316 | <string name="memory_exabyte">EB</string> | ||
| 240 | 317 | ||
| 241 | <!-- Renderer APIs --> | 318 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">Vulkan</string> | 319 | <string name="renderer_vulkan">Vulkan</string> |
| @@ -274,6 +351,11 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">FXAA</string> | 351 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 275 | <string name="anti_aliasing_smaa">SMAA</string> | 352 | <string name="anti_aliasing_smaa">SMAA</string> |
| 276 | 353 | ||
| 354 | <!-- Screen Layouts --> | ||
| 355 | <string name="screen_layout_landscape">Paisaje</string> | ||
| 356 | <string name="screen_layout_portrait">Retrato</string> | ||
| 357 | <string name="screen_layout_auto">Auto</string> | ||
| 358 | |||
| 277 | <!-- Aspect Ratios --> | 359 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">Predeterminado (16:9)</string> | 360 | <string name="ratio_default">Predeterminado (16:9)</string> |
| 279 | <string name="ratio_force_four_three">Forzar 4:3</string> | 361 | <string name="ratio_force_four_three">Forzar 4:3</string> |
| @@ -298,7 +380,7 @@ | |||
| 298 | <string name="building_shaders">Construyendo shaders</string> | 380 | <string name="building_shaders">Construyendo shaders</string> |
| 299 | 381 | ||
| 300 | <!-- Theme options --> | 382 | <!-- Theme options --> |
| 301 | <string name="change_app_theme">Cambiar Tema</string> | 383 | <string name="change_app_theme">Cambiar tema</string> |
| 302 | <string name="theme_default">Predeterminado</string> | 384 | <string name="theme_default">Predeterminado</string> |
| 303 | <string name="theme_material_you">Material You</string> | 385 | <string name="theme_material_you">Material You</string> |
| 304 | 386 | ||
| @@ -308,8 +390,22 @@ | |||
| 308 | <string name="theme_mode_light">Claro</string> | 390 | <string name="theme_mode_light">Claro</string> |
| 309 | <string name="theme_mode_dark">Oscuro</string> | 391 | <string name="theme_mode_dark">Oscuro</string> |
| 310 | 392 | ||
| 393 | <!-- Audio output engines --> | ||
| 394 | <string name="cubeb">cubeb</string> | ||
| 395 | |||
| 311 | <!-- Black backgrounds theme --> | 396 | <!-- Black backgrounds theme --> |
| 312 | <string name="use_black_backgrounds">Usar Fondos Negros</string> | 397 | <string name="use_black_backgrounds">Fondos oscuros</string> |
| 313 | <string name="use_black_backgrounds_description">Cuando utilice el modo oscuro, aplique fondos negros.</string> | 398 | <string name="use_black_backgrounds_description">Cuando utilice el modo oscuro, aplique fondos negros.</string> |
| 314 | 399 | ||
| 315 | </resources> | 400 | <!-- Picture-In-Picture --> |
| 401 | <string name="picture_in_picture">Picture in Picture</string> | ||
| 402 | <string name="picture_in_picture_description">Minimizar ventana cuando esté en segundo plano</string> | ||
| 403 | <string name="pause">Pausar</string> | ||
| 404 | <string name="play">Jugar</string> | ||
| 405 | <string name="mute">Mutear</string> | ||
| 406 | <string name="unmute">Desmutear</string> | ||
| 407 | |||
| 408 | <!-- Licenses screen strings --> | ||
| 409 | <string name="licenses">Licencias</string> | ||
| 410 | <string name="license_fidelityfx_fsr_description">Upscaling de alta calidad de AMD</string> | ||
| 411 | </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 index 1e02828aa..5a827c50b 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.<br /><br />Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string> | 4 | <string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.<br /><br />Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">L\'émulation est active</string> | 5 | <string name="emulation_notification_channel_name">L\'émulation est active</string> |
| @@ -19,12 +19,13 @@ | |||
| 19 | <string name="games">Jeux</string> | 19 | <string name="games">Jeux</string> |
| 20 | <string name="games_description">Sélectionnez votre dossier <b>de Jeux</b> avec le bouton ci-dessous.</string> | 20 | <string name="games_description">Sélectionnez votre dossier <b>de Jeux</b> avec le bouton ci-dessous.</string> |
| 21 | <string name="done">Terminé</string> | 21 | <string name="done">Terminé</string> |
| 22 | <string name="done_description">Vous êtes prêt.\nProfitez de vos jeux !</string> | 22 | <string name="done_description">Vous êtes prêt.\nProfitez de vos jeux !</string> |
| 23 | <string name="text_continue">Continuer</string> | 23 | <string name="text_continue">Continuer</string> |
| 24 | <string name="next">Suivant</string> | 24 | <string name="next">Suivant</string> |
| 25 | <string name="back">Retour</string> | 25 | <string name="back">Retour</string> |
| 26 | <string name="add_games">Ajouter des jeux</string> | 26 | <string name="add_games">Ajouter des jeux</string> |
| 27 | <string name="add_games_description">Sélectionner votre dossier de jeux</string> | 27 | <string name="add_games_description">Sélectionner le dossier des jeux</string> |
| 28 | <string name="step_complete">Terminé !</string> | ||
| 28 | 29 | ||
| 29 | <!-- Home strings --> | 30 | <!-- Home strings --> |
| 30 | <string name="home_games">Jeux</string> | 31 | <string name="home_games">Jeux</string> |
| @@ -32,12 +33,13 @@ | |||
| 32 | <string name="home_settings">Paramètres</string> | 33 | <string name="home_settings">Paramètres</string> |
| 33 | <string name="empty_gamelist">Aucun fichier n\'a été trouvé ou aucun répertoire de jeu n\'a encore été sélectionné.</string> | 34 | <string name="empty_gamelist">Aucun fichier n\'a été trouvé ou aucun répertoire de jeu n\'a encore été sélectionné.</string> |
| 34 | <string name="search_and_filter_games">Rechercher et filtrer les jeux</string> | 35 | <string name="search_and_filter_games">Rechercher et filtrer les jeux</string> |
| 35 | <string name="select_games_folder">Sélectionner le dossier de jeux</string> | 36 | <string name="select_games_folder">Sélectionner le dossier des jeux</string> |
| 36 | <string name="select_games_folder_description">Permet à yuzu de remplir la liste des jeux</string> | 37 | <string name="select_games_folder_description">Permet à yuzu de remplir la liste des jeux</string> |
| 37 | <string name="add_games_warning">Ne pas sélectionner le dossier des jeux ?</string> | 38 | <string name="add_games_warning">Ne pas sélectionner le dossier des jeux ?</string> |
| 38 | <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> | 39 | <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> |
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 40 | <string name="home_search_games">Rechercher des jeux</string> | 41 | <string name="home_search_games">Rechercher des jeux</string> |
| 42 | <string name="search_settings">Rechercher un paramètre</string> | ||
| 41 | <string name="games_dir_selected">Répertoire de jeux sélectionné</string> | 43 | <string name="games_dir_selected">Répertoire de jeux sélectionné</string> |
| 42 | <string name="install_prod_keys">Installer prod.keys</string> | 44 | <string name="install_prod_keys">Installer prod.keys</string> |
| 43 | <string name="install_prod_keys_description">Nécessaire pour décrypter les jeux commerciaux.</string> | 45 | <string name="install_prod_keys_description">Nécessaire pour décrypter les jeux commerciaux.</string> |
| @@ -46,7 +48,7 @@ | |||
| 46 | <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> | 48 | <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> |
| 47 | <string name="notifications">Notifications</string> | 49 | <string name="notifications">Notifications</string> |
| 48 | <string name="notifications_description">Accordez l\'autorisation de notification avec le bouton ci-dessous.</string> | 50 | <string name="notifications_description">Accordez l\'autorisation de notification avec le bouton ci-dessous.</string> |
| 49 | <string name="give_permission">Donner la permission</string> | 51 | <string name="give_permission">Accorder la permission</string> |
| 50 | <string name="notification_warning">Ne pas accorder la permission de notification ?</string> | 52 | <string name="notification_warning">Ne pas accorder la permission de notification ?</string> |
| 51 | <string name="notification_warning_description">yuzu ne pourra pas vous communiquer d\'informations importantes.</string> | 53 | <string name="notification_warning_description">yuzu ne pourra pas vous communiquer d\'informations importantes.</string> |
| 52 | <string name="permission_denied">Permission refusée</string> | 54 | <string name="permission_denied">Permission refusée</string> |
| @@ -61,12 +63,15 @@ | |||
| 61 | <string name="invalid_keys_file">Fichier de clés sélectionné invalide</string> | 63 | <string name="invalid_keys_file">Fichier de clés sélectionné invalide</string> |
| 62 | <string name="install_keys_success">Clés installées avec succès</string> | 64 | <string name="install_keys_success">Clés installées avec succès</string> |
| 63 | <string name="reading_keys_failure">Erreur lors de la lecture des clés de chiffrement</string> | 65 | <string name="reading_keys_failure">Erreur lors de la lecture des clés de chiffrement</string> |
| 66 | <string name="install_prod_keys_failure_extension_description">Vérifiez que votre fichier de clés a une extension .keys et réessayez.</string> | ||
| 67 | <string name="install_amiibo_keys_failure_extension_description">Vérifiez que votre fichier de clés a une extension .bin et réessayez.</string> | ||
| 64 | <string name="invalid_keys_error">Clés de chiffrement invalides</string> | 68 | <string name="invalid_keys_error">Clés de chiffrement invalides</string> |
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 69 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">Le fichier sélectionné est incorrect ou corrompu. Veuillez dumper à nouveau vos clés.</string> | 70 | <string name="install_keys_failure_description">Le fichier sélectionné est incorrect ou corrompu. Veuillez dumper à nouveau vos clés.</string> |
| 67 | <string name="install_gpu_driver">Installer le pilote du GPU</string> | 71 | <string name="install_gpu_driver">Installer le pilote du GPU</string> |
| 68 | <string name="install_gpu_driver_description">Installez des pilotes alternatifs pour des performances ou une précision potentiellement meilleures</string> | 72 | <string name="install_gpu_driver_description">Installer des pilotes alternatifs pour des performances ou une précision potentiellement meilleures</string> |
| 69 | <string name="advanced_settings">Paramètres avancés</string> | 73 | <string name="advanced_settings">Paramètres avancés</string> |
| 74 | <string name="advanced_settings_game">Paramètres avancés : %1$s</string> | ||
| 70 | <string name="settings_description">Configurer les paramètres de l\'émulateur</string> | 75 | <string name="settings_description">Configurer les paramètres de l\'émulateur</string> |
| 71 | <string name="search_recently_played">Joué récemment</string> | 76 | <string name="search_recently_played">Joué récemment</string> |
| 72 | <string name="search_recently_added">Ajouté récemment</string> | 77 | <string name="search_recently_added">Ajouté récemment</string> |
| @@ -86,6 +91,33 @@ | |||
| 86 | <string name="save_file_invalid_zip_structure_description">Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu.</string> | 91 | <string name="save_file_invalid_zip_structure_description">Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu.</string> |
| 87 | <string name="import_saves">Importer</string> | 92 | <string name="import_saves">Importer</string> |
| 88 | <string name="export_saves">Exporter</string> | 93 | <string name="export_saves">Exporter</string> |
| 94 | <string name="install_firmware">Installer le firmware</string> | ||
| 95 | <string name="install_firmware_description">Le firmware doit être dans une archive ZIP et est nécessaire pour démarrer certains jeux.</string> | ||
| 96 | <string name="firmware_installing">Installation du firmware</string> | ||
| 97 | <string name="firmware_installed_success">Firmware installé avec succès</string> | ||
| 98 | <string name="firmware_installed_failure">L\'installation du firmware a échoué</string> | ||
| 99 | <string name="firmware_installed_failure_description">Assurez-vous que les fichiers NCA du firmware se trouvent à la racine du fichier ZIP, puis réessayez.</string> | ||
| 100 | <string name="share_log">Partager les logs de débogage</string> | ||
| 101 | <string name="share_log_description">Partagez le fichier de log de yuzu pour déboguer les problèmes.</string> | ||
| 102 | <string name="share_log_missing">Aucun fichier de log trouvé</string> | ||
| 103 | <string name="install_game_content">Installer le contenu du jeu</string> | ||
| 104 | <string name="install_game_content_description">Installer une mise à jour ou un DLC</string> | ||
| 105 | <string name="installing_game_content">Installation du contenu en cours...</string> | ||
| 106 | <string name="install_game_content_failure">Erreur lors de l\'installation du fichier dans la NAND</string> | ||
| 107 | <string name="install_game_content_failure_description">Veuillez vous assurer que le contenu est valide et que le fichier prod.keys est installé.</string> | ||
| 108 | <string name="install_game_content_failure_base">L\'installation de jeux de base n\'est pas autorisée afin d\'éviter d\'éventuels conflits.</string> | ||
| 109 | <string name="install_game_content_failure_file_extension">Seuls les contenus NSP et XCI sont pris en charge. Veuillez vérifier que le contenu du jeu est valide.</string> | ||
| 110 | <string name="install_game_content_failed_count">%1$d erreur(s) d\'installation</string> | ||
| 111 | <string name="install_game_content_success">Contenu du jeu installé avec succès</string> | ||
| 112 | <string name="install_game_content_success_install">%1$d installé avec succès</string> | ||
| 113 | <string name="install_game_content_success_overwrite">%1$d écrasé avec succès</string> | ||
| 114 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 115 | <string name="custom_driver_not_supported">Pilotes personnalisés non supporté</string> | ||
| 116 | <string name="custom_driver_not_supported_description">Le chargement des pilotes personnalisés ne sont pas actuellement pris en charge pour ce périphérique. Vérifiez à nouveau cette option à l\'avenir pour voir si la prise en charge a été ajoutée !</string> | ||
| 117 | <string name="manage_yuzu_data">Gérer les données de yuzu</string> | ||
| 118 | <string name="manage_yuzu_data_description">Importer/exporter le firmware, les clés, les données utilisateur, et bien plus encore !</string> | ||
| 119 | <string name="share_save_file">Partager le fichier de sauvegarde</string> | ||
| 120 | <string name="export_save_failed">Échec de l\'exportation de la sauvegarde</string> | ||
| 89 | 121 | ||
| 90 | <!-- About screen strings --> | 122 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">Gaia n\'est pas réel</string> | 123 | <string name="gaia_is_not_real">Gaia n\'est pas réel</string> |
| @@ -94,7 +126,18 @@ | |||
| 94 | <string name="contributors">Contributeurs</string> | 126 | <string name="contributors">Contributeurs</string> |
| 95 | <string name="contributors_description">Fait avec \u2764 de l\'équipe yuzu</string> | 127 | <string name="contributors_description">Fait avec \u2764 de l\'équipe yuzu</string> |
| 96 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 128 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 129 | <string name="licenses_description">Des projets qui rendent possible yuzu pour Android</string> | ||
| 97 | <string name="build">Build</string> | 130 | <string name="build">Build</string> |
| 131 | <string name="user_data">Données utilisateur</string> | ||
| 132 | <string name="user_data_description">Importer/exporter toutes les données de l\'application.\n\nLors de l\'importation des données utilisateur, toutes les données utilisateur existantes seront supprimées !</string> | ||
| 133 | <string name="exporting_user_data">Exportation des données utilisateur...</string> | ||
| 134 | <string name="importing_user_data">Importation des données utilisateur...</string> | ||
| 135 | <string name="import_user_data">Importer des données utilisateur</string> | ||
| 136 | <string name="invalid_yuzu_backup">Backup yuzu invalide</string> | ||
| 137 | <string name="user_data_export_success">Les données utilisateur ont été exportés avec succès</string> | ||
| 138 | <string name="user_data_import_success">Les données utilisateur ont été importées avec succès</string> | ||
| 139 | <string name="user_data_export_cancelled">Exportation annulée</string> | ||
| 140 | <string name="user_data_import_failed_description">Assurez-vous que les dossiers de données utilisateur se trouvent à la racine du dossier ZIP et contiennent un fichier de configuration à config/config.ini, puis réessayez.</string> | ||
| 98 | <string name="support_link">https://discord.gg/u77vRWY</string> | 141 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 99 | <string name="website_link">https://yuzu-emu.org/</string> | 142 | <string name="website_link">https://yuzu-emu.org/</string> |
| 100 | <string name="github_link">https://github.com/yuzu-emu</string> | 143 | <string name="github_link">https://github.com/yuzu-emu</string> |
| @@ -114,64 +157,87 @@ | |||
| 114 | <string name="are_you_interested">Es tu intéressé ?</string> | 157 | <string name="are_you_interested">Es tu intéressé ?</string> |
| 115 | 158 | ||
| 116 | <!-- General settings strings --> | 159 | <!-- General settings strings --> |
| 117 | <string name="frame_limit_enable">Activer la vitesse limite</string> | 160 | <string name="frame_limit_enable">Limitation de vitesse</string> |
| 118 | <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> | 161 | <string name="frame_limit_enable_description">Limiter la vitesse d\'émulation à un pourcentage spécifié de la vitesse normale</string> |
| 119 | <string name="frame_limit_slider">Limite en pourcentage de vitesse</string> | 162 | <string name="frame_limit_slider">Limite en pourcentage de vitesse</string> |
| 120 | <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> | 163 | <string name="frame_limit_slider_description">Spécifier le pourcentage pour limiter la vitesse d\'émulation. 100% correspond à la vitesse normale. Des valeurs plus élevées ou plus basses augmenteront ou diminueront la limite de vitesse.</string> |
| 121 | <string name="cpu_accuracy">Précision du CPU</string> | 164 | <string name="cpu_accuracy">Précision du CPU</string> |
| 165 | <string name="value_with_units">%1$s%2$s</string> | ||
| 122 | 166 | ||
| 123 | <!-- System settings strings --> | 167 | <!-- System settings strings --> |
| 124 | <string name="use_docked_mode">Mode TV</string> | 168 | <string name="use_docked_mode">Mode TV</string> |
| 125 | <string name="use_docked_mode_description">Émuler en mode TV augmente la résolution au détriment des performances.</string> | 169 | <string name="use_docked_mode_description">Augmenter la résolution, ce qui diminue les performances. Le mode portable est utilisé lorsque la fonction est désactivée, ce qui réduit la résolution et améliore les performances.</string> |
| 126 | <string name="emulated_region">Région émulée</string> | 170 | <string name="emulated_region">Région émulée</string> |
| 127 | <string name="emulated_language">Langue émulée</string> | 171 | <string name="emulated_language">Langue émulée</string> |
| 128 | <string name="select_rtc_date">Sélectionner la date RTC</string> | 172 | <string name="select_rtc_date">Sélectionner la date RTC</string> |
| 129 | <string name="select_rtc_time">Sélectionner l\'heure RTC</string> | 173 | <string name="select_rtc_time">Sélectionner l\'heure RTC</string> |
| 130 | <string name="use_custom_rtc">Activer l\'horloge RTC personnalisée</string> | 174 | <string name="use_custom_rtc">RTC personnalisé</string> |
| 131 | <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> | 175 | <string name="use_custom_rtc_description">Vous permet de définir une horloge en temps réel personnalisée distincte de l\'heure actuelle de votre système.</string> |
| 132 | <string name="set_custom_rtc">Définir l\'horloge RTC personnalisée</string> | 176 | <string name="set_custom_rtc">Définir l\'horloge RTC personnalisée</string> |
| 133 | 177 | ||
| 134 | <!-- Graphics settings strings --> | 178 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">API</string> | ||
| 136 | <string name="renderer_accuracy">Niveau de précision</string> | 179 | <string name="renderer_accuracy">Niveau de précision</string> |
| 137 | <string name="renderer_resolution">Résolution</string> | 180 | <string name="renderer_resolution">Résolution (Mode Portable/Mode TV)</string> |
| 138 | <string name="renderer_vsync">Mode VSync</string> | 181 | <string name="renderer_vsync">Mode VSync</string> |
| 182 | <string name="renderer_screen_layout">Orientation</string> | ||
| 139 | <string name="renderer_aspect_ratio">Format</string> | 183 | <string name="renderer_aspect_ratio">Format</string> |
| 140 | <string name="renderer_scaling_filter">Filtre de fenêtre adaptatif</string> | 184 | <string name="renderer_scaling_filter">Filtre de fenêtre adaptatif</string> |
| 141 | <string name="renderer_anti_aliasing">Méthode d\'anticrénelage :</string> | 185 | <string name="renderer_anti_aliasing">Méthode d\'anticrénelage</string> |
| 142 | <string name="renderer_force_max_clock">Forcer la fréquence d\'horloge maximale (Adreno uniquement)</string> | 186 | <string name="renderer_force_max_clock">Forcer les fréquences maximales (Adreno uniquement)</string> |
| 143 | <string name="renderer_force_max_clock_description">Force le GPU à fonctionner au maximum d\'horloges possibles (les contraintes thermiques seront toujours appliquées).</string> | 187 | <string name="renderer_force_max_clock_description">Forcer le GPU à fonctionner à ses fréquences maximales possibles (les contraintes thermiques seront toujours appliquées).</string> |
| 144 | <string name="renderer_asynchronous_shaders">Utiliser les shaders asynchrones</string> | 188 | <string name="renderer_asynchronous_shaders">Utiliser les shaders asynchrones</string> |
| 145 | <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> | 189 | <string name="renderer_asynchronous_shaders_description">Compile les shaders de manière asynchrone, réduisant les saccades mais pouvant entraîner des problèmes visuels.</string> |
| 146 | <string name="renderer_debug">Activer le débogage des graphismes</string> | 190 | <string name="renderer_reactive_flushing">Utiliser le vidage réactif</string> |
| 147 | <string name="renderer_debug_description">Lorsque cette case est cochée, l\'API graphique entre dans un mode de débogage plus lent.</string> | 191 | <string name="renderer_reactive_flushing_description">Améliore la précision du rendu dans certains jeux au détriment des performances.</string> |
| 148 | <string name="use_disk_shader_cache">Utiliser les shader cache de disque</string> | 192 | <string name="use_disk_shader_cache">Utiliser les shader cache</string> |
| 149 | <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> | 193 | <string name="use_disk_shader_cache_description">Réduire les saccades en stockant et en chargeant localement les shaders générés</string> |
| 194 | |||
| 195 | <!-- Debug settings strings --> | ||
| 196 | <string name="cpu">CPU</string> | ||
| 197 | <string name="cpu_debug_mode">Débogage du CPU</string> | ||
| 198 | <string name="cpu_debug_mode_description">Place le CPU en mode lent de débogage.</string> | ||
| 199 | <string name="gpu">GPU</string> | ||
| 200 | <string name="renderer_api">API</string> | ||
| 201 | <string name="renderer_debug">Débogage des graphismes</string> | ||
| 202 | <string name="renderer_debug_description">Définit l\'API graphique en mode de débogage lent.</string> | ||
| 203 | <string name="fastmem">Fastmem</string> | ||
| 150 | 204 | ||
| 151 | <!-- Audio settings strings --> | 205 | <!-- Audio settings strings --> |
| 206 | <string name="audio_output_engine">Moteur de sortie</string> | ||
| 152 | <string name="audio_volume">Volume</string> | 207 | <string name="audio_volume">Volume</string> |
| 153 | <string name="audio_volume_description">Spécifie le volume de la sortie audio.</string> | 208 | <string name="audio_volume_description">Spécifier le volume de la sortie audio.</string> |
| 154 | 209 | ||
| 155 | <!-- Miscellaneous --> | 210 | <!-- Miscellaneous --> |
| 156 | <string name="slider_default">Défaut</string> | 211 | <string name="slider_default">Par défaut</string> |
| 157 | <string name="ini_saved">Paramètres enregistrés</string> | 212 | <string name="ini_saved">Paramètres enregistrés</string> |
| 158 | <string name="gameid_saved">Paramètres enregistrés pour %1$s</string> | 213 | <string name="gameid_saved">Paramètres enregistrés pour %1$s</string> |
| 159 | <string name="error_saving">Erreur lors de l\'enregistrement de %1$s.ini: %2$s</string> | 214 | <string name="error_saving">Erreur lors de l\'enregistrement de %1$s.ini: %2$s</string> |
| 215 | <string name="unimplemented_menu">Menu non implémenté</string> | ||
| 160 | <string name="loading">Chargement...</string> | 216 | <string name="loading">Chargement...</string> |
| 161 | <string name="reset_setting_confirmation">Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ?</string> | 217 | <string name="shutting_down">Extinction en cours...</string> |
| 218 | <string name="reset_setting_confirmation">Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ?</string> | ||
| 162 | <string name="reset_to_default">Réinitialiser par défaut</string> | 219 | <string name="reset_to_default">Réinitialiser par défaut</string> |
| 163 | <string name="reset_all_settings">Réinitialiser tous les réglages ?</string> | 220 | <string name="reset_all_settings">Réinitialiser tous les réglages ?</string> |
| 164 | <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> | 221 | <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> |
| 165 | <string name="settings_reset">Paramètres réinitialisés</string> | 222 | <string name="settings_reset">Paramètres réinitialisés</string> |
| 166 | <string name="close">Fermer</string> | 223 | <string name="close">Fermer</string> |
| 167 | <string name="learn_more">Plus d\'informations</string> | 224 | <string name="learn_more">En savoir plus</string> |
| 225 | <string name="auto">Auto</string> | ||
| 226 | <string name="submit">Soumettre</string> | ||
| 227 | <string name="string_null">Nul</string> | ||
| 228 | <string name="string_import">Importer</string> | ||
| 229 | <string name="export">Exporter</string> | ||
| 230 | <string name="export_failed">L\'exportation a échoué</string> | ||
| 231 | <string name="import_failed">L\'importation a échoué</string> | ||
| 232 | <string name="cancelling">Annulation</string> | ||
| 168 | 233 | ||
| 169 | <!-- GPU driver installation --> | 234 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">Sélectionner le pilote du GPU</string> | 235 | <string name="select_gpu_driver">Sélectionner le pilote du GPU</string> |
| 171 | <string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string> | 236 | <string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string> |
| 172 | <string name="select_gpu_driver_install">Installer</string> | 237 | <string name="select_gpu_driver_install">Installer</string> |
| 173 | <string name="select_gpu_driver_default">Défaut</string> | 238 | <string name="select_gpu_driver_default">Par défaut</string> |
| 174 | <string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string> | 239 | <string name="select_gpu_driver_use_default">Utilisation du pilote du GPU par défaut</string> |
| 240 | <string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string> | ||
| 175 | <string name="system_gpu_driver">Pilote du GPU du système</string> | 241 | <string name="system_gpu_driver">Pilote du GPU du système</string> |
| 176 | <string name="installing_driver">Installation du pilote...</string> | 242 | <string name="installing_driver">Installation du pilote...</string> |
| 177 | 243 | ||
| @@ -182,13 +248,14 @@ | |||
| 182 | <string name="preferences_graphics">Vidéo</string> | 248 | <string name="preferences_graphics">Vidéo</string> |
| 183 | <string name="preferences_audio">Audio</string> | 249 | <string name="preferences_audio">Audio</string> |
| 184 | <string name="preferences_theme">Thème et couleur</string> | 250 | <string name="preferences_theme">Thème et couleur</string> |
| 251 | <string name="preferences_debug">Débogage</string> | ||
| 185 | 252 | ||
| 186 | <!-- ROM loading errors --> | 253 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">Votre ROM est cryptée</string> | 254 | <string name="loader_error_encrypted">Votre ROM est cryptée</string> |
| 188 | <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> | 255 | <string name="loader_error_encrypted_roms_description"><![CDATA[Veuillez suivre les guides pour refaire un dump de vos <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">cartouches de jeu</a> ou de vos <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">titres installés</a>.]]></string> |
| 189 | <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> | 256 | <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> |
| 190 | <string name="loader_error_video_core">Une erreur s\'est produite lors de l\'initialisation du noyau vidéo</string> | 257 | <string name="loader_error_video_core">Une erreur s\'est produite lors de l\'initialisation du noyau vidéo</string> |
| 191 | <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> | 258 | <string name="loader_error_video_core_description">Cela est généralement dû à un pilote GPU incompatible. L\'installation d\'un pilote GPU personnalisé peut résoudre ce problème.</string> |
| 192 | <string name="loader_error_invalid_format">Impossible de charger la ROM</string> | 259 | <string name="loader_error_invalid_format">Impossible de charger la ROM</string> |
| 193 | <string name="loader_error_file_not_found">Le fichier ROM n\'existe pas</string> | 260 | <string name="loader_error_file_not_found">Le fichier ROM n\'existe pas</string> |
| 194 | 261 | ||
| @@ -198,8 +265,8 @@ | |||
| 198 | <string name="emulation_fps_counter">Compteur FPS</string> | 265 | <string name="emulation_fps_counter">Compteur FPS</string> |
| 199 | <string name="emulation_toggle_controls">Activer/Désactiver les contrôles</string> | 266 | <string name="emulation_toggle_controls">Activer/Désactiver les contrôles</string> |
| 200 | <string name="emulation_rel_stick_center">Centre du stick relatif</string> | 267 | <string name="emulation_rel_stick_center">Centre du stick relatif</string> |
| 201 | <string name="emulation_dpad_slide">Glissement du DPad</string> | 268 | <string name="emulation_dpad_slide">Glissement du D-pad</string> |
| 202 | <string name="emulation_haptics">Haptique</string> | 269 | <string name="emulation_haptics">Toucher haptique</string> |
| 203 | <string name="emulation_show_overlay">Afficher l\'overlay</string> | 270 | <string name="emulation_show_overlay">Afficher l\'overlay</string> |
| 204 | <string name="emulation_toggle_all">Tout basculer</string> | 271 | <string name="emulation_toggle_all">Tout basculer</string> |
| 205 | <string name="emulation_control_adjust">Ajuster l\'overlay</string> | 272 | <string name="emulation_control_adjust">Ajuster l\'overlay</string> |
| @@ -225,7 +292,10 @@ | |||
| 225 | <string name="save_load_error">Erreur de sauvegarde/chargement</string> | 292 | <string name="save_load_error">Erreur de sauvegarde/chargement</string> |
| 226 | <string name="fatal_error">Erreur fatale</string> | 293 | <string name="fatal_error">Erreur fatale</string> |
| 227 | <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> | 294 | <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> |
| 228 | <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> | 295 | <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> |
| 296 | <string name="device_memory_inadequate">Mémoire RAM de l\'appareil : %1$s\nRecommandé : %2$s</string> | ||
| 297 | <string name="memory_formatted">%1$s %2$s</string> | ||
| 298 | <string name="no_game_present">Aucun jeu démarreable présent !</string> | ||
| 229 | 299 | ||
| 230 | <!-- Region Names --> | 300 | <!-- Region Names --> |
| 231 | <string name="region_japan">Japon</string> | 301 | <string name="region_japan">Japon</string> |
| @@ -236,7 +306,14 @@ | |||
| 236 | <string name="region_korea">Corée</string> | 306 | <string name="region_korea">Corée</string> |
| 237 | <string name="region_taiwan">Taïwan</string> | 307 | <string name="region_taiwan">Taïwan</string> |
| 238 | 308 | ||
| 239 | <!-- Language Names --> | 309 | <!-- Memory Sizes --> |
| 310 | <string name="memory_byte">Octet</string> | ||
| 311 | <string name="memory_kilobyte">Ko</string> | ||
| 312 | <string name="memory_megabyte">Mo</string> | ||
| 313 | <string name="memory_gigabyte">GB</string> | ||
| 314 | <string name="memory_terabyte">To</string> | ||
| 315 | <string name="memory_petabyte">Po</string> | ||
| 316 | <string name="memory_exabyte">Eo</string> | ||
| 240 | 317 | ||
| 241 | <!-- Renderer APIs --> | 318 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">Vulkan</string> | 319 | <string name="renderer_vulkan">Vulkan</string> |
| @@ -274,6 +351,11 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">FXAA</string> | 351 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 275 | <string name="anti_aliasing_smaa">SMAA</string> | 352 | <string name="anti_aliasing_smaa">SMAA</string> |
| 276 | 353 | ||
| 354 | <!-- Screen Layouts --> | ||
| 355 | <string name="screen_layout_landscape">Paysage</string> | ||
| 356 | <string name="screen_layout_portrait">Portrait</string> | ||
| 357 | <string name="screen_layout_auto">Auto</string> | ||
| 358 | |||
| 277 | <!-- Aspect Ratios --> | 359 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">Par défaut (16:9)</string> | 360 | <string name="ratio_default">Par défaut (16:9)</string> |
| 279 | <string name="ratio_force_four_three">Forcer le 4:3</string> | 361 | <string name="ratio_force_four_three">Forcer le 4:3</string> |
| @@ -288,8 +370,8 @@ | |||
| 288 | 370 | ||
| 289 | <!-- Gamepad Buttons --> | 371 | <!-- Gamepad Buttons --> |
| 290 | <string name="gamepad_d_pad">Pavé directionnel</string> | 372 | <string name="gamepad_d_pad">Pavé directionnel</string> |
| 291 | <string name="gamepad_left_stick">Stick Gauche</string> | 373 | <string name="gamepad_left_stick">Stick gauche</string> |
| 292 | <string name="gamepad_right_stick">Stick Droit</string> | 374 | <string name="gamepad_right_stick">Stick droit</string> |
| 293 | <string name="gamepad_home">Home</string> | 375 | <string name="gamepad_home">Home</string> |
| 294 | <string name="gamepad_screenshot">Capture d\'écran</string> | 376 | <string name="gamepad_screenshot">Capture d\'écran</string> |
| 295 | 377 | ||
| @@ -299,7 +381,7 @@ | |||
| 299 | 381 | ||
| 300 | <!-- Theme options --> | 382 | <!-- Theme options --> |
| 301 | <string name="change_app_theme">Changer le thème de l\'application</string> | 383 | <string name="change_app_theme">Changer le thème de l\'application</string> |
| 302 | <string name="theme_default">Défaut</string> | 384 | <string name="theme_default">Par défaut</string> |
| 303 | <string name="theme_material_you">Material You</string> | 385 | <string name="theme_material_you">Material You</string> |
| 304 | 386 | ||
| 305 | <!-- Theme Modes --> | 387 | <!-- Theme Modes --> |
| @@ -308,8 +390,22 @@ | |||
| 308 | <string name="theme_mode_light">Lumineux</string> | 390 | <string name="theme_mode_light">Lumineux</string> |
| 309 | <string name="theme_mode_dark">Sombre</string> | 391 | <string name="theme_mode_dark">Sombre</string> |
| 310 | 392 | ||
| 311 | <!-- Black backgrounds theme --> | 393 | <!-- Audio output engines --> |
| 312 | <string name="use_black_backgrounds">Utiliser des arrière-plans noirs</string> | 394 | <string name="cubeb">cubeb</string> |
| 313 | <string name="use_black_backgrounds_description">Lorsque vous utilisez le thème sombre, appliquer des arrière-plans noirs.</string> | ||
| 314 | 395 | ||
| 315 | </resources> | 396 | <!-- Black backgrounds theme --> |
| 397 | <string name="use_black_backgrounds">Arrière-plan noir</string> | ||
| 398 | <string name="use_black_backgrounds_description">Lorsque vous utilisez le thème sombre, appliquer un arrière-plan noir.</string> | ||
| 399 | |||
| 400 | <!-- Picture-In-Picture --> | ||
| 401 | <string name="picture_in_picture">Lecteur réduit</string> | ||
| 402 | <string name="picture_in_picture_description">Réduire la fenêtre lorsqu\'elle est placée en arrière-plan</string> | ||
| 403 | <string name="pause">Pause</string> | ||
| 404 | <string name="play">Jouer</string> | ||
| 405 | <string name="mute">Couper le son</string> | ||
| 406 | <string name="unmute">Remettre le son</string> | ||
| 407 | |||
| 408 | <!-- Licenses screen strings --> | ||
| 409 | <string name="licenses">Licences</string> | ||
| 410 | <string name="license_fidelityfx_fsr_description">Mise à l\'échelle de haute qualité par AMD.</string> | ||
| 411 | </resources> | ||
diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml new file mode 100644 index 000000000..0af78a57c --- /dev/null +++ b/src/android/app/src/main/res/values-he/strings.xml | |||
| @@ -0,0 +1,367 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | ||
| 3 | |||
| 4 | <string name="app_disclaimer">×”×ª×•×›× ×” תריץ ×ž×©×—×§×™× ×œ×§×•× ×¡×•×œ×ª ×” Nintendo Switch. ××£ משחק ×ו ×§×‘×¦×™× ×‘×¢×œ×™ זכויות ×™×•×¦×¨×™× × ×›×œ×œ×™×.<br /><br /> ×œ×¤× ×™ ש×ת/×” מתחיל בבקשה ×ž×¦× ×ת קובץ <![CDATA[<b>prod.keys</b>]]> על המכשיר.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">×§×¨× ×¢×•×“</a>]]></string> | ||
| 5 | <string name="emulation_notification_channel_name">×מולציה פעילה</string> | ||
| 6 | <string name="emulation_notification_channel_description">מציג התר××” מתמשכת ×›×שר ×”×מולציה פועלת.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu רץ</string> | ||
| 8 | <string name="notice_notification_channel_name">התר×ות ותקלות</string> | ||
| 9 | <string name="notice_notification_channel_description">מציג התר×ות ×›×שר משהו הולך ×œ× ×›×©×•×¨×”.</string> | ||
| 10 | <string name="notification_permission_not_granted">הרש×ות התר×ות ×œ× × ×™×ª× ×”!</string> | ||
| 11 | |||
| 12 | <!-- Setup strings --> | ||
| 13 | <string name="welcome">×‘×¨×•×›×™× ×”×‘××™×!</string> | ||
| 14 | <string name="welcome_description">למד ×יך להפעיל <b>yuzu</b> וקפוץ ישר ל×מולציה.</string> | ||
| 15 | <string name="get_started">כדי להתחיל</string> | ||
| 16 | <string name="keys">מפתחות</string> | ||
| 17 | <string name="keys_description">בחר ×ת קובץ ×” <b>prod.keys</b> שלך ×¢× ×”×›×¤×ª×•×¨ למטה.</string> | ||
| 18 | <string name="select_keys">בחר מפתחות</string> | ||
| 19 | <string name="games">משחקי×</string> | ||
| 20 | <string name="games_description">בחר ×ת התיקיית ×” <b>Games</b> שלך ×¢× ×”×›×¤×ª×•×¨ למטה.</string> | ||
| 21 | <string name="done">סיו×</string> | ||
| 22 | <string name="done_description">×ת/×” מוכן. \n×ª×”× ×”/×™ ×ž×”×ž×©×—×§×™× ×©×œ×š </string> | ||
| 23 | <string name="text_continue">המשך</string> | ||
| 24 | <string name="next">הב×</string> | ||
| 25 | <string name="back">×חורה</string> | ||
| 26 | <string name="add_games">הוסף משחקי×</string> | ||
| 27 | <string name="add_games_description">בחר/×™ ×ת תיקיית ×”×ž×©×—×§×™× ×©×œ×š</string> | ||
| 28 | <string name="step_complete">הושל×!</string> | ||
| 29 | |||
| 30 | <!-- Home strings --> | ||
| 31 | <string name="home_games">משחקי×</string> | ||
| 32 | <string name="home_search">חפש</string> | ||
| 33 | <string name="home_settings">הגדרות</string> | ||
| 34 | <string name="empty_gamelist">×œ× × ×ž×¦×ו ×§×‘×¦×™× ×ו ×œ× ×‘×—×¨×” ספריית ×§×‘×¦×™× ×‘×™× ×ª×™×™×.</string> | ||
| 35 | <string name="search_and_filter_games">חפש ×•×¡× ×Ÿ משחקי×</string> | ||
| 36 | <string name="select_games_folder">בחר תיקיית משחקי×</string> | ||
| 37 | <string name="select_games_folder_description">×פשר ל yuzu ל×כלס ×ת רשימת המשחקי×</string> | ||
| 38 | <string name="add_games_warning">לדלג על בחירת תיקיית המשחקי×?</string> | ||
| 39 | <string name="add_games_warning_description">×ž×©×—×§×™× ×œ× ×™×•×¦×’×• ברשימת ×”×ž×©×—×§×™× ×× ×œ× ×‘×—×¨×” תיקיית משחקי×.</string> | ||
| 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | ||
| 41 | <string name="home_search_games">חפש משחקי×</string> | ||
| 42 | <string name="search_settings">חפש בהגדרות</string> | ||
| 43 | <string name="games_dir_selected">ספריית ×ž×©×—×§×™× × ×‘×—×¨×”</string> | ||
| 44 | <string name="install_prod_keys">התקן prod.keys</string> | ||
| 45 | <string name="install_prod_keys_description">הכרחי בכדי ×œ×¤×¢× ×— משחקי×</string> | ||
| 46 | <string name="install_prod_keys_warning">לדלג על הוספת מפתחות?</string> | ||
| 47 | <string name="install_prod_keys_warning_description">מפתחות ×—×•×§×™×™× ×”×›×¨×—×™×™× ×›×“×™ לשחק במשחקי×. רק ×פליקציות פירטיות יפעלו ×× ×ª×ž×©×™×š.</string> | ||
| 48 | <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> | ||
| 49 | <string name="notifications">התר×ות</string> | ||
| 50 | <string name="notifications_description">תן גישה להתר×ות ×¢× ×”×›×¤×ª×•×¨ למטה.</string> | ||
| 51 | <string name="give_permission">תן הרש××”</string> | ||
| 52 | <string name="notification_warning">דלג על מתן הרש××” להתר×ות?</string> | ||
| 53 | <string name="notification_warning_description">yuzu ×œ× ×™×•×›×œ להתריע לך על מידע חשוב.</string> | ||
| 54 | <string name="permission_denied">הרש××” × ×“×—×ª×”</string> | ||
| 55 | <string name="permission_denied_description">×ת/×” דיחת ×ת ההרש××” יותר מדי ×¤×¢×ž×™× ×•×¢×›×©×™×• ×ת/×” צריך/×” לתת גישה ב×ופן ×™×“× ×™ בהגדרות.</string> | ||
| 56 | <string name="about">×ודות</string> | ||
| 57 | <string name="about_description">מספר גירסה, ×§×¨×“×™×˜×™× ×•×¢×•×“</string> | ||
| 58 | <string name="warning_help">עזרה</string> | ||
| 59 | <string name="warning_skip">דלג</string> | ||
| 60 | <string name="warning_cancel">ביטול</string> | ||
| 61 | <string name="install_amiibo_keys">התקן מפתחות Amiibo</string> | ||
| 62 | <string name="install_amiibo_keys_description">× ×—×•×¥ כדי להשתמש ב Amiibo במשחק</string> | ||
| 63 | <string name="invalid_keys_file">קובץ מפתחות ×œ× ×—×•×§×™ × ×‘×—×¨</string> | ||
| 64 | <string name="install_keys_success">מפתחות ×”×•×ª×§× ×• בהצלחה</string> | ||
| 65 | <string name="reading_keys_failure">שגי××” בקרי×ת מפתחות ×”×”×¦×¤× ×”</string> | ||
| 66 | <string name="install_prod_keys_failure_extension_description">×•×“× ×©×œ×§×•×‘×¥ המפתחות שלך יש סיומת של key. ×•× ×¡×”/×™ שוב.</string> | ||
| 67 | <string name="install_amiibo_keys_failure_extension_description">וד×/×™ שלקובץ המפתחות שלך יש סיומת של bin. ×•× ×¡×”/×™ שוב.</string> | ||
| 68 | <string name="invalid_keys_error">מפתחות ×”×¦×¤× ×” ×œ× ×—×•×§×™×™×</string> | ||
| 69 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | ||
| 70 | <string name="install_keys_failure_description">קבוץ ×©× ×‘×—×¨ מושחת ×ו ×œ× × ×›×•×Ÿ. בבקשה ×”×•×¦× ×ž×—×“×© ×ת המפתחות שלך.</string> | ||
| 71 | <string name="install_gpu_driver">התקן דרייבר למעבד הגרפי</string> | ||
| 72 | <string name="install_gpu_driver_description">התקן ×“×¨×™×™×‘×¨×™× ××—×¨×™× ×‘×©×‘×™×œ סיכוי ×œ×‘×™×¦×•×¢×™× ×ו דיוק ×’×‘×•×”×”×™× ×™×•×ª×¨</string> | ||
| 73 | <string name="advanced_settings">הגדרות מתקדמות</string> | ||
| 74 | <string name="advanced_settings_game">הגדרות מתקדמות: %1$s</string> | ||
| 75 | <string name="settings_description">הדר ×ת הגדרות ×”×מולטור</string> | ||
| 76 | <string name="search_recently_played">שוחק ל××—×¨×•× ×”</string> | ||
| 77 | <string name="search_recently_added">הוסף ל××—×¨×•× ×”</string> | ||
| 78 | <string name="search_retail">×§×ž×¢×•× ××™</string> | ||
| 79 | <string name="search_homebrew">Homebrew</string> | ||
| 80 | <string name="open_user_folder">פתח ×ת תיקיית yuzu </string> | ||
| 81 | <string name="open_user_folder_description">× ×” ל ×ת ×”×§×‘×¦×™× ×”×¤× ×™×ž×™×™×Ÿ של yuzu</string> | ||
| 82 | <string name="theme_and_color_description">ערוך ×ת × ×¨×ות ×”×פליקציה</string> | ||
| 83 | <string name="no_file_manager">×œ× × ×ž×¦× ×ž× ×”×œ קבצי×</string> | ||
| 84 | <string name="notification_no_directory_link">×œ× ×™×›×•×œ לפתוח ×ת ספריית yuzu</string> | ||
| 85 | <string name="notification_no_directory_link_description">בבקשה ×ž×§× ×ת תיקיית המשתמש ×‘×¤× ×œ הצידי של ×ž× ×”×œ ×”×§×‘×¦×™× ×‘×ופן ×™×“× ×™.</string> | ||
| 86 | <string name="manage_save_data">× ×”×œ מידע שמור</string> | ||
| 87 | <string name="manage_save_data_description">מידע שמור ×œ× × ×ž×¦×. בבקשה בחר/×™ ×ופציה מלמטה</string> | ||
| 88 | <string name="import_export_saves_description">×™×‘× ×ו ×™×¦× ×§×‘×¦×™ שמירה</string> | ||
| 89 | <string name="save_file_imported_success">×™×•×‘× ×‘×”×¦×œ×—×”</string> | ||
| 90 | <string name="save_file_invalid_zip_structure">×ž×‘× ×” ספריית השמירות ×œ× ×—×•×§×™</string> | ||
| 91 | <string name="save_file_invalid_zip_structure_description">התת תיקייה הר××©×•× ×” חייב להיות ×” title ID של המשחק</string> | ||
| 92 | <string name="import_saves">ייבו×</string> | ||
| 93 | <string name="export_saves">ייצו×</string> | ||
| 94 | <string name="install_firmware">התקן firmware</string> | ||
| 95 | <string name="install_firmware_description">×” frimware חייב להיות בקובץ zip ×•×”×•× ×”×›×¨×—×™ להפעלת חלק מהמשחקי×</string> | ||
| 96 | <string name="firmware_installing">מתקין frimware</string> | ||
| 97 | <string name="firmware_installed_success">ה frimware הותקן בהצלחה</string> | ||
| 98 | <string name="firmware_installed_failure">×”×ª×§× ×ª ×” frimware × ×›×©×œ×”</string> | ||
| 99 | <string name="firmware_installed_failure_description">×•×“× ×©×§×‘×¦×™ ×” firmware nca × ×ž×¦××™× ×‘×©×•×¨×© ×” zip ×•× ×¡×” שוב.</string> | ||
| 100 | <string name="share_log">שתף ×ת ×™×•×ž× ×™ ×”×¨×™×©×•× ×©×œ מיפוי הב××’×™×</string> | ||
| 101 | <string name="share_log_description">שתף ×ת קובץ ×™×•×ž× ×™ ×”×¨×™×©×•× ×©×œ yuzu בכדי לתקן בעיות</string> | ||
| 102 | <string name="share_log_missing">×œ× × ×ž×¦× ×§×•×‘×¥ יומן רישו×</string> | ||
| 103 | <string name="install_game_content">התקן תוכן משחק</string> | ||
| 104 | <string name="install_game_content_description">התקן ×¢×“×›×•× ×™ משחק ×ו DLC</string> | ||
| 105 | <string name="installing_game_content">מתקין תוכן...</string> | ||
| 106 | <string name="install_game_content_failure">תקלה ×‘×”×ª×§× ×ª הקובץ (×ו קבצי×) ל NAND</string> | ||
| 107 | <string name="install_game_content_failure_description">בבקשה ×•×“× ×©×”×ª×•×›×Ÿ (×ו ×ª×›× ×™×) ×—×•×§×™×™× ×•×©×§×•×‘×¥ ×” prod.keys מותקן.</string> | ||
| 108 | <string name="install_game_content_failure_base">×”×ª×§× ×ª משחק בסיס × ×“×—×ª בכדי ×œ×”×™×ž× ×¢ ×ž×§×•× ×¤×œ×™×§×˜×™× ×פשריי×.</string> | ||
| 109 | <string name="install_game_content_failure_file_extension">רק קבצי NSP ו XCI × ×ª×ž×›×™×. בבקשה ×•×“× ×©×ª×•×›×Ÿ (×ו ×ª×›× ×™×) המשחק חוקי.</string> | ||
| 110 | <string name="install_game_content_failed_count">%1$dבעיה (בעיות) ×”×ª×§× ×”</string> | ||
| 111 | <string name="install_game_content_success">תוכן (×ו ×ª×›× ×™) המשחק ×”×•×ª×§× ×• בהצלחה</string> | ||
| 112 | <string name="install_game_content_success_install">%1$d הותקן בהצלחה</string> | ||
| 113 | <string name="install_game_content_success_overwrite">%1$d × ×“×¨×¡/× ×›×ª×‘ מעל בהצלחה</string> | ||
| 114 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 115 | <string name="custom_driver_not_supported">×“×¨×™×™×‘×¨×™× ×ž×•×ª××ž×™× ×ישית ×œ× × ×ª×ž×›×™×</string> | ||
| 116 | <string name="custom_driver_not_supported_description">×”×˜×¢× ×ª ×“×¨×™×™×‘×™× ×ž×•×ª××ž×™× ×ישית ×œ× × ×ª×ž×š כרגע על מכשיר ×–×”. \nבבקשה בדוק ×ופציה זו בעתיד בכדי לר×ות ×× × ×•×¡×¤×” תמיכה!</string> | ||
| 117 | <string name="manage_yuzu_data">× ×”×œ ×ת המידע של yuzu</string> | ||
| 118 | <string name="manage_yuzu_data_description">יב×/×™×¦× firmware, keys, מידע של משתמש ועוד!</string> | ||
| 119 | <string name="share_save_file">שתף קובץ שמירה</string> | ||
| 120 | <string name="export_save_failed">× ×›×©×œ ×‘×™×™×¦×•× ×©×ž×™×¨×”</string> | ||
| 121 | |||
| 122 | <!-- About screen strings --> | ||
| 123 | <string name="gaia_is_not_real">Gaia ×œ× ×מיתית</string> | ||
| 124 | <string name="copied_to_clipboard">הועתק ללוח</string> | ||
| 125 | <string name="about_app_description">×מולטור Switch ×¢× ×§×•×“ פתוח</string> | ||
| 126 | <string name="contributors">תורמי×</string> | ||
| 127 | <string name="contributors_description">× ×•×¦×¨ ×¢× \u2764 מקבוצת yuzu</string> | ||
| 128 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | ||
| 129 | <string name="licenses_description">×¤×¨×•×™×™×§×˜×™× ×©×”×•×¤×›×™× ×ת yuzu ל Android ×פשרי</string> | ||
| 130 | <string name="build">גרסה</string> | ||
| 131 | <string name="user_data">× ×ª×•× ×™ משתמש</string> | ||
| 132 | <string name="user_data_description">יב×/×™×¦× ×ת כל × ×ª×•× ×™ ×”×פליקציה.\n\n×›×שר מייב××™× ×ת × ×ª×•× ×™ המשתמש, כל × ×ª×•× ×™ המשתמש ×”×§×™×™×ž×™× ×™×ž×—×§×•!</string> | ||
| 133 | <string name="exporting_user_data">×ž×™×™×¦× × ×ª×•× ×™ משתמש...</string> | ||
| 134 | <string name="importing_user_data">×ž×™×™×‘× × ×ª×•× ×™ משתמש...</string> | ||
| 135 | <string name="import_user_data">×™×‘× × ×ª×•× ×™ משתמש</string> | ||
| 136 | <string name="invalid_yuzu_backup">גיבוי yuzu ×œ× ×—×•×§×™</string> | ||
| 137 | <string name="user_data_export_success">× ×ª×•× ×™ משתמש יוצ×ו בהצלחה</string> | ||
| 138 | <string name="user_data_import_success">× ×ª×•× ×™ משתמש יוב×ו בהצלחה</string> | ||
| 139 | <string name="user_data_export_cancelled">×™×™×¦×•× ×‘×•×˜×œ</string> | ||
| 140 | <string name="user_data_import_failed_description">×•×“× ×©× ×ª×•× ×™ המשתמש × ×ž×¦××™× ×‘×©×•×¨×© קובץ ×” zip ×•×©×”×•× ×ž×›×™×œ קובץ סידור ב config/config.ini ×•× ×¡×” שוב.</string> | ||
| 141 | <string name="support_link">https://discord.gg/u77vRWY</string> | ||
| 142 | <string name="website_link">https://yuzu-emu.org/</string> | ||
| 143 | <string name="github_link">https://github.com/yuzu-emu</string> | ||
| 144 | |||
| 145 | <!-- Early access upgrade strings --> | ||
| 146 | <string name="early_access">גישה מוקדמת</string> | ||
| 147 | <string name="get_early_access">קבל גישה מוקדמת</string> | ||
| 148 | <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> | ||
| 149 | <string name="get_early_access_description">×ª×›×•× ×•×ª חותכות קצה, גישה מוקדמת ×œ×¢×“×›×•× ×™×, ועוד</string> | ||
| 150 | <string name="early_access_benefits">×™×ª×¨×•× ×•×ª של גישה מקודמת</string> | ||
| 151 | <string name="cutting_edge_features">×ª×›×•× ×•×ª חותכות קצה</string> | ||
| 152 | <string name="early_access_updates">גישה מוקדמת ×œ×¢×“×›×•× ×™×</string> | ||
| 153 | <string name="no_manual_installation">×œ×œ× ×”×ª×§× ×” ×™×“× ×™×ª</string> | ||
| 154 | <string name="prioritized_support">תמיכה בעדיפות</string> | ||
| 155 | <string name="helping_game_preservation">עוזר בשמירת משחקי×</string> | ||
| 156 | <string name="our_eternal_gratitude">התודה ×”××™× ×¡×•×¤×™×ª ×©×œ× ×•</string> | ||
| 157 | <string name="are_you_interested">×תה ×ž×¢×•× ×™×™×Ÿ?</string> | ||
| 158 | |||
| 159 | <!-- General settings strings --> | ||
| 160 | <string name="frame_limit_enable">הגבל מהירות</string> | ||
| 161 | <string name="frame_limit_enable_description">מגביל ×ת מהירות ×”×מולציה ל×חוז מהירות המבוקש מהמהירות הרגילה.</string> | ||
| 162 | <string name="frame_limit_slider">הגבל ×ת ×חוז המהירות</string> | ||
| 163 | <string name="frame_limit_slider_description">מדייק ×ת ×חוז מהירות ×”×מולציה. 100% ×–×” מהירות רגילה. ×¢×¨×›×™× ×’×“×•×œ×™× ×ו ×§×˜× ×™× ×™×יצו ×ו ×™×טו ×ת מהירות ×”×מולציה.</string> | ||
| 164 | <string name="cpu_accuracy">דיוק המעבד</string> | ||
| 165 | <string name="value_with_units">%1$s%2$s</string> | ||
| 166 | |||
| 167 | <!-- System settings strings --> | ||
| 168 | <string name="use_docked_mode">מצב ×¢×’×™× ×”</string> | ||
| 169 | <string name="use_docked_mode_description">מעלה ×ת הרזולוציה, פוגע בביצועי×. משתמש במצב × ×™×™×“ ×›×שר ×ž× ×•×˜×¨×œ, מפחית ×ת הרזולוציה ומעלה ×ת הביצועי×.</string> | ||
| 170 | <string name="emulated_region">×זור ×מולציה</string> | ||
| 171 | <string name="emulated_language">שפת ×מולציה</string> | ||
| 172 | <string name="select_rtc_date">בחר ת×ריך RTC</string> | ||
| 173 | <string name="select_rtc_time">בחר זמן RTC</string> | ||
| 174 | <string name="use_custom_rtc">RTC מות×× ×ישית</string> | ||
| 175 | <string name="use_custom_rtc_description">מ×פשר לך לקבוע שעון זמן ×מת × ×¤×¨×“ משעון המערכת שלך.</string> | ||
| 176 | <string name="set_custom_rtc">קבע RTC מות×× ×ישית</string> | ||
| 177 | |||
| 178 | <!-- Graphics settings strings --> | ||
| 179 | <string name="renderer_accuracy">רמת דיוק</string> | ||
| 180 | <string name="renderer_resolution">רזולוציה (מעוגן/× ×™×™×“)</string> | ||
| 181 | <string name="renderer_vsync">מצב VSync</string> | ||
| 182 | <string name="renderer_screen_layout">כיוון</string> | ||
| 183 | <string name="renderer_aspect_ratio">יחס רוחב גובה</string> | ||
| 184 | <string name="renderer_scaling_filter">פילטר מת×× ×—×œ×•×Ÿ</string> | ||
| 185 | <string name="renderer_anti_aliasing">שיטת Anti-aliasing</string> | ||
| 186 | <string name="renderer_force_max_clock">החזק מהירות שעון מקסימלית (רק ל Adreno)</string> | ||
| 187 | <string name="renderer_force_max_clock_description">מכריח לדחוף ×ת מהירויות המעבד הגרפי ×œ×ž×§×¡×™×ž×•× (הגבלות ×—×•× ×™×ž×©×™×›×• לתפקד).</string> | ||
| 188 | <string name="renderer_reactive_flushing_description">משפר ×ת הדיוק של ×”×מולציה ×‘×ž×©×—×§×™× ×ž×¡×•×™×™×ž×™× ×‘×ž×—×™×¨ של ביצועי×.</string> | ||
| 189 | <!-- Debug settings strings --> | ||
| 190 | <string name="cpu">מעבד</string> | ||
| 191 | <string name="cpu_debug_mode_description">×ž×›× ×™×¡ ×ת המעבד למצב דיב××’ ×יטי</string> | ||
| 192 | <string name="gpu">מעבד גרפי</string> | ||
| 193 | <!-- Audio settings strings --> | ||
| 194 | <string name="audio_output_engine">×ž× ×•×¢ פלט</string> | ||
| 195 | <string name="audio_volume">עוצמת שמע</string> | ||
| 196 | <!-- Miscellaneous --> | ||
| 197 | <string name="slider_default">ברירת מחדל</string> | ||
| 198 | <string name="ini_saved">הגדרות שמורות</string> | ||
| 199 | <string name="gameid_saved">הגדרות שמורות עבור %1$s</string> | ||
| 200 | <string name="error_saving">תקלה בשמירת %1$s.ini: %2$s</string> | ||
| 201 | <string name="loading">טוען...</string> | ||
| 202 | <string name="shutting_down">כיבוי...</string> | ||
| 203 | <string name="reset_setting_confirmation">×תה ×ž×¢×•× ×™×™×Ÿ ל×פס ×ת ההגדרה הזו חזרה לברירת המחדל?</string> | ||
| 204 | <string name="reset_to_default">×פס לברירת המחדל</string> | ||
| 205 | <string name="reset_all_settings">ל×פס ×ת כל ההגדרות?</string> | ||
| 206 | <string name="reset_all_settings_description">כל ההגדרות המתקדמות ×™×ופסו לברירת המחדל. ×œ× × ×™×ª×Ÿ לבטל פעולה זו.</string> | ||
| 207 | <string name="settings_reset">×פס הגדרות</string> | ||
| 208 | <string name="close">סגור</string> | ||
| 209 | <string name="learn_more">למד עוד</string> | ||
| 210 | <string name="auto">×וטומטי</string> | ||
| 211 | <string name="submit">שלח</string> | ||
| 212 | <string name="string_import">ייבו×</string> | ||
| 213 | <string name="export">ייצו×</string> | ||
| 214 | <string name="export_failed">×™×™×¦×•× × ×›×©×œ</string> | ||
| 215 | <string name="import_failed">×™×™×‘×•× × ×›×©×œ</string> | ||
| 216 | <string name="cancelling">מבטל</string> | ||
| 217 | |||
| 218 | <!-- GPU driver installation --> | ||
| 219 | <string name="select_gpu_driver">בחר דרייבר למעבד הגרפי</string> | ||
| 220 | <string name="select_gpu_driver_title">×תה ×ž×¢×•× ×™×™×Ÿ להחליף ×ת הדרייבר של המעבד הגרפי שלך?</string> | ||
| 221 | <string name="select_gpu_driver_install">התקן</string> | ||
| 222 | <string name="select_gpu_driver_default">ברירת מחדל</string> | ||
| 223 | <string name="select_gpu_driver_use_default">משתמש בדרייבר ברירת המחדל של המעבד הגרפי</string> | ||
| 224 | <string name="select_gpu_driver_error">דרייבר ×œ× ×—×•×§×™ × ×‘×—×¨, משתמש בברירת המחדל של המערכת!</string> | ||
| 225 | <string name="system_gpu_driver">דרייבר של המעבד הגרפי של המערכת</string> | ||
| 226 | <string name="installing_driver">מתקין דרייבר...</string> | ||
| 227 | |||
| 228 | <!-- Preferences Screen --> | ||
| 229 | <string name="preferences_settings">הגדרות</string> | ||
| 230 | <string name="preferences_general">כללי</string> | ||
| 231 | <string name="preferences_system">מערכת</string> | ||
| 232 | <string name="preferences_graphics">גרפיקה</string> | ||
| 233 | <string name="preferences_audio">שמע</string> | ||
| 234 | <string name="preferences_theme">צבע ×•× ×•×©×</string> | ||
| 235 | <!-- ROM loading errors --> | ||
| 236 | <string name="loader_error_encrypted">המשחק שלך מוצפן</string> | ||
| 237 | <string name="loader_error_invalid_format">×ין ×פשרות לטעון ×ת המשחק</string> | ||
| 238 | <string name="loader_error_file_not_found">קובץ המשחק ×œ× ×§×™×™×</string> | ||
| 239 | |||
| 240 | <!-- Emulation Menu --> | ||
| 241 | <string name="emulation_exit">×¦× ×ž×”×מולציה</string> | ||
| 242 | <string name="emulation_done">סיו×</string> | ||
| 243 | <string name="emulation_fps_counter">סופר FPS</string> | ||
| 244 | <string name="emulation_control_scale">×§× ×” מידה</string> | ||
| 245 | <string name="emulation_control_opacity">שקיפות</string> | ||
| 246 | <string name="emulation_pause">עצור ×מולציה</string> | ||
| 247 | <string name="emulation_unpause">המשך ×מולציה</string> | ||
| 248 | <string name="load_settings">טוען הגדרות...</string> | ||
| 249 | |||
| 250 | <!-- Software keyboard --> | ||
| 251 | <string name="software_keyboard">מקלדת ×ª×•×›× ×”</string> | ||
| 252 | |||
| 253 | <!-- Errors and warnings --> | ||
| 254 | <string name="abort_button">×ודות</string> | ||
| 255 | <string name="continue_button">המשך</string> | ||
| 256 | <string name="system_archive_not_found">×רכיון מערכת ×œ× × ×ž×¦×</string> | ||
| 257 | <string name="system_archive_not_found_message">%s חסר. בבקשה ×”×•×¦× ×ª× ××¨×›×™×•× ×™ המערכת שלך./nהמשכת ×”×מולציה עלולה ×œ×’×¨×•× ×œ×§×¨×™×¡×•×ª וב××’×™×.</string> | ||
| 258 | <string name="system_archive_general">×רכיון מערכת</string> | ||
| 259 | <string name="save_load_error">בעיית שמירה/×˜×¢×™× ×”</string> | ||
| 260 | <string name="fatal_error">שגי××” חמורה</string> | ||
| 261 | <string name="device_memory_inadequate">RAM המכשיר: %1$s/nמומלץ: %2$s</string> | ||
| 262 | <string name="memory_formatted">%1$s%2$s</string> | ||
| 263 | <string name="no_game_present">×ין משחק ×©× ×™×ª×Ÿ להריץ!</string> | ||
| 264 | |||
| 265 | <!-- Region Names --> | ||
| 266 | <string name="region_japan">יפן</string> | ||
| 267 | <string name="region_usa">×רה״ב</string> | ||
| 268 | <string name="region_europe">×ירופה</string> | ||
| 269 | <string name="region_australia">×וסטרליה</string> | ||
| 270 | <string name="region_china">סין</string> | ||
| 271 | <string name="region_korea">קורי××”</string> | ||
| 272 | <string name="region_taiwan">טייוו×ן</string> | ||
| 273 | |||
| 274 | <!-- Memory Sizes --> | ||
| 275 | <string name="memory_byte">בייט</string> | ||
| 276 | <string name="memory_kilobyte">KB</string> | ||
| 277 | <string name="memory_megabyte">MB</string> | ||
| 278 | <string name="memory_gigabyte">GB</string> | ||
| 279 | <string name="memory_terabyte">TB</string> | ||
| 280 | <string name="memory_petabyte">PB</string> | ||
| 281 | <string name="memory_exabyte">EB</string> | ||
| 282 | |||
| 283 | <!-- Renderer APIs --> | ||
| 284 | <string name="renderer_vulkan">Vulkan</string> | ||
| 285 | <string name="renderer_none">×ין ×©×•× ×“×‘×¨</string> | ||
| 286 | |||
| 287 | <!-- Renderer Accuracy --> | ||
| 288 | <string name="renderer_accuracy_normal">רגיל</string> | ||
| 289 | <string name="renderer_accuracy_high">גבוה</string> | ||
| 290 | <string name="renderer_accuracy_extreme">××§×¡×˜×¨×™× (×יטי)</string> | ||
| 291 | |||
| 292 | <!-- Resolutions --> | ||
| 293 | <string name="resolution_half">0.5X (360p/540p)</string> | ||
| 294 | <string name="resolution_three_quarter">0.75X (540p/810p)</string> | ||
| 295 | <string name="resolution_one">1X (720p/1080p)</string> | ||
| 296 | <string name="resolution_two">2X (1440p/2160p) (×יטי)</string> | ||
| 297 | <string name="resolution_three">3X (2160p/3240p) (×יטי)</string> | ||
| 298 | <string name="resolution_four">4X (2880p/4320p) (×יטי)</string> | ||
| 299 | |||
| 300 | <string name="renderer_vsync_mailbox">תיבת דו×ר</string> | ||
| 301 | <string name="renderer_vsync_fifo">FIFO (On)</string> | ||
| 302 | <string name="renderer_vsync_fifo_relaxed">FIFO × ×™× ×•×—</string> | ||
| 303 | |||
| 304 | <!-- Scaling Filters --> | ||
| 305 | <string name="scaling_filter_nearest_neighbor">השכן הקרוב ביותר</string> | ||
| 306 | <string name="scaling_filter_scale_force">ScaleForce</string> | ||
| 307 | <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ Super Resolution</string> | ||
| 308 | |||
| 309 | <!-- Anti-Aliasing --> | ||
| 310 | <string name="anti_aliasing_none">×ין ×©×•× ×“×‘×¨</string> | ||
| 311 | <string name="anti_aliasing_fxaa">FXAA</string> | ||
| 312 | <string name="anti_aliasing_smaa">SMAA</string> | ||
| 313 | |||
| 314 | <!-- Screen Layouts --> | ||
| 315 | <string name="screen_layout_landscape">לרוחב</string> | ||
| 316 | <string name="screen_layout_portrait">ל×ורך</string> | ||
| 317 | <string name="screen_layout_auto">×וטומטי</string> | ||
| 318 | |||
| 319 | <!-- Aspect Ratios --> | ||
| 320 | <string name="ratio_default">ברירת מחדל (16:9)</string> | ||
| 321 | <string name="ratio_force_four_three">הכרח 4:3</string> | ||
| 322 | <string name="ratio_force_twenty_one_nine">הכרח 21:9</string> | ||
| 323 | <string name="ratio_force_sixteen_ten">הכרח 16:10</string> | ||
| 324 | <string name="ratio_stretch">הרחב לגודל המסך</string> | ||
| 325 | |||
| 326 | <!-- CPU Accuracy --> | ||
| 327 | <string name="cpu_accuracy_accurate">מדויק</string> | ||
| 328 | <string name="cpu_accuracy_unsafe">×œ× ×‘×˜×•×—</string> | ||
| 329 | <string name="cpu_accuracy_paranoid">פר×× ×•×ידי (×יטי)</string> | ||
| 330 | |||
| 331 | <!-- Gamepad Buttons --> | ||
| 332 | <string name="gamepad_d_pad">D-pad</string> | ||
| 333 | <string name="gamepad_left_stick">ג׳ויסטיק שמ×לי</string> | ||
| 334 | <string name="gamepad_right_stick">ג׳ויסטיק ×™×ž× ×™</string> | ||
| 335 | <string name="gamepad_home">בית</string> | ||
| 336 | <string name="gamepad_screenshot">×¦×™×œ×•× ×ž×¡×š</string> | ||
| 337 | |||
| 338 | <!-- Theme options --> | ||
| 339 | <string name="change_app_theme">×©× ×” ×ת × ×•×©× ×”×פליקצייה</string> | ||
| 340 | <string name="theme_default">ברירת מחדל</string> | ||
| 341 | <string name="theme_material_you">חומר ×תה/מ×טירי×ל יו</string> | ||
| 342 | |||
| 343 | <!-- Theme Modes --> | ||
| 344 | <string name="change_theme_mode">×©× ×” ×ת מצב ×”× ×•×©×</string> | ||
| 345 | <string name="theme_mode_follow_system">עקוב ×חרי המערכת</string> | ||
| 346 | <string name="theme_mode_light">בהיר</string> | ||
| 347 | <string name="theme_mode_dark">×›×”×”</string> | ||
| 348 | |||
| 349 | <!-- Audio output engines --> | ||
| 350 | <string name="cubeb">cubeb</string> | ||
| 351 | |||
| 352 | <!-- Black backgrounds theme --> | ||
| 353 | <string name="use_black_backgrounds">×¨×§×¢×™× ×©×—×•×¨×™×</string> | ||
| 354 | <string name="use_black_backgrounds_description">×›×©×ž×ª×©×ž×©×™× ×‘×ž×¦×‘ ×›×”×”, ×©× ×¨×§×¢×™× ×©×—×•×¨×™×.</string> | ||
| 355 | |||
| 356 | <!-- Picture-In-Picture --> | ||
| 357 | <string name="picture_in_picture">×ª×ž×•× ×” בתוך ×ª×ž×•× ×”</string> | ||
| 358 | <string name="picture_in_picture_description">הקטן ×ת החלון ×›×שר × ×ž×¦× ×‘×¨×§×¢</string> | ||
| 359 | <string name="pause">עצור</string> | ||
| 360 | <string name="play">שחק</string> | ||
| 361 | <string name="mute">השתק</string> | ||
| 362 | <string name="unmute">בטל השתקה</string> | ||
| 363 | |||
| 364 | <!-- Licenses screen strings --> | ||
| 365 | <string name="licenses">×¨×™×©×™×•× ×•×ª</string> | ||
| 366 | <string name="license_fidelityfx_fsr_description">××¤×¡×§×™×™×œ×™× ×’ ב×יכות גבוהה מ AMD</string> | ||
| 367 | </resources> | ||
diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml new file mode 100644 index 000000000..6563ba288 --- /dev/null +++ b/src/android/app/src/main/res/values-hu/strings.xml | |||
| @@ -0,0 +1,402 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | ||
| 3 | |||
| 4 | <string name="app_disclaimer">Ez a szoftver Nintendo Switch játékkonzolhoz készült játékokat futtat. Nem tartalmaz játékokat vagy kulcsokat. .<br /><br />Mielőtt hozzákezdenél, kérjük, válaszd ki a <![CDATA[<b>prod.keys</b>]]> fájl helyét a készülék tárhelyén<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tudj meg többet</a>]]></string> | ||
| 5 | <string name="emulation_notification_channel_name">Emuláció aktÃv</string> | ||
| 6 | <string name="emulation_notification_channel_description">Ãllandó értesÃtést jelenÃt meg, amÃg az emuláció fut.</string> | ||
| 7 | <string name="emulation_notification_running">A yuzu fut</string> | ||
| 8 | <string name="notice_notification_channel_name">Megjegyzések és hibák</string> | ||
| 9 | <string name="notice_notification_channel_description">ÉrtesÃtések megjelenÃtése, ha valami rosszul sül el.</string> | ||
| 10 | <string name="notification_permission_not_granted">Nincs engedély az értesÃtés megjelenÃtéséhez!</string> | ||
| 11 | |||
| 12 | <!-- Setup strings --> | ||
| 13 | <string name="welcome">Üdvözöljük!</string> | ||
| 14 | <string name="welcome_description">Ismerkedj meg a <b>yuzu</b> beállÃtásával és ugorj bele az emulációba.</string> | ||
| 15 | <string name="get_started">Vágjunk bele</string> | ||
| 16 | <string name="keys">Kulcsok</string> | ||
| 17 | <string name="keys_description">Válaszd ki a(z) <b>prod.keys</b> fájlodat az alábbi gombbal.</string> | ||
| 18 | <string name="select_keys">Kulcsok kiválasztása</string> | ||
| 19 | <string name="games">Játékok</string> | ||
| 20 | <string name="games_description"> | ||
| 21 | Válaszd ki a(z) <b>Games</b> mappát az alábbi gombbal.</string> | ||
| 22 | <string name="done">Kész</string> | ||
| 23 | <string name="done_description">Minden kész.\nJó szórakozást!</string> | ||
| 24 | <string name="text_continue">Folytatás</string> | ||
| 25 | <string name="next">Következő</string> | ||
| 26 | <string name="back">Vissza</string> | ||
| 27 | <string name="add_games">Játékok hozzáadása</string> | ||
| 28 | <string name="add_games_description">Játékaid mappa kiválasztása</string> | ||
| 29 | <string name="step_complete">Kész!</string> | ||
| 30 | |||
| 31 | <!-- Home strings --> | ||
| 32 | <string name="home_games">Játékok</string> | ||
| 33 | <string name="home_search">Keresés</string> | ||
| 34 | <string name="home_settings">BeállÃtások</string> | ||
| 35 | <string name="empty_gamelist">Nem található fájl, vagy még nincs kiválasztva könyvtár.</string> | ||
| 36 | <string name="search_and_filter_games">Játékok keresése és szűrése</string> | ||
| 37 | <string name="select_games_folder">Játékmappa kiválasztása</string> | ||
| 38 | <string name="add_games_warning">Kihagyod a játékok mappa kiválasztását?</string> | ||
| 39 | <string name="add_games_warning_description">A játékok nem jelennek meg a Játékok listában, ha egy mappa nincs kijelölve.</string> | ||
| 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | ||
| 41 | <string name="home_search_games">Játékok keresése</string> | ||
| 42 | <string name="search_settings">BeállÃtások keresése</string> | ||
| 43 | <string name="games_dir_selected">Játékok könyvtár kiválasztva</string> | ||
| 44 | <string name="install_prod_keys">prod.keys telepÃtése</string> | ||
| 45 | <string name="install_prod_keys_description">Kiskereskedelmi játékok dekódolásához szükséges</string> | ||
| 46 | <string name="install_prod_keys_warning">Kihagyod a kulcsok hozzáadását?</string> | ||
| 47 | <string name="install_prod_keys_warning_description">A kiskereskedelmi játékok emulálásához érvényes kulcsokra van szükség. Csak a homebrew alkalmazások fognak működni, ha folytatod.</string> | ||
| 48 | <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> | ||
| 49 | <string name="notifications">ÉrtesÃtések</string> | ||
| 50 | <string name="notifications_description">ÉrtesÃtési engedélyek megadása az alábbi gombbal.</string> | ||
| 51 | <string name="give_permission">Engedély megadása</string> | ||
| 52 | <string name="notification_warning">Kihagyod az értesÃtési engedély megadását?</string> | ||
| 53 | <string name="notification_warning_description">yuzu nem fog tudni értesÃteni a fontos imformációkról</string> | ||
| 54 | <string name="permission_denied">Engedély megtagadva</string> | ||
| 55 | <string name="permission_denied_description">Túl gyakran utasÃtottad el a hozzáférést, Ãgy manuálisan kell jóváhagynod a rendszer beállÃtásokban.</string> | ||
| 56 | <string name="about">A programról</string> | ||
| 57 | <string name="about_description">Build verzió, készÃtÅ‘k, és még több</string> | ||
| 58 | <string name="warning_help">SegÃtség</string> | ||
| 59 | <string name="warning_skip">Kihagyás</string> | ||
| 60 | <string name="warning_cancel">Mégse</string> | ||
| 61 | <string name="install_amiibo_keys">Amiibo kulcsok telepÃtése</string> | ||
| 62 | <string name="install_amiibo_keys_description">Amiibo használata szükséges a játékhoz</string> | ||
| 63 | <string name="invalid_keys_file">Érvénytelen titkosÃtófájlok kiválasztva</string> | ||
| 64 | <string name="install_keys_success">Kulcsok sikeresen telepÃtve</string> | ||
| 65 | <string name="reading_keys_failure">Hiba történt a titkosÃtókulcsok olvasása során</string> | ||
| 66 | <string name="install_prod_keys_failure_extension_description">GyÅ‘zÅ‘dj meg róla, hogy a titkosÃtó fájlod .keys kiterjesztéssel rendelkezik, majd próbáld újra.</string> | ||
| 67 | <string name="install_amiibo_keys_failure_extension_description">GyÅ‘zÅ‘dj meg róla, hogy a titkosÃtó fájlod .bin kiterjesztéssel rendelkezik, majd próbáld újra.</string> | ||
| 68 | <string name="invalid_keys_error">Érvénytelen titkosÃtókulcsok</string> | ||
| 69 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | ||
| 70 | <string name="install_keys_failure_description">A kiválasztott fájl helytelen, vagy sérült. ÃllÃts össze egy új kulcsot.</string> | ||
| 71 | <string name="install_gpu_driver">GPU illesztÅ‘program telepÃtése</string> | ||
| 72 | <string name="install_gpu_driver_description">AlternatÃv illesztÅ‘programok telepÃtése az esetlegesen elérhetÅ‘ teljesÃtmény és pontosság érdekében</string> | ||
| 73 | <string name="advanced_settings">Haladó beállÃtások</string> | ||
| 74 | <string name="advanced_settings_game">Haladó beállÃtások: %1$s</string> | ||
| 75 | <string name="settings_description">EmulátorbeállÃtások konfigurálása</string> | ||
| 76 | <string name="search_recently_played">Nemrég játszva</string> | ||
| 77 | <string name="search_recently_added">Nemrég hozzáadva</string> | ||
| 78 | <string name="search_retail">Kiskereskedelmi</string> | ||
| 79 | <string name="open_user_folder">yuzu mappa megnyitása</string> | ||
| 80 | <string name="open_user_folder_description">yuzu belső fájljainak kezelése</string> | ||
| 81 | <string name="theme_and_color_description">Az alkalmazás megjelenésének módosÃtása</string> | ||
| 82 | <string name="no_file_manager">Nem található fájlkezelő</string> | ||
| 83 | <string name="notification_no_directory_link">Nem sikerült megnyitni a yuzu könyvtárat</string> | ||
| 84 | <string name="notification_no_directory_link_description">Kérjük, manuálisan keresd meg a felhasználói mappát a fájlkezelő oldalsó paneljével.</string> | ||
| 85 | <string name="manage_save_data">Mentésadatok kezelése</string> | ||
| 86 | <string name="manage_save_data_description">Mentés található. Kérjük, válassz egyet az alábbi opciók közül.</string> | ||
| 87 | <string name="import_export_saves_description">Mentési fájlok importálás vagy exportálása</string> | ||
| 88 | <string name="save_file_imported_success">Sikeresen importálva</string> | ||
| 89 | <string name="save_file_invalid_zip_structure">Érvénytelen mentési könyvtárstruktúra</string> | ||
| 90 | <string name="save_file_invalid_zip_structure_description">Az elsÅ‘ almappa neve a játék azonosÃtója kell, hogy legyen.</string> | ||
| 91 | <string name="import_saves">Importálás</string> | ||
| 92 | <string name="export_saves">Exportálás</string> | ||
| 93 | <string name="install_firmware">Firmware telepÃtés</string> | ||
| 94 | <string name="install_firmware_description">A firmwarenek ZIP archÃvumban kell lennie, és szükséges a játékok indÃtásához</string> | ||
| 95 | <string name="firmware_installing">Firmware telepÃtése</string> | ||
| 96 | <string name="firmware_installed_success">Firmware sikeresen telepÃtve</string> | ||
| 97 | <string name="firmware_installed_failure">Firmware telepÃtése sikertelen</string> | ||
| 98 | <string name="firmware_installed_failure_description">Győződj meg róla, hogy a firmware nca fájlok a zip gyökerénél vannak, és próbáld meg újra.</string> | ||
| 99 | <string name="share_log">Hibakereső logok megosztása</string> | ||
| 100 | <string name="share_log_description">A yuzu naplófájl megosztása a problémák elhárÃtásához</string> | ||
| 101 | <string name="share_log_missing">Nem található log fájl</string> | ||
| 102 | <string name="install_game_content">Játéktartalom telepÃtése</string> | ||
| 103 | <string name="install_game_content_description">JátékfrissÃtések vagy DLC telepÃtése</string> | ||
| 104 | <string name="installing_game_content">Tartalom telepÃtése...</string> | ||
| 105 | <string name="install_game_content_failure">Hiba történt a fájl(ok) NAND-ra telepÃtése közben</string> | ||
| 106 | <string name="install_game_content_failure_description">GyÅ‘zÅ‘dj meg róla, hogy a tartalom valós, és a prod.keys fájl telepÃtve van.</string> | ||
| 107 | <string name="install_game_content_failure_base">Az alapjátékok telepÃtése nem engedélyezett az esetleges konfliktusok elkerülése érdekében.</string> | ||
| 108 | <string name="install_game_content_failure_file_extension">Csak NSP és XCI tartalom támogatott. Győződj meg róla, hogy a játéktartalom érvényes.</string> | ||
| 109 | <string name="install_game_content_failed_count">%1$d telepÃtési hiba</string> | ||
| 110 | <string name="install_game_content_success">Játéktartalom sikeresen telepÃtve</string> | ||
| 111 | <string name="install_game_content_success_install">%1$d sikeresen telepÃtve</string> | ||
| 112 | <string name="install_game_content_success_overwrite">%1$d sikeresen felülÃrva</string> | ||
| 113 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 114 | <string name="custom_driver_not_supported">Egyéni illesztőprogramok nem támogatottak</string> | ||
| 115 | <string name="custom_driver_not_supported_description">Egyéni illesztÅ‘program telepÃtése jelenleg nem támogatott ezen az eszközön.\nNézz vissza késÅ‘bb, hátha hozzáadtuk a támogatását!</string> | ||
| 116 | <string name="manage_yuzu_data">yuzu adatok kezelése</string> | ||
| 117 | <string name="manage_yuzu_data_description">Firmware, kulcsok, felhasználói adatok és egyebek importálása/exportálása</string> | ||
| 118 | <string name="share_save_file">Mentési fájl megosztása</string> | ||
| 119 | <string name="export_save_failed">A mentés exportálása sikertelen</string> | ||
| 120 | |||
| 121 | <!-- About screen strings --> | ||
| 122 | <string name="gaia_is_not_real">Gaia nem valódi</string> | ||
| 123 | <string name="copied_to_clipboard">Másolva a vágólapra</string> | ||
| 124 | <string name="about_app_description">Egy nyÃlt forráskódú Switch emulátor</string> | ||
| 125 | <string name="contributors">Hozzájárulók</string> | ||
| 126 | <string name="contributors_description">\u2764 által készÃtve a yuzu csapattól</string> | ||
| 127 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | ||
| 128 | <string name="licenses_description">Projektek, amik nélkül a yuzu nem jöhetett volna létre Androidra</string> | ||
| 129 | <string name="user_data">Felhasználói adatok</string> | ||
| 130 | <string name="user_data_description">Az összes alkalmazásadat importálása/exportálása.\n\nA felhasználói adatok importálásakor az összes meglévő felhasználói adat törlődik!</string> | ||
| 131 | <string name="exporting_user_data">Felhasználói adatok exportálása...</string> | ||
| 132 | <string name="importing_user_data">Felhasználói adatok importálása...</string> | ||
| 133 | <string name="import_user_data">Felhasználói adatok importálása</string> | ||
| 134 | <string name="invalid_yuzu_backup">Érvénytelen yuzu biztonsági másolat</string> | ||
| 135 | <string name="user_data_export_success">Felhasználói adatok sikeresen exportálva</string> | ||
| 136 | <string name="user_data_import_success">Felhasználói adatok sikeresen importálva</string> | ||
| 137 | <string name="user_data_export_cancelled">Exportálás megszakÃtva</string> | ||
| 138 | <string name="user_data_import_failed_description">EllenÅ‘rizd, hogy a felhasználói adatok mappái a zip mappa gyökerében vannak, és tartalmaznak egy konfig fájlt a config/config.ini cÃmen, majd próbáld meg újra.</string> | ||
| 139 | <string name="support_link">https://discord.gg/u77vRWY</string> | ||
| 140 | <string name="website_link">https://yuzu-emu.org/</string> | ||
| 141 | <string name="github_link">https://github.com/yuzu-emu</string> | ||
| 142 | |||
| 143 | <!-- Early access upgrade strings --> | ||
| 144 | <string name="early_access">Korai hozzáférés</string> | ||
| 145 | <string name="get_early_access">Szerezz korai hozzáférést</string> | ||
| 146 | <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> | ||
| 147 | <string name="get_early_access_description">Legújabb funkciók, korai hozzáférés a frissÃtésekhez, és sok más</string> | ||
| 148 | <string name="early_access_benefits">Korai hozzáférés előnyei</string> | ||
| 149 | <string name="cutting_edge_features">Legújabb funkciók</string> | ||
| 150 | <string name="early_access_updates">Korai hozzáférés a frissÃtésekhez</string> | ||
| 151 | <string name="no_manual_installation">Automatikus telepÃtések</string> | ||
| 152 | <string name="prioritized_support">Priorizált támogatás</string> | ||
| 153 | <string name="our_eternal_gratitude">Valamint az örök hálánk</string> | ||
| 154 | <string name="are_you_interested">Érdekel a dolog?</string> | ||
| 155 | |||
| 156 | <!-- General settings strings --> | ||
| 157 | <string name="frame_limit_enable">Sebességkorlát</string> | ||
| 158 | <string name="frame_limit_enable_description">Korlátozza az emuláció sebességét a normál sebesség adott százalékára.</string> | ||
| 159 | <string name="frame_limit_slider">Sebességkorlát százaléka</string> | ||
| 160 | <string name="frame_limit_slider_description">Az emuláció sebességét határozza meg. 100% a normál sebesség. A magasabb értékek növelik, az alacsonyabbak csökkentik a sebességkorlátot.</string> | ||
| 161 | <string name="cpu_accuracy">CPU pontosság</string> | ||
| 162 | <string name="value_with_units">%1$s%2$s</string> | ||
| 163 | |||
| 164 | <!-- System settings strings --> | ||
| 165 | <string name="use_docked_mode">Dokkolt mód</string> | ||
| 166 | <string name="use_docked_mode_description">Növeli a felbontást, de csökkenti a teljesÃtményt. Kikapcsolás esetén a Kézi mód van használatban, ami kisebb felbontást, de nagyobb teljesÃtményt eredményez.</string> | ||
| 167 | <string name="emulated_region">Emulált régió</string> | ||
| 168 | <string name="emulated_language">Emulált nyelv</string> | ||
| 169 | <string name="select_rtc_date">Válassz RTC dátumot</string> | ||
| 170 | <string name="select_rtc_time">Válassz RTC időt</string> | ||
| 171 | <string name="use_custom_rtc">Egyéni RTC</string> | ||
| 172 | <string name="use_custom_rtc_description">Megadhatsz egy valós idejű órát, amely eltér a rendszer által használt órától.</string> | ||
| 173 | <string name="set_custom_rtc">Egyéni RTC beállÃtása</string> | ||
| 174 | |||
| 175 | <!-- Graphics settings strings --> | ||
| 176 | <string name="renderer_accuracy">Pontosság szintje</string> | ||
| 177 | <string name="renderer_resolution">Felbontás (Kézi/Dockolt)</string> | ||
| 178 | <string name="renderer_vsync">VSync mód</string> | ||
| 179 | <string name="renderer_screen_layout">Orientáció</string> | ||
| 180 | <string name="renderer_aspect_ratio">Képarány</string> | ||
| 181 | <string name="renderer_scaling_filter">Ablakhoz alkalmazkodó szűrő</string> | ||
| 182 | <string name="renderer_anti_aliasing">ÉlsimÃtási módszer</string> | ||
| 183 | <string name="renderer_force_max_clock">Maximum órajel kényszerÃtése (csak Adreno)</string> | ||
| 184 | <string name="renderer_force_max_clock_description">KényszerÃti a GPU-t a lehetÅ‘ legnagyobb órajelen működésre (a hÅ‘mérséklet korlátozások továbbra is érvényben maradnak).</string> | ||
| 185 | <string name="renderer_asynchronous_shaders">Aszinkron árnyékolók használata</string> | ||
| 186 | <string name="renderer_asynchronous_shaders_description">Aszinkron módon fordÃtja az árnyékolókat, ami csökkenti az akadozást, de hibákat okozhat.</string> | ||
| 187 | <string name="renderer_reactive_flushing">ReaktÃv ürÃtés használata</string> | ||
| 188 | <string name="renderer_reactive_flushing_description">JavÃtja a renderelési pontosságot néhány játékban a teljesÃtmény rovására.</string> | ||
| 189 | <string name="use_disk_shader_cache">Lemez árnyékoló gyorsÃtótár</string> | ||
| 190 | <string name="use_disk_shader_cache_description">Csökkenti az akadásokat azáltal, hogy helyileg tárolja és tölti be a generált árnyékolókat.</string> | ||
| 191 | |||
| 192 | <!-- Debug settings strings --> | ||
| 193 | <string name="cpu">CPU</string> | ||
| 194 | <string name="cpu_debug_mode">CPU hibakeresés</string> | ||
| 195 | <string name="cpu_debug_mode_description">Lassú hibakeresÅ‘ módba állÃtja a CPU-t.</string> | ||
| 196 | <string name="gpu">GPU</string> | ||
| 197 | <string name="renderer_api">API</string> | ||
| 198 | <string name="renderer_debug">Grafikai hibakeresés</string> | ||
| 199 | <string name="renderer_debug_description">Lassú hibakeresési módba állÃtja a grafikus API-t .</string> | ||
| 200 | <!-- Audio settings strings --> | ||
| 201 | <string name="audio_output_engine">Kimeneti rendszer</string> | ||
| 202 | <string name="audio_volume">Hangerő</string> | ||
| 203 | <string name="audio_volume_description">Hangkimenet hangerejének megadása</string> | ||
| 204 | |||
| 205 | <!-- Miscellaneous --> | ||
| 206 | <string name="slider_default">Alapértelmezett</string> | ||
| 207 | <string name="ini_saved">BeállÃtások elmentve</string> | ||
| 208 | <string name="gameid_saved">BeállÃtások elmentve a következÅ‘höz: %1$s</string> | ||
| 209 | <string name="error_saving">Mentési hiba%1$s .ini: %2$s</string> | ||
| 210 | <string name="unimplemented_menu">Nem implementált menü</string> | ||
| 211 | <string name="loading">Betöltés...</string> | ||
| 212 | <string name="shutting_down">LeállÃtás...</string> | ||
| 213 | <string name="reset_setting_confirmation">Szeretnéd visszaállÃtani a beállÃtások az alapértelmezett értékekre?</string> | ||
| 214 | <string name="reset_to_default">Alaphelyzetbe állÃtás</string> | ||
| 215 | <string name="reset_all_settings">Alaphelyzetbe állÃtod a beállÃtásokat?</string> | ||
| 216 | <string name="reset_all_settings_description">Minden haladó beállÃtás vissza lesz állÃtva az alapértelmezett konfigurációra. Ez a művelet nem vonható vissza.</string> | ||
| 217 | <string name="settings_reset">BeállÃtások alaphelyzetbe állÃtva</string> | ||
| 218 | <string name="close">Bezárás</string> | ||
| 219 | <string name="learn_more">Tudj meg többet</string> | ||
| 220 | <string name="auto">Automatikus</string> | ||
| 221 | <string name="submit">Küldés</string> | ||
| 222 | <string name="string_null">Nulla</string> | ||
| 223 | <string name="string_import">Importálás</string> | ||
| 224 | <string name="export">Exportálás</string> | ||
| 225 | <string name="export_failed">Exportálás sikertelen</string> | ||
| 226 | <string name="import_failed">Importálás sikertelen</string> | ||
| 227 | <string name="cancelling">MegszakÃtás</string> | ||
| 228 | |||
| 229 | <!-- GPU driver installation --> | ||
| 230 | <string name="select_gpu_driver">Válassz GPU illesztőprogramot</string> | ||
| 231 | <string name="select_gpu_driver_title">Szeretnéd lecserélni a jelenlegi GPU illesztőprogramot?</string> | ||
| 232 | <string name="select_gpu_driver_install">TelepÃtés</string> | ||
| 233 | <string name="select_gpu_driver_default">Alapértelmezett</string> | ||
| 234 | <string name="select_gpu_driver_use_default">Alapértelmezett GPU illesztőprogram használata</string> | ||
| 235 | <string name="select_gpu_driver_error">Érvénytelen driver kiválasztva, a rendszer alapértelmezett lesz használva!</string> | ||
| 236 | <string name="system_gpu_driver">Rendszer GPU illesztőprogram</string> | ||
| 237 | <string name="installing_driver">IllesztÅ‘program telepÃtése...</string> | ||
| 238 | |||
| 239 | <!-- Preferences Screen --> | ||
| 240 | <string name="preferences_settings">BeállÃtások</string> | ||
| 241 | <string name="preferences_general">Ãltalános</string> | ||
| 242 | <string name="preferences_system">Rendszer</string> | ||
| 243 | <string name="preferences_graphics">Grafika</string> | ||
| 244 | <string name="preferences_audio">Hang</string> | ||
| 245 | <string name="preferences_theme">Téma és szÃnek</string> | ||
| 246 | <string name="preferences_debug">Hibakeresés</string> | ||
| 247 | |||
| 248 | <!-- ROM loading errors --> | ||
| 249 | <string name="loader_error_encrypted">ROM titkosÃtva</string> | ||
| 250 | <string name="loader_error_encrypted_keys_description"><![CDATA[GyÅ‘zÅ‘dj meg róla, hogy a <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> fájl telepÃtve van, hogy a játékok visszafejthetÅ‘k legyenek.]]></string> | ||
| 251 | <string name="loader_error_video_core">Hiba lépett fel a videómag inicializása során</string> | ||
| 252 | <string name="loader_error_video_core_description">Ezt általában egy nem kompatibilis GPU illesztÅ‘ okozza. Egyéni GPU illesztÅ‘program telepÃtése megoldhatja a problémát.</string> | ||
| 253 | <string name="loader_error_invalid_format">Nem sikerült betölteni a ROM-ot</string> | ||
| 254 | <string name="loader_error_file_not_found">ROM fájl nem létezik</string> | ||
| 255 | |||
| 256 | <!-- Emulation Menu --> | ||
| 257 | <string name="emulation_exit">Emuláció bezárása</string> | ||
| 258 | <string name="emulation_done">Kész</string> | ||
| 259 | <string name="emulation_fps_counter">FPS számláló</string> | ||
| 260 | <string name="emulation_toggle_controls">IrányÃtás átkapcsolása</string> | ||
| 261 | <string name="emulation_dpad_slide">D-pad csúsztatása</string> | ||
| 262 | <string name="emulation_haptics">Érintés haptikája</string> | ||
| 263 | <string name="emulation_show_overlay">Ãtfedés mutatása</string> | ||
| 264 | <string name="emulation_toggle_all">Össze átkapcsolása</string> | ||
| 265 | <string name="emulation_control_adjust">Ãtfedés testreszabása</string> | ||
| 266 | <string name="emulation_control_scale">Skálázás</string> | ||
| 267 | <string name="emulation_control_opacity">Ãtlátszóság</string> | ||
| 268 | <string name="emulation_touch_overlay_reset">Ãtfedés visszaállÃtása</string> | ||
| 269 | <string name="emulation_touch_overlay_edit">Ãtfedés módosÃtása</string> | ||
| 270 | <string name="emulation_pause">Emuláció szünetelése</string> | ||
| 271 | <string name="emulation_unpause">Emuláció folytatása</string> | ||
| 272 | <string name="emulation_input_overlay">Ãtfedés beállÃtásai</string> | ||
| 273 | |||
| 274 | <string name="load_settings">BeállÃtások betöltése...</string> | ||
| 275 | |||
| 276 | <!-- Software keyboard --> | ||
| 277 | <string name="software_keyboard">Szoftver billenytűzet</string> | ||
| 278 | |||
| 279 | <!-- Errors and warnings --> | ||
| 280 | <string name="abort_button">MegszakÃtás</string> | ||
| 281 | <string name="continue_button">Folytatás</string> | ||
| 282 | <string name="system_archive_not_found">Nem található rendszerarchÃvum</string> | ||
| 283 | <string name="system_archive_not_found_message">%s hiányzik. Kérjük, mentsd ki a rendszerarchÃvumaidat.\nAz emuláció folytatása összeomlásokhoz és hibákhoz vezethet.</string> | ||
| 284 | <string name="system_archive_general">Egy rendszerarchÃvum</string> | ||
| 285 | <string name="save_load_error">Mentési/betöltési hiba</string> | ||
| 286 | <string name="fatal_error">Végzetes hiba</string> | ||
| 287 | <string name="fatal_error_message">Végzetes hiba történt. Ellenőrizd a logot a részletekért.\nAz emuláció folytatása összeomlást és hibákat eredményzhet.</string> | ||
| 288 | <string name="performance_warning">Ennek a beállÃtásnak a kikapcsolása jelentÅ‘s mértékben csökkenti a teljesÃtményt! A legjobb élmény érdekében javasolt a beállÃtás bekapcsolva tartása.</string> | ||
| 289 | <string name="device_memory_inadequate">Eszköz RAM: %1$s\nAjánlott: %2$s</string> | ||
| 290 | <string name="memory_formatted">%1$s %2$s</string> | ||
| 291 | <string name="no_game_present">Nincs indÃtható játék!</string> | ||
| 292 | |||
| 293 | <!-- Region Names --> | ||
| 294 | <string name="region_japan">Japán</string> | ||
| 295 | <string name="region_usa">USA</string> | ||
| 296 | <string name="region_europe">Európa</string> | ||
| 297 | <string name="region_australia">Ausztrália</string> | ||
| 298 | <string name="region_china">KÃna</string> | ||
| 299 | <string name="region_korea">Korea</string> | ||
| 300 | <string name="region_taiwan">Tajvan</string> | ||
| 301 | |||
| 302 | <!-- Memory Sizes --> | ||
| 303 | <string name="memory_byte">Bájt</string> | ||
| 304 | <string name="memory_kilobyte">KB</string> | ||
| 305 | <string name="memory_megabyte">MB</string> | ||
| 306 | <string name="memory_gigabyte">GB</string> | ||
| 307 | <string name="memory_terabyte">TB</string> | ||
| 308 | <string name="memory_petabyte">PB</string> | ||
| 309 | <string name="memory_exabyte">EB</string> | ||
| 310 | |||
| 311 | <!-- Renderer APIs --> | ||
| 312 | <string name="renderer_vulkan">Vulkan</string> | ||
| 313 | <string name="renderer_none">Nincs</string> | ||
| 314 | |||
| 315 | <!-- Renderer Accuracy --> | ||
| 316 | <string name="renderer_accuracy_normal">Normál</string> | ||
| 317 | <string name="renderer_accuracy_high">Magas</string> | ||
| 318 | <string name="renderer_accuracy_extreme">Extrém (Lassú)</string> | ||
| 319 | |||
| 320 | <!-- Resolutions --> | ||
| 321 | <string name="resolution_half">0.5X (360p/540p)</string> | ||
| 322 | <string name="resolution_three_quarter">0.75X (540p/810p)</string> | ||
| 323 | <string name="resolution_one">1X (720p/1080p)</string> | ||
| 324 | <string name="resolution_two">2X (1440p/2160p) (Lassú)</string> | ||
| 325 | <string name="resolution_three">3X (2160p/3240p) (Lassú)</string> | ||
| 326 | <string name="resolution_four">4X (2880p/4320p) (Lassú)</string> | ||
| 327 | |||
| 328 | <!-- Renderer VSync --> | ||
| 329 | <string name="renderer_vsync_immediate">Azonnali (Ki)</string> | ||
| 330 | <string name="renderer_vsync_mailbox">Postaláda</string> | ||
| 331 | <string name="renderer_vsync_fifo">FIFO (Be)</string> | ||
| 332 | <string name="renderer_vsync_fifo_relaxed">FIFO Relaxált</string> | ||
| 333 | |||
| 334 | <!-- Scaling Filters --> | ||
| 335 | <string name="scaling_filter_nearest_neighbor">Legközelebbi szomszéd</string> | ||
| 336 | <string name="scaling_filter_bilinear">Bilineáris</string> | ||
| 337 | <string name="scaling_filter_bicubic">Bikubikus</string> | ||
| 338 | <string name="scaling_filter_gaussian">Gauss-féle</string> | ||
| 339 | <string name="scaling_filter_scale_force">ScaleForce</string> | ||
| 340 | <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ Super Resolution</string> | ||
| 341 | |||
| 342 | <!-- Anti-Aliasing --> | ||
| 343 | <string name="anti_aliasing_none">Nincs</string> | ||
| 344 | <string name="anti_aliasing_fxaa">FXAA</string> | ||
| 345 | <string name="anti_aliasing_smaa">SMAA</string> | ||
| 346 | |||
| 347 | <!-- Screen Layouts --> | ||
| 348 | <string name="screen_layout_landscape">Fekvő</string> | ||
| 349 | <string name="screen_layout_portrait">Ãlló</string> | ||
| 350 | <string name="screen_layout_auto">Automatikus</string> | ||
| 351 | |||
| 352 | <!-- Aspect Ratios --> | ||
| 353 | <string name="ratio_default">Alapértelmezett (16:9)</string> | ||
| 354 | <string name="ratio_force_four_three">4:3 kényszerÃtése</string> | ||
| 355 | <string name="ratio_force_twenty_one_nine">21:9 kényszerÃtése</string> | ||
| 356 | <string name="ratio_force_sixteen_ten">16:10 kényszerÃtése</string> | ||
| 357 | <string name="ratio_stretch">Ablakhoz nyújtás</string> | ||
| 358 | |||
| 359 | <!-- CPU Accuracy --> | ||
| 360 | <string name="cpu_accuracy_accurate">Pontos</string> | ||
| 361 | <string name="cpu_accuracy_unsafe">Nem biztonságos</string> | ||
| 362 | <string name="cpu_accuracy_paranoid">Paranoid (Lassú)</string> | ||
| 363 | |||
| 364 | <!-- Gamepad Buttons --> | ||
| 365 | <string name="gamepad_d_pad">D-pad</string> | ||
| 366 | <string name="gamepad_left_stick">Bal kar</string> | ||
| 367 | <string name="gamepad_right_stick">Jobb kar</string> | ||
| 368 | <string name="gamepad_home">Home</string> | ||
| 369 | <string name="gamepad_screenshot">Képernyőmentés</string> | ||
| 370 | |||
| 371 | <!-- Disk shader cache --> | ||
| 372 | <string name="preparing_shaders">Ãrnyékolók elÅ‘készÃtése</string> | ||
| 373 | <string name="building_shaders">Ãrnyékolók létrehozása</string> | ||
| 374 | |||
| 375 | <!-- Theme options --> | ||
| 376 | <string name="change_app_theme">Alkalmazás témájának módosÃtása</string> | ||
| 377 | <string name="theme_default">Alapértelmezett</string> | ||
| 378 | <!-- Theme Modes --> | ||
| 379 | <string name="change_theme_mode">Téma váltása</string> | ||
| 380 | <string name="theme_mode_follow_system">RendszerbeállÃtások használata</string> | ||
| 381 | <string name="theme_mode_light">Világos</string> | ||
| 382 | <string name="theme_mode_dark">Sötét</string> | ||
| 383 | |||
| 384 | <!-- Audio output engines --> | ||
| 385 | <string name="cubeb">cubeb</string> | ||
| 386 | |||
| 387 | <!-- Black backgrounds theme --> | ||
| 388 | <string name="use_black_backgrounds">Fekete háttér</string> | ||
| 389 | <string name="use_black_backgrounds_description">Sötét téma használatakor fekete háttér használata.</string> | ||
| 390 | |||
| 391 | <!-- Picture-In-Picture --> | ||
| 392 | <string name="picture_in_picture">Kép a képben</string> | ||
| 393 | <string name="picture_in_picture_description">Ablak minimalizálása, amikor háttérbe kerül</string> | ||
| 394 | <string name="pause">Szünet</string> | ||
| 395 | <string name="play">Lejátszás</string> | ||
| 396 | <string name="mute">NémÃtás</string> | ||
| 397 | <string name="unmute">NémÃtás feloldása</string> | ||
| 398 | |||
| 399 | <!-- Licenses screen strings --> | ||
| 400 | <string name="licenses">Licenszek</string> | ||
| 401 | <string name="license_fidelityfx_fsr_description">Magas minőségű felskálázás az AMD-től</string> | ||
| 402 | </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 index 09c9345b0..5afebb4c4 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.<br /><br />Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string> | 4 | <string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.<br /><br />Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">L\'emulatore è attivo</string> | 5 | <string name="emulation_notification_channel_name">L\'emulatore è attivo</string> |
| @@ -13,9 +13,9 @@ | |||
| 13 | <string name="welcome">Benvenuto!</string> | 13 | <string name="welcome">Benvenuto!</string> |
| 14 | <string name="welcome_description">Scopri come configurare <b>yuzu</b> e passare all\'emulazione.</string> | 14 | <string name="welcome_description">Scopri come configurare <b>yuzu</b> e passare all\'emulazione.</string> |
| 15 | <string name="get_started">Iniziare</string> | 15 | <string name="get_started">Iniziare</string> |
| 16 | <string name="keys">Pulsanti</string> | 16 | <string name="keys">Chiavi</string> |
| 17 | <string name="keys_description">Seleziona il tuo file <b>prod.keys</b> con il pulsante in basso.</string> | 17 | <string name="keys_description">Seleziona il tuo file <b>prod.keys</b> con il pulsante in basso.</string> |
| 18 | <string name="select_keys">Selezione Pulsanti</string> | 18 | <string name="select_keys">Seleziona le chiavi</string> |
| 19 | <string name="games">Giochi</string> | 19 | <string name="games">Giochi</string> |
| 20 | <string name="games_description">Seleziona la cartella <b>Games</b> con il pulsante in basso.</string> | 20 | <string name="games_description">Seleziona la cartella <b>Games</b> con il pulsante in basso.</string> |
| 21 | <string name="done">Fatto</string> | 21 | <string name="done">Fatto</string> |
| @@ -25,6 +25,7 @@ | |||
| 25 | <string name="back">Indietro</string> | 25 | <string name="back">Indietro</string> |
| 26 | <string name="add_games">Aggiungi giochi</string> | 26 | <string name="add_games">Aggiungi giochi</string> |
| 27 | <string name="add_games_description">Seleziona la cartella dei giochi</string> | 27 | <string name="add_games_description">Seleziona la cartella dei giochi</string> |
| 28 | <string name="step_complete">Completato!</string> | ||
| 28 | 29 | ||
| 29 | <!-- Home strings --> | 30 | <!-- Home strings --> |
| 30 | <string name="home_games">Giochi</string> | 31 | <string name="home_games">Giochi</string> |
| @@ -38,6 +39,7 @@ | |||
| 38 | <string name="add_games_warning_description">I giochi non saranno mostrati nella lista dei giochi se una cartella non è selezionata.</string> | 39 | <string name="add_games_warning_description">I giochi non saranno mostrati nella lista dei giochi se una cartella non è selezionata.</string> |
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 40 | <string name="home_search_games">Cerca giochi</string> | 41 | <string name="home_search_games">Cerca giochi</string> |
| 42 | <string name="search_settings">Cerca impostazione</string> | ||
| 41 | <string name="games_dir_selected">Cartella dei giochi selezionata</string> | 43 | <string name="games_dir_selected">Cartella dei giochi selezionata</string> |
| 42 | <string name="install_prod_keys">Installa prod.keys</string> | 44 | <string name="install_prod_keys">Installa prod.keys</string> |
| 43 | <string name="install_prod_keys_description">Necessario per decrittografare i giochi</string> | 45 | <string name="install_prod_keys_description">Necessario per decrittografare i giochi</string> |
| @@ -61,15 +63,18 @@ | |||
| 61 | <string name="invalid_keys_file">Selezionate chiavi non valide</string> | 63 | <string name="invalid_keys_file">Selezionate chiavi non valide</string> |
| 62 | <string name="install_keys_success">Chiavi installate correttamente</string> | 64 | <string name="install_keys_success">Chiavi installate correttamente</string> |
| 63 | <string name="reading_keys_failure">Errore durante la lettura delle chiavi di crittografia</string> | 65 | <string name="reading_keys_failure">Errore durante la lettura delle chiavi di crittografia</string> |
| 66 | <string name="install_prod_keys_failure_extension_description">Controlla che le tue chiavi abbiano l\'estensione .keys e prova di nuovo.</string> | ||
| 67 | <string name="install_amiibo_keys_failure_extension_description">Controlla che le tue chiavi abbiano l\'estensione .bin e prova di nuovo</string> | ||
| 64 | <string name="invalid_keys_error">Chiavi di crittografia non valide</string> | 68 | <string name="invalid_keys_error">Chiavi di crittografia non valide</string> |
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 69 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">Il file selezionato è incorretto o corrotto. Per favore riesegui il dump delle tue chiavi.</string> | 70 | <string name="install_keys_failure_description">Il file selezionato è incorretto o corrotto. Per favore riesegui il dump delle tue chiavi.</string> |
| 67 | <string name="install_gpu_driver">Installa i driver GPU</string> | 71 | <string name="install_gpu_driver">Installa i driver GPU</string> |
| 68 | <string name="install_gpu_driver_description">Installa driver alternativi per potenziali prestazioni migliori o accuratezza.</string> | 72 | <string name="install_gpu_driver_description">Installa driver alternativi per potenziali prestazioni migliori o accuratezza.</string> |
| 69 | <string name="advanced_settings">Impostazioni avanzate</string> | 73 | <string name="advanced_settings">Impostazioni avanzate</string> |
| 74 | <string name="advanced_settings_game">Impostazioni Avanzate: %1$s</string> | ||
| 70 | <string name="settings_description">Configura le impostazioni dell\'emulatore</string> | 75 | <string name="settings_description">Configura le impostazioni dell\'emulatore</string> |
| 71 | <string name="search_recently_played">Giocato recentemente</string> | 76 | <string name="search_recently_played">Giocati recentemente</string> |
| 72 | <string name="search_recently_added">Aggiunto recentemente</string> | 77 | <string name="search_recently_added">Aggiunti recentemente</string> |
| 73 | <string name="search_retail">Rivenditore</string> | 78 | <string name="search_retail">Rivenditore</string> |
| 74 | <string name="search_homebrew">Homebrew</string> | 79 | <string name="search_homebrew">Homebrew</string> |
| 75 | <string name="open_user_folder">Apri la cartella di yuzu</string> | 80 | <string name="open_user_folder">Apri la cartella di yuzu</string> |
| @@ -86,6 +91,33 @@ | |||
| 86 | <string name="save_file_invalid_zip_structure_description">La prima sotto cartella <b>deve</b> chiamarsi come l\'ID del titolo del gioco.</string> | 91 | <string name="save_file_invalid_zip_structure_description">La prima sotto cartella <b>deve</b> chiamarsi come l\'ID del titolo del gioco.</string> |
| 87 | <string name="import_saves">Importa</string> | 92 | <string name="import_saves">Importa</string> |
| 88 | <string name="export_saves">Esporta</string> | 93 | <string name="export_saves">Esporta</string> |
| 94 | <string name="install_firmware">Installa firmware</string> | ||
| 95 | <string name="install_firmware_description">Il firmware deve essere in un archivio ZIP ed è necessario per avviare alcuni giochi</string> | ||
| 96 | <string name="firmware_installing">Installando il firmware</string> | ||
| 97 | <string name="firmware_installed_success">Firmware installato con successo</string> | ||
| 98 | <string name="firmware_installed_failure">L\'installazione del firmware è fallita</string> | ||
| 99 | <string name="firmware_installed_failure_description">Accertati che i file .nca del firmware siano contenuti direttamente nella radice dello .zip e riprova.</string> | ||
| 100 | <string name="share_log">Condividi log di debug</string> | ||
| 101 | <string name="share_log_description">Condividi i log di yuzu per ricevere supporto</string> | ||
| 102 | <string name="share_log_missing">Nessun file di log trovato</string> | ||
| 103 | <string name="install_game_content">Installa contenuti di gioco</string> | ||
| 104 | <string name="install_game_content_description">Installa aggiornamenti o DLC</string> | ||
| 105 | <string name="installing_game_content">Installazione dei contenuti...</string> | ||
| 106 | <string name="install_game_content_failure">Errore durante l\'installazione del contenuto in NAND.</string> | ||
| 107 | <string name="install_game_content_failure_description">Accertati che i contenuti da installare siano validi e che le prod.keys siano presenti.</string> | ||
| 108 | <string name="install_game_content_failure_base">Installare i giochi base in NAND non è permesso, perché potrebbe causare dei conflitti con altri tipi di contenuti(Aggiornamenti e DLC)</string> | ||
| 109 | <string name="install_game_content_failure_file_extension">Solo i tipi NSP e XCI sono supportati. Verifica che i contenuti di gioco siano validi.</string> | ||
| 110 | <string name="install_game_content_failed_count">Errori di installazione: %1$d</string> | ||
| 111 | <string name="install_game_content_success">Contenuto/i di gioco installato/i con successo.</string> | ||
| 112 | <string name="install_game_content_success_install">%1$dinstallato con successo.</string> | ||
| 113 | <string name="install_game_content_success_overwrite">%1$dsovrascritto con successo</string> | ||
| 114 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 115 | <string name="custom_driver_not_supported">I driver personalizzati non sono supportati.</string> | ||
| 116 | <string name="custom_driver_not_supported_description">I driver personalizzati non sono attualmente supportati su questo dispositivo.\n Ricontrolla in futuro.</string> | ||
| 117 | <string name="manage_yuzu_data">Gestisci i dati di Yuzu</string> | ||
| 118 | <string name="manage_yuzu_data_description">Importa/Esporta il firmware, le keys, i dati utente, e altro!</string> | ||
| 119 | <string name="share_save_file">Condividi i tuoi dati di salvataggio</string> | ||
| 120 | <string name="export_save_failed">Errore durante l\'esportazione del salvataggio</string> | ||
| 89 | 121 | ||
| 90 | <!-- About screen strings --> | 122 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">Gaia non è reale</string> | 123 | <string name="gaia_is_not_real">Gaia non è reale</string> |
| @@ -94,7 +126,18 @@ | |||
| 94 | <string name="contributors">Collaboratori</string> | 126 | <string name="contributors">Collaboratori</string> |
| 95 | <string name="contributors_description">Realizzato con \u2764 dal team yuzu</string> | 127 | <string name="contributors_description">Realizzato con \u2764 dal team yuzu</string> |
| 96 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 128 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 129 | <string name="licenses_description">Progetti che rendono yuzu per Android possibile</string> | ||
| 97 | <string name="build">Compilazione</string> | 130 | <string name="build">Compilazione</string> |
| 131 | <string name="user_data">Dati Utente</string> | ||
| 132 | <string name="user_data_description">Importa/Esporta tutti i dati dell\'applicazione.\n\nDurante l\'importazione dei Dati Utente, quelli già esistenti verranno ELIMINATI.</string> | ||
| 133 | <string name="exporting_user_data">Esportazione dei Dati Utente...</string> | ||
| 134 | <string name="importing_user_data">Importazione dei Dati Utente...</string> | ||
| 135 | <string name="import_user_data">Importa i Dati Utente</string> | ||
| 136 | <string name="invalid_yuzu_backup">Backup di Yuzu Invalido</string> | ||
| 137 | <string name="user_data_export_success">Dati Utente esportati con successo</string> | ||
| 138 | <string name="user_data_import_success">Dati Utente importati con successo.</string> | ||
| 139 | <string name="user_data_export_cancelled">Esportazione annullata</string> | ||
| 140 | <string name="user_data_import_failed_description">Assicurati che la cartella dei Dati dell\'utente stiano nella radice del file.zip e che sia presente una cartella config in config/config.ini, poi, riprova.</string> | ||
| 98 | <string name="support_link">https://discord.gg/u77vRWY</string> | 141 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 99 | <string name="website_link">https://yuzu-emu.org/</string> | 142 | <string name="website_link">https://yuzu-emu.org/</string> |
| 100 | <string name="github_link">https://github.com/yuzu-emu</string> | 143 | <string name="github_link">https://github.com/yuzu-emu</string> |
| @@ -114,41 +157,53 @@ | |||
| 114 | <string name="are_you_interested">Sei interessato?</string> | 157 | <string name="are_you_interested">Sei interessato?</string> |
| 115 | 158 | ||
| 116 | <!-- General settings strings --> | 159 | <!-- General settings strings --> |
| 117 | <string name="frame_limit_enable">Abilita il limite di velocità </string> | 160 | <string name="frame_limit_enable">Limita velocità </string> |
| 118 | <string name="frame_limit_enable_description">Quando abilitato, la velocità di emulazione verrà limitata a una specifica percentuale della velocità normale.</string> | 161 | <string name="frame_limit_enable_description">Limita la velocità dell\'emulazione a una specifica percentuale della velocità normale.</string> |
| 119 | <string name="frame_limit_slider">Limite velocità percentuale</string> | 162 | <string name="frame_limit_slider">Limite velocità percentuale</string> |
| 120 | <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> | 163 | <string name="frame_limit_slider_description">Specifica la percentuale per limitare la velocità di emulazione. 100% è la velocità normale. Valori maggiori o minori aumenteranno o diminuiranno il limite di velocità </string> |
| 121 | <string name="cpu_accuracy">Accuratezza della CPU</string> | 164 | <string name="cpu_accuracy">Accuratezza della CPU</string> |
| 165 | <string name="value_with_units">%1$s%2$s</string> | ||
| 122 | 166 | ||
| 123 | <!-- System settings strings --> | 167 | <!-- System settings strings --> |
| 124 | <string name="use_docked_mode">Modalità docked</string> | 168 | <string name="use_docked_mode">Modalità Docked</string> |
| 125 | <string name="use_docked_mode_description">Emula in modalità docked, questo aumenta la risoluzione a spese delle performance.</string> | 169 | <string name="use_docked_mode_description">Aumenta la risoluzione, diminuendo le performance. La modalità portatile è usata quando disabilitato, diminuendo la risoluzione e aumentando le performance.</string> |
| 126 | <string name="emulated_region">Regione emulata</string> | 170 | <string name="emulated_region">Regione emulata</string> |
| 127 | <string name="emulated_language">Lingua emulata</string> | 171 | <string name="emulated_language">Lingua emulata</string> |
| 128 | <string name="select_rtc_date">Seleziona la data dall\'orologio in tempo reale</string> | 172 | <string name="select_rtc_date">Imposta la data </string> |
| 129 | <string name="select_rtc_time">Seleziona il tempo dall\'orologio in tempo reale</string> | 173 | <string name="select_rtc_time">Imposta l\'ora, i minuti e i secondi.</string> |
| 130 | <string name="use_custom_rtc">Abilità l\'orologio in tempo reale personalizzato</string> | 174 | <string name="use_custom_rtc">RTC Personalizzato</string> |
| 131 | <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> | 175 | <string name="use_custom_rtc_description">Ti permette di impostare un orologio in tempo reale personalizzato, completamente separato da quello di sistema.</string> |
| 132 | <string name="set_custom_rtc">Imposta l\'orologio in tempo reale personalizzato</string> | 176 | <string name="set_custom_rtc">Imposta un orologio in tempo reale personalizzato</string> |
| 133 | 177 | ||
| 134 | <!-- Graphics settings strings --> | 178 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">API</string> | ||
| 136 | <string name="renderer_accuracy">Livello di accuratezza</string> | 179 | <string name="renderer_accuracy">Livello di accuratezza</string> |
| 137 | <string name="renderer_resolution">Risoluzione</string> | 180 | <string name="renderer_resolution">Risoluzione (Portatile/Docked)</string> |
| 138 | <string name="renderer_vsync">Modalità VSync</string> | 181 | <string name="renderer_vsync">Modalità VSync</string> |
| 139 | <string name="renderer_aspect_ratio">Rapporto d\'aspetto</string> | 182 | <string name="renderer_screen_layout">Orientamento</string> |
| 140 | <string name="renderer_scaling_filter">Filtro di adattamento alla finestra</string> | 183 | <string name="renderer_aspect_ratio">Rapporto d\'aspetto: </string> |
| 184 | <string name="renderer_scaling_filter">Filtro adattivo della finestra </string> | ||
| 141 | <string name="renderer_anti_aliasing">Metodo di anti-aliasing</string> | 185 | <string name="renderer_anti_aliasing">Metodo di anti-aliasing</string> |
| 142 | <string name="renderer_force_max_clock">Forza clock massimi (solo Adreno)</string> | 186 | <string name="renderer_force_max_clock">Forza clock massimi (solo Adreno)</string> |
| 143 | <string name="renderer_force_max_clock_description">Forza la GPU a girare col massimo clock possibile (i vincoli alla temperatura saranno comunque applicati)</string> | 187 | <string name="renderer_force_max_clock_description">Forza la GPU a girare col massimo clock possibile (i vincoli alla temperatura saranno comunque applicati)</string> |
| 144 | <string name="renderer_asynchronous_shaders">Usa shaders asincrone</string> | 188 | <string name="renderer_asynchronous_shaders">Usa shaders asincrone</string> |
| 145 | <string name="renderer_asynchronous_shaders_description">Compila le shaders asincronamente, questo riduce lo shutter ma potrebbe introdurre dei glitch. </string> | 189 | <string name="renderer_asynchronous_shaders_description">Compila le shader in modo asincrone, riducendo lo stutter. Può causare glitch grafici.</string> |
| 146 | <string name="renderer_debug">Abilità il debug grafico</string> | 190 | <string name="renderer_reactive_flushing">Abilita il Reactive Flushing</string> |
| 147 | <string name="renderer_debug_description">Quando l\'opzione è selezionata, l\'API grafica entra in una modalità di debug più lenta</string> | 191 | <string name="renderer_reactive_flushing_description">Migliora l\'accuratezza della grafica in alcuni giochi, al costo delle performance.</string> |
| 148 | <string name="use_disk_shader_cache">Usa cache shader su disco</string> | 192 | <string name="use_disk_shader_cache">Usa la cache delle shader</string> |
| 149 | <string name="use_disk_shader_cache_description">Riduce lo stuttering salvando e caricando le shader generate sul disco.</string> | 193 | <string name="use_disk_shader_cache_description">Riduce lo stuttering caricando le shader già compilate all\'avvio.</string> |
| 194 | |||
| 195 | <!-- Debug settings strings --> | ||
| 196 | <string name="cpu">CPU</string> | ||
| 197 | <string name="cpu_debug_mode">Debug della CPU</string> | ||
| 198 | <string name="cpu_debug_mode_description">Imposta la CPU in modalità Debug (Più lento)</string> | ||
| 199 | <string name="gpu">GPU</string> | ||
| 200 | <string name="renderer_api">API</string> | ||
| 201 | <string name="renderer_debug">Debug GPU</string> | ||
| 202 | <string name="renderer_debug_description">Imposta l\'API grafica in uno stato dedicato al Debugging. Impatta di molto sulle performance.</string> | ||
| 203 | <string name="fastmem">Fastmem</string> | ||
| 150 | 204 | ||
| 151 | <!-- Audio settings strings --> | 205 | <!-- Audio settings strings --> |
| 206 | <string name="audio_output_engine">Motore di Output</string> | ||
| 152 | <string name="audio_volume">Volume</string> | 207 | <string name="audio_volume">Volume</string> |
| 153 | <string name="audio_volume_description">Specifica il volume dell\'audio in uscita.</string> | 208 | <string name="audio_volume_description">Specifica il volume dell\'audio in uscita.</string> |
| 154 | 209 | ||
| @@ -157,14 +212,24 @@ | |||
| 157 | <string name="ini_saved">Impostazioni salvate</string> | 212 | <string name="ini_saved">Impostazioni salvate</string> |
| 158 | <string name="gameid_saved">Impostazioni salvate per %1$s</string> | 213 | <string name="gameid_saved">Impostazioni salvate per %1$s</string> |
| 159 | <string name="error_saving">Errore nel salvare %1$s.ini %2$s</string> | 214 | <string name="error_saving">Errore nel salvare %1$s.ini %2$s</string> |
| 215 | <string name="unimplemented_menu">Menu non implementato</string> | ||
| 160 | <string name="loading">Caricamento…</string> | 216 | <string name="loading">Caricamento…</string> |
| 217 | <string name="shutting_down">Spegnimento...</string> | ||
| 161 | <string name="reset_setting_confirmation">Vuoi ripristinare queste impostazioni al loro valore originale?</string> | 218 | <string name="reset_setting_confirmation">Vuoi ripristinare queste impostazioni al loro valore originale?</string> |
| 162 | <string name="reset_to_default">Riportare alle impostazioni originali</string> | 219 | <string name="reset_to_default">Riportare alle impostazioni originali</string> |
| 163 | <string name="reset_all_settings">Resettare tutte le impostazioni?</string> | 220 | <string name="reset_all_settings">Resettare tutte le impostazioni?</string> |
| 164 | <string name="reset_all_settings_description">Tutte le Impostazioni Avanzate saranno ripristinate a quelle originali. Questa operazione non è reversibile</string> | 221 | <string name="reset_all_settings_description">Le impostazione avanzate verranno completamente reimpostate. Questa operazione è IRREVERSIBILE.</string> |
| 165 | <string name="settings_reset">Reimposta le impostazioni</string> | 222 | <string name="settings_reset">Reimposta le impostazioni</string> |
| 166 | <string name="close">Chiudi</string> | 223 | <string name="close">Chiudi</string> |
| 167 | <string name="learn_more">Per saperne di più</string> | 224 | <string name="learn_more">Per saperne di più</string> |
| 225 | <string name="auto">Automatico</string> | ||
| 226 | <string name="submit">Invia</string> | ||
| 227 | <string name="string_null">Nullo</string> | ||
| 228 | <string name="string_import">Importa</string> | ||
| 229 | <string name="export">Esporta</string> | ||
| 230 | <string name="export_failed">Esportazione Fallita</string> | ||
| 231 | <string name="import_failed">Importazione Fallita</string> | ||
| 232 | <string name="cancelling">Cancellazione</string> | ||
| 168 | 233 | ||
| 169 | <!-- GPU driver installation --> | 234 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">Seleziona il driver della GPU</string> | 235 | <string name="select_gpu_driver">Seleziona il driver della GPU</string> |
| @@ -172,6 +237,7 @@ | |||
| 172 | <string name="select_gpu_driver_install">Installa</string> | 237 | <string name="select_gpu_driver_install">Installa</string> |
| 173 | <string name="select_gpu_driver_default">Predefinito</string> | 238 | <string name="select_gpu_driver_default">Predefinito</string> |
| 174 | <string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string> | 239 | <string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string> |
| 240 | <string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string> | ||
| 175 | <string name="system_gpu_driver">Driver GPU del sistema</string> | 241 | <string name="system_gpu_driver">Driver GPU del sistema</string> |
| 176 | <string name="installing_driver">Installando i driver...</string> | 242 | <string name="installing_driver">Installando i driver...</string> |
| 177 | 243 | ||
| @@ -182,10 +248,11 @@ | |||
| 182 | <string name="preferences_graphics">Grafica</string> | 248 | <string name="preferences_graphics">Grafica</string> |
| 183 | <string name="preferences_audio">Audio</string> | 249 | <string name="preferences_audio">Audio</string> |
| 184 | <string name="preferences_theme">Tema e colori</string> | 250 | <string name="preferences_theme">Tema e colori</string> |
| 251 | <string name="preferences_debug">Debug</string> | ||
| 185 | 252 | ||
| 186 | <!-- ROM loading errors --> | 253 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">La tua ROM è criptata</string> | 254 | <string name="loader_error_encrypted">La tua ROM è criptata</string> |
| 188 | <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> | 255 | <string name="loader_error_encrypted_roms_description"><![CDATA[Segui la nostra guida per fare il <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">dump delle tue cartucce di gioco</a>oppure <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">dei titoli già installati</a>.]]></string> |
| 189 | <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> | 256 | <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> |
| 190 | <string name="loader_error_video_core">È stato riscontrato un errore nell\'inizializzazione del core video</string> | 257 | <string name="loader_error_video_core">È stato riscontrato un errore nell\'inizializzazione del core video</string> |
| 191 | <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> | 258 | <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> |
| @@ -193,28 +260,28 @@ | |||
| 193 | <string name="loader_error_file_not_found">Il file della ROM non esiste</string> | 260 | <string name="loader_error_file_not_found">Il file della ROM non esiste</string> |
| 194 | 261 | ||
| 195 | <!-- Emulation Menu --> | 262 | <!-- Emulation Menu --> |
| 196 | <string name="emulation_exit">Uscire dall\'emulazione</string> | 263 | <string name="emulation_exit">Arresta emulazione</string> |
| 197 | <string name="emulation_done">Fatto</string> | 264 | <string name="emulation_done">Fatto</string> |
| 198 | <string name="emulation_fps_counter">Contatore degli FPS</string> | 265 | <string name="emulation_fps_counter">Contatore FPS</string> |
| 199 | <string name="emulation_toggle_controls">Controlli a interruttore</string> | 266 | <string name="emulation_toggle_controls">Controlli a interruttore</string> |
| 200 | <string name="emulation_rel_stick_center">Centro relativo degli Stick</string> | 267 | <string name="emulation_rel_stick_center">Centro relativo degli Stick</string> |
| 201 | <string name="emulation_dpad_slide">Slittamento del Pad Direzionale</string> | 268 | <string name="emulation_dpad_slide">DPad A Scorrimento</string> |
| 202 | <string name="emulation_haptics">Aptico</string> | 269 | <string name="emulation_haptics">Feedback Aptico</string> |
| 203 | <string name="emulation_show_overlay">Mostra Overlay</string> | 270 | <string name="emulation_show_overlay">Mostra l\'Overlay</string> |
| 204 | <string name="emulation_toggle_all">Attiva/disattiva tutto</string> | 271 | <string name="emulation_toggle_all">Attiva/Disattiva tutto</string> |
| 205 | <string name="emulation_control_adjust">Aggiusta Overlay</string> | 272 | <string name="emulation_control_adjust">Modifica l\'Overlay</string> |
| 206 | <string name="emulation_control_scale">Scala</string> | 273 | <string name="emulation_control_scale">Scala</string> |
| 207 | <string name="emulation_control_opacity">Opacità </string> | 274 | <string name="emulation_control_opacity">Opacità </string> |
| 208 | <string name="emulation_touch_overlay_reset">Reimposta Overlay</string> | 275 | <string name="emulation_touch_overlay_reset">Reimposta l\'Overlay</string> |
| 209 | <string name="emulation_touch_overlay_edit">Modifica Overlay</string> | 276 | <string name="emulation_touch_overlay_edit">Modifica l\'Overlay</string> |
| 210 | <string name="emulation_pause">Metti in pausa l\'emulazione</string> | 277 | <string name="emulation_pause">Sospendi l\'emulazione</string> |
| 211 | <string name="emulation_unpause">Riprendi Emulazione</string> | 278 | <string name="emulation_unpause">Riprendi l\'emulazione</string> |
| 212 | <string name="emulation_input_overlay">Impostazioni Overlay</string> | 279 | <string name="emulation_input_overlay">Opzioni overlay</string> |
| 213 | 280 | ||
| 214 | <string name="load_settings">Caricamento delle impostazioni...</string> | 281 | <string name="load_settings">Carico le impostazioni...</string> |
| 215 | 282 | ||
| 216 | <!-- Software keyboard --> | 283 | <!-- Software keyboard --> |
| 217 | <string name="software_keyboard">Tastiera software</string> | 284 | <string name="software_keyboard">Tastiera Software</string> |
| 218 | 285 | ||
| 219 | <!-- Errors and warnings --> | 286 | <!-- Errors and warnings --> |
| 220 | <string name="abort_button">Interrompi</string> | 287 | <string name="abort_button">Interrompi</string> |
| @@ -226,6 +293,9 @@ | |||
| 226 | <string name="fatal_error">Errore Fatale</string> | 293 | <string name="fatal_error">Errore Fatale</string> |
| 227 | <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> | 294 | <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> |
| 228 | <string name="performance_warning">Disattivare questa impostazione può ridurre significativamente le performance di emulazione! Per una migliore esperienza, è consigliato lasciare questa impostazione attivata.</string> | 295 | <string name="performance_warning">Disattivare questa impostazione può ridurre significativamente le performance di emulazione! Per una migliore esperienza, è consigliato lasciare questa impostazione attivata.</string> |
| 296 | <string name="device_memory_inadequate">RAM Totale:%1$s\nRaccomandati: %2$s</string> | ||
| 297 | <string name="memory_formatted">%1$s%2$s</string> | ||
| 298 | <string name="no_game_present">Non è presente alcun gioco avviabile.</string> | ||
| 229 | 299 | ||
| 230 | <!-- Region Names --> | 300 | <!-- Region Names --> |
| 231 | <string name="region_japan">Giappone</string> | 301 | <string name="region_japan">Giappone</string> |
| @@ -236,7 +306,14 @@ | |||
| 236 | <string name="region_korea">Corea</string> | 306 | <string name="region_korea">Corea</string> |
| 237 | <string name="region_taiwan">Taiwan</string> | 307 | <string name="region_taiwan">Taiwan</string> |
| 238 | 308 | ||
| 239 | <!-- Language Names --> | 309 | <!-- Memory Sizes --> |
| 310 | <string name="memory_byte">Byte</string> | ||
| 311 | <string name="memory_kilobyte">Kb</string> | ||
| 312 | <string name="memory_megabyte">Mb</string> | ||
| 313 | <string name="memory_gigabyte">GB</string> | ||
| 314 | <string name="memory_terabyte">Tb</string> | ||
| 315 | <string name="memory_petabyte">Pb</string> | ||
| 316 | <string name="memory_exabyte">Eb</string> | ||
| 240 | 317 | ||
| 241 | <!-- Renderer APIs --> | 318 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">Vulkan</string> | 319 | <string name="renderer_vulkan">Vulkan</string> |
| @@ -274,12 +351,17 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">FXAA</string> | 351 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 275 | <string name="anti_aliasing_smaa">SMAA</string> | 352 | <string name="anti_aliasing_smaa">SMAA</string> |
| 276 | 353 | ||
| 354 | <!-- Screen Layouts --> | ||
| 355 | <string name="screen_layout_landscape">Layout Orizzontale</string> | ||
| 356 | <string name="screen_layout_portrait">Layout Verticale</string> | ||
| 357 | <string name="screen_layout_auto">Automatico</string> | ||
| 358 | |||
| 277 | <!-- Aspect Ratios --> | 359 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">Predefinito (16:9)</string> | 360 | <string name="ratio_default">Predefinito (16:9)</string> |
| 279 | <string name="ratio_force_four_three">Forza 4:3</string> | 361 | <string name="ratio_force_four_three">Forza 4:3</string> |
| 280 | <string name="ratio_force_twenty_one_nine">Forza 21:9</string> | 362 | <string name="ratio_force_twenty_one_nine">Forza 21:9</string> |
| 281 | <string name="ratio_force_sixteen_ten">Forza 16:10</string> | 363 | <string name="ratio_force_sixteen_ten">Forza 16:10</string> |
| 282 | <string name="ratio_stretch">Allunga a finestra</string> | 364 | <string name="ratio_stretch">Adatta alla finestra</string> |
| 283 | 365 | ||
| 284 | <!-- CPU Accuracy --> | 366 | <!-- CPU Accuracy --> |
| 285 | <string name="cpu_accuracy_accurate">Accurata</string> | 367 | <string name="cpu_accuracy_accurate">Accurata</string> |
| @@ -287,9 +369,9 @@ | |||
| 287 | <string name="cpu_accuracy_paranoid">Paranoico (Lento)</string> | 369 | <string name="cpu_accuracy_paranoid">Paranoico (Lento)</string> |
| 288 | 370 | ||
| 289 | <!-- Gamepad Buttons --> | 371 | <!-- Gamepad Buttons --> |
| 290 | <string name="gamepad_d_pad">D-Pad</string> | 372 | <string name="gamepad_d_pad">D-pad</string> |
| 291 | <string name="gamepad_left_stick">Levetta sinistra</string> | 373 | <string name="gamepad_left_stick">Analogico sinistro</string> |
| 292 | <string name="gamepad_right_stick">Levetta destra</string> | 374 | <string name="gamepad_right_stick">Analogico destro</string> |
| 293 | <string name="gamepad_home">Home</string> | 375 | <string name="gamepad_home">Home</string> |
| 294 | <string name="gamepad_screenshot">Screenshot</string> | 376 | <string name="gamepad_screenshot">Screenshot</string> |
| 295 | 377 | ||
| @@ -298,7 +380,7 @@ | |||
| 298 | <string name="building_shaders">Costruendo gli shaders</string> | 380 | <string name="building_shaders">Costruendo gli shaders</string> |
| 299 | 381 | ||
| 300 | <!-- Theme options --> | 382 | <!-- Theme options --> |
| 301 | <string name="change_app_theme">Cambia il tema dell\'app</string> | 383 | <string name="change_app_theme">Cambia tema dell\'app</string> |
| 302 | <string name="theme_default">Predefinito</string> | 384 | <string name="theme_default">Predefinito</string> |
| 303 | <string name="theme_material_you">Material You</string> | 385 | <string name="theme_material_you">Material You</string> |
| 304 | 386 | ||
| @@ -308,8 +390,22 @@ | |||
| 308 | <string name="theme_mode_light">Chiaro</string> | 390 | <string name="theme_mode_light">Chiaro</string> |
| 309 | <string name="theme_mode_dark">Scuro</string> | 391 | <string name="theme_mode_dark">Scuro</string> |
| 310 | 392 | ||
| 393 | <!-- Audio output engines --> | ||
| 394 | <string name="cubeb">cubeb</string> | ||
| 395 | |||
| 311 | <!-- Black backgrounds theme --> | 396 | <!-- Black backgrounds theme --> |
| 312 | <string name="use_black_backgrounds">Usa sfondi neri</string> | 397 | <string name="use_black_backgrounds">Sfondi neri</string> |
| 313 | <string name="use_black_backgrounds_description">Quando utilizzi il tema scuro, applica sfondi neri.</string> | 398 | <string name="use_black_backgrounds_description">Quando utilizzi il tema scuro, applica sfondi neri.</string> |
| 314 | 399 | ||
| 315 | </resources> | 400 | <!-- Picture-In-Picture --> |
| 401 | <string name="picture_in_picture">Picture in Picture</string> | ||
| 402 | <string name="picture_in_picture_description">Minimizza la finestra quando viene impostata in background</string> | ||
| 403 | <string name="pause">Pausa</string> | ||
| 404 | <string name="play">Gioca</string> | ||
| 405 | <string name="mute">Silenzia</string> | ||
| 406 | <string name="unmute">Riattiva</string> | ||
| 407 | |||
| 408 | <!-- Licenses screen strings --> | ||
| 409 | <string name="licenses">Licenze</string> | ||
| 410 | <string name="license_fidelityfx_fsr_description">Upscaling di alta qualità da parte di AMD</string> | ||
| 411 | </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 index a0ea78bef..3be4e7d26 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml | |||
| @@ -1,11 +1,12 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">ã“ã®ã‚½ãƒ•トウェアã¯ã€Nintendo Switch用ã®ã‚²ãƒ¼ãƒ を実行ã—ã¾ã™ã€‚ ゲームソフトやã‚ーã¯å«ã¾ã‚Œã¾ã›ã‚“。<br /><br />事å‰ã«ã€ <![CDATA[<b> prod.keys </b>]]> ファイルをデãƒã‚¤ã‚¹ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã«é…ç½®ã—ã¦ãŠã„ã¦ãã ã•ã„。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string> | 4 | <string name="app_disclaimer">ã“ã®ã‚½ãƒ•トウェアã§ã¯ã€Nintendo Switchã®ã‚²ãƒ¼ãƒ を実行ã§ãã¾ã™ã€‚ ゲームソフトやã‚ーã¯å«ã¾ã‚Œã¾ã›ã‚“。<br /><br />事å‰ã«ã€ <![CDATA[<b> prod.keys </b>]]> ファイルをストレージã«é…ç½®ã—ã¦ãŠã„ã¦ãã ã•ã„。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãŒæœ‰åйã§ã™</string> | 5 | <string name="emulation_notification_channel_name">ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãŒæœ‰åйã§ã™</string> |
| 6 | <string name="emulation_notification_channel_description">エミュレーションã®å®Ÿè¡Œä¸ã«å¸¸è¨é€šçŸ¥ã‚’表示ã—ã¾ã™ã€‚</string> | 6 | <string name="emulation_notification_channel_description">エミュレーションã®å®Ÿè¡Œä¸ã«å¸¸è¨é€šçŸ¥ã‚’表示ã—ã¾ã™ã€‚</string> |
| 7 | <string name="emulation_notification_running">yuzu ã¯å®Ÿè¡Œä¸ã§ã™</string> | 7 | <string name="emulation_notification_running">yuzu ã¯å®Ÿè¡Œä¸ã§ã™</string> |
| 8 | <string name="notice_notification_channel_description">å•題ãŒç™ºç”Ÿã—ãŸã¨ãã«é€šçŸ¥ã‚’表示ã—ã¾ã™ã€‚</string> | 8 | <string name="notice_notification_channel_name">通知ã¨ã‚¨ãƒ©ãƒ¼</string> |
| 9 | <string name="notice_notification_channel_description">å•題ã®ç™ºç”Ÿæ™‚ã«é€šçŸ¥ã‚’表示ã—ã¾ã™ã€‚</string> | ||
| 9 | <string name="notification_permission_not_granted">通知ãŒè¨±å¯ã•れã¦ã„ã¾ã›ã‚“!</string> | 10 | <string name="notification_permission_not_granted">通知ãŒè¨±å¯ã•れã¦ã„ã¾ã›ã‚“!</string> |
| 10 | 11 | ||
| 11 | <!-- Setup strings --> | 12 | <!-- Setup strings --> |
| @@ -16,7 +17,7 @@ | |||
| 16 | <string name="keys_description">下ã®ãƒœã‚¿ãƒ³ã‹ã‚‰ <b>prod.keys</b> ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„。</string> | 17 | <string name="keys_description">下ã®ãƒœã‚¿ãƒ³ã‹ã‚‰ <b>prod.keys</b> ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„。</string> |
| 17 | <string name="select_keys">ã‚ãƒ¼ã‚’é¸æŠž</string> | 18 | <string name="select_keys">ã‚ãƒ¼ã‚’é¸æŠž</string> |
| 18 | <string name="games">ゲーム</string> | 19 | <string name="games">ゲーム</string> |
| 19 | <string name="games_description">下ã®ãƒœã‚¿ãƒ³ã‹ã‚‰<b>ゲーム</b>ãŒã‚ã‚‹ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠžã—ã¦ãã ã•ã„。</string> | 20 | <string name="games_description">下ã®ãƒœã‚¿ãƒ³ã‹ã‚‰<b>ゲーム</b>ã®ã‚ã‚‹ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠžã—ã¦ãã ã•ã„。</string> |
| 20 | <string name="done">完了</string> | 21 | <string name="done">完了</string> |
| 21 | <string name="done_description">準備ãŒå®Œäº†ã—ã¾ã—ãŸã€‚\nã‚²ãƒ¼ãƒ ã‚’ãŠæ¥½ã—ã¿ãã ã•ã„!</string> | 22 | <string name="done_description">準備ãŒå®Œäº†ã—ã¾ã—ãŸã€‚\nã‚²ãƒ¼ãƒ ã‚’ãŠæ¥½ã—ã¿ãã ã•ã„!</string> |
| 22 | <string name="text_continue">続行</string> | 23 | <string name="text_continue">続行</string> |
| @@ -24,48 +25,53 @@ | |||
| 24 | <string name="back">戻る</string> | 25 | <string name="back">戻る</string> |
| 25 | <string name="add_games">ã‚²ãƒ¼ãƒ ã‚’è¿½åŠ </string> | 26 | <string name="add_games">ã‚²ãƒ¼ãƒ ã‚’è¿½åŠ </string> |
| 26 | <string name="add_games_description">ã‚²ãƒ¼ãƒ ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠž</string> | 27 | <string name="add_games_description">ã‚²ãƒ¼ãƒ ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠž</string> |
| 28 | <string name="step_complete">完了!</string> | ||
| 27 | 29 | ||
| 28 | <!-- Home strings --> | 30 | <!-- Home strings --> |
| 29 | <string name="home_games">ゲーム</string> | 31 | <string name="home_games">ゲーム</string> |
| 30 | <string name="home_search">検索</string> | 32 | <string name="home_search">検索</string> |
| 31 | <string name="home_settings">è¨å®š</string> | 33 | <string name="home_settings">è¨å®š</string> |
| 32 | <string name="empty_gamelist">ファイルãŒè¦‹ã¤ã‹ã‚‰ãªã„ã‹ã€ã‚²ãƒ¼ãƒ ディレクトリãŒã¾ã é¸æŠžã•れã¦ã„ã¾ã›ã‚“。</string> | 34 | <string name="empty_gamelist">ファイルãŒå˜åœ¨ã—ãªã„ã‹ã‚²ãƒ¼ãƒ フォルダãŒé¸æŠžã•れã¦ã„ã¾ã›ã‚“。</string> |
| 33 | <string name="search_and_filter_games">ã‚²ãƒ¼ãƒ ã®æ¤œç´¢ã¨çµžã‚Šè¾¼ã¿</string> | 35 | <string name="search_and_filter_games">ã‚²ãƒ¼ãƒ ã®æ¤œç´¢ã¨çµžã‚Šè¾¼ã¿</string> |
| 34 | <string name="select_games_folder">ã‚²ãƒ¼ãƒ ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠž</string> | 36 | <string name="select_games_folder">ゲームフォルダ</string> |
| 35 | <string name="select_games_folder_description">yuzu ãŒã‚²ãƒ¼ãƒ リストã«è¿½åŠ ã§ãるよã†ã«ã—ã¾ã™</string> | 37 | <string name="select_games_folder_description">ゲームをyuzuã®ã‚²ãƒ¼ãƒ リストã«è¿½åŠ ã—ã¾ã™</string> |
| 36 | <string name="add_games_warning">ゲームフォルダã®é¸æŠžã‚’スã‚ップã—ã¾ã™ã‹?</string> | 38 | <string name="add_games_warning">ゲームフォルダã®é¸æŠžã‚’スã‚ップã—ã¾ã™ã‹?</string> |
| 37 | <string name="add_games_warning_description">ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠžã—ãªã„å ´åˆã€ã‚²ãƒ¼ãƒ ã¯ã‚²ãƒ¼ãƒ リストã«è¡¨ç¤ºã•れã¾ã›ã‚“。</string> | 39 | <string name="add_games_warning_description">ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠžã—ãªã„ã¨ã€ã‚²ãƒ¼ãƒ ãŒãƒªã‚¹ãƒˆã«è¡¨ç¤ºã•れã¾ã›ã‚“。</string> |
| 38 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 39 | <string name="home_search_games">ゲームを検索</string> | 41 | <string name="home_search_games">ゲームを検索</string> |
| 40 | <string name="games_dir_selected">ゲームディレクトリãŒé¸æŠžã•れã¾ã—ãŸ</string> | 42 | <string name="search_settings">検索è¨å®š</string> |
| 41 | <string name="install_prod_keys">prod.keys をインストール</string> | 43 | <string name="games_dir_selected">ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠžã—ã¾ã—ãŸ</string> |
| 42 | <string name="install_prod_keys_description">ゲームã®å¾©å·åŒ–ã«å¿…è¦</string> | 44 | <string name="install_prod_keys">prod.keys</string> |
| 45 | <string name="install_prod_keys_description">製å“版ゲームã®å¾©å·åŒ–ã«å¿…è¦ã§ã™</string> | ||
| 43 | <string name="install_prod_keys_warning">ã‚ーã®è¿½åŠ ã‚’ã‚¹ã‚ップã—ã¾ã™ã‹ï¼Ÿ</string> | 46 | <string name="install_prod_keys_warning">ã‚ーã®è¿½åŠ ã‚’ã‚¹ã‚ップã—ã¾ã™ã‹ï¼Ÿ</string> |
| 44 | <string name="install_prod_keys_warning_description">製å“版ゲームã®ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã«ã¯ã€æœ‰åйãªã‚ーãŒå¿…è¦ã§ã™ã€‚続行ã™ã‚‹ã¨è‡ªä½œã‚¢ãƒ—リã—ã‹æ©Ÿèƒ½ã—ã¾ã›ã‚“。</string> | 47 | <string name="install_prod_keys_warning_description">製å“版ゲームã®ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã«ã¯ã€æœ‰åйãªã‚ーãŒå¿…è¦ã§ã™ã€‚続行ã™ã‚‹ã¨è‡ªä½œã‚¢ãƒ—リã—ã‹æ©Ÿèƒ½ã—ã¾ã›ã‚“。</string> |
| 45 | <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> | 48 | <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> |
| 46 | <string name="notifications">通知</string> | 49 | <string name="notifications">通知</string> |
| 47 | <string name="notifications_description">下ã®ãƒœã‚¿ãƒ³ã§é€šçŸ¥ã®æ¨©é™ã‚’許å¯ã—ã¦ãã ã•ã„。</string> | 50 | <string name="notifications_description">下ã®ãƒœã‚¿ãƒ³ã§é€šçŸ¥ã‚’許å¯ã—ã¦ãã ã•ã„。</string> |
| 48 | <string name="give_permission">許å¯</string> | 51 | <string name="give_permission">許å¯</string> |
| 49 | <string name="notification_warning">通知ã®è¨±å¯ã‚’スã‚ップã—ã¾ã™ã‹ï¼Ÿ</string> | 52 | <string name="notification_warning">通知ã®è¨±å¯ã‚’スã‚ップã—ã¾ã™ã‹ï¼Ÿ</string> |
| 50 | <string name="notification_warning_description">yuzuã¯é‡è¦ãªãŠçŸ¥ã‚‰ã›ã‚’通知ã§ãã¾ã›ã‚“。</string> | 53 | <string name="notification_warning_description">yuzuã¯é‡è¦ãªãŠçŸ¥ã‚‰ã›ã‚’通知ã§ãã¾ã›ã‚“。</string> |
| 51 | <string name="permission_denied">権é™ãŒæ‹’å¦ã•れã¾ã—ãŸ</string> | 54 | <string name="permission_denied">権é™ãŒæ‹’å¦ã•れã¾ã—ãŸ</string> |
| 52 | <string name="permission_denied_description">ã“ã®æ¨©é™ã‚’複数回拒å¦ã—ãŸãŸã‚ã€ã‚·ã‚¹ãƒ†ãƒ è¨å®šã§æ‰‹å‹•ã§è¨±å¯ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚</string> | 55 | <string name="permission_denied_description">ã“ã®æ¨©é™ã‚’複数回拒å¦ã—ãŸãŸã‚ã€è¨å®šã‹ã‚‰æ‰‹å‹•ã§è¨±å¯ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚</string> |
| 53 | <string name="about">æƒ…å ±</string> | 56 | <string name="about">æƒ…å ±</string> |
| 54 | <string name="about_description">ビルドãƒãƒ¼ã‚¸ãƒ§ãƒ³ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆãªã©</string> | 57 | <string name="about_description">ビルドãƒãƒ¼ã‚¸ãƒ§ãƒ³ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆãªã©</string> |
| 55 | <string name="warning_help">ヘルプ</string> | 58 | <string name="warning_help">ヘルプ</string> |
| 56 | <string name="warning_skip">スã‚ップ</string> | 59 | <string name="warning_skip">スã‚ップ</string> |
| 57 | <string name="warning_cancel">ã‚ャンセル</string> | 60 | <string name="warning_cancel">ã‚ャンセル</string> |
| 58 | <string name="install_amiibo_keys">Amiibo ã‚ーをインストール</string> | 61 | <string name="install_amiibo_keys">Amiibo</string> |
| 59 | <string name="install_amiibo_keys_description">ゲーム内ã§ã® Amiibo ã®ä½¿ç”¨ã«å¿…è¦</string> | 62 | <string name="install_amiibo_keys_description">ゲーム内ã§ã® Amiibo ã®ä½¿ç”¨ã«å¿…è¦ã§ã™</string> |
| 60 | <string name="invalid_keys_file">無効ãªã‚ーファイルãŒé¸æŠžã•れã¾ã—ãŸ</string> | 63 | <string name="invalid_keys_file">無効ãªã‚ーファイルã§ã™</string> |
| 61 | <string name="install_keys_success">æ£å¸¸ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¾ã—ãŸ</string> | 64 | <string name="install_keys_success">æ£å¸¸ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¾ã—ãŸ</string> |
| 62 | <string name="reading_keys_failure">æš—å·åŒ–ã‚ーã®èªã¿å–りエラー</string> | 65 | <string name="reading_keys_failure">æš—å·åŒ–ã‚ーã®èªã¿è¾¼ã¿å¤±æ•—</string> |
| 63 | <string name="invalid_keys_error">æš—å·åŒ–ã‚ーãŒç„¡åйã§ã™</string> | 66 | <string name="install_prod_keys_failure_extension_description">ã‚ãƒ¼ã®æ‹¡å¼µåãŒ.keysã§ã‚ã‚‹ã“ã¨ã‚’確èªã—ã€å†åº¦ãŠè©¦ã—ãã ã•ã„。</string> |
| 67 | <string name="install_amiibo_keys_failure_extension_description">ã‚ãƒ¼ã®æ‹¡å¼µåãŒ.binã§ã‚ã‚‹ã“ã¨ã‚’確èªã—ã€å†åº¦ãŠè©¦ã—ãã ã•ã„。</string> | ||
| 68 | <string name="invalid_keys_error">æš—å·åŒ–ã‚ーãŒç„¡åй</string> | ||
| 64 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 69 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 65 | <string name="install_keys_failure_description">é¸æŠžã•れãŸãƒ•ァイルãŒä¸æ£ã¾ãŸã¯ç ´æã—ã¦ã„ã¾ã™ã€‚ã‚ーをå†ãƒ€ãƒ³ãƒ—ã—ã¦ãã ã•ã„。</string> | 70 | <string name="install_keys_failure_description">ファイルãŒé–“é•ã£ã¦ã„ã‚‹ã‹ç ´æã—ã¦ã„ã¾ã™ã€‚ã‚ーをå†ãƒ€ãƒ³ãƒ—ã—ã¦ãã ã•ã„。</string> |
| 66 | <string name="install_gpu_driver">GPUドライãƒãƒ¼ã‚’インストール</string> | 71 | <string name="install_gpu_driver">GPUドライãƒãƒ¼</string> |
| 67 | <string name="install_gpu_driver_description">代替ドライãƒãƒ¼ã‚’インストールã—ã¦ãƒ‘フォーマンスや精度をå‘上ã•ã›ã¾ã™</string> | 72 | <string name="install_gpu_driver_description">代替ドライãƒãƒ¼ã‚’インストールã—ã¦ãƒ‘フォーマンスや精度をå‘上ã•ã›ã¾ã™</string> |
| 68 | <string name="advanced_settings">高度ãªè¨å®š</string> | 73 | <string name="advanced_settings">高度ãªè¨å®š</string> |
| 74 | <string name="advanced_settings_game">高度ãªè¨å®š: %1$s</string> | ||
| 69 | <string name="settings_description">エミュレーターã®è¨å®šã‚’æ§‹æˆã—ã¾ã™</string> | 75 | <string name="settings_description">エミュレーターã®è¨å®šã‚’æ§‹æˆã—ã¾ã™</string> |
| 70 | <string name="search_recently_played">最近プレイã—ãŸ</string> | 76 | <string name="search_recently_played">最近プレイã—ãŸ</string> |
| 71 | <string name="search_recently_added">æœ€è¿‘è¿½åŠ ã•れãŸ</string> | 77 | <string name="search_recently_added">æœ€è¿‘è¿½åŠ ã•れãŸ</string> |
| @@ -77,15 +83,34 @@ | |||
| 77 | <string name="no_file_manager">ファイルマãƒãƒ¼ã‚¸ãƒ£ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ</string> | 83 | <string name="no_file_manager">ファイルマãƒãƒ¼ã‚¸ãƒ£ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ</string> |
| 78 | <string name="notification_no_directory_link">yuzuã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’é–‹ã‘ã¾ã›ã‚“</string> | 84 | <string name="notification_no_directory_link">yuzuã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’é–‹ã‘ã¾ã›ã‚“</string> |
| 79 | <string name="notification_no_directory_link_description">ファイルマãƒãƒ¼ã‚¸ãƒ£ã®ã‚µã‚¤ãƒ‰ãƒ‘ãƒãƒ«ã§ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒ•ã‚©ãƒ«ãƒ€ã‚’æ‰‹å‹•ã§æŽ¢ã—ã¦ãã ã•ã„。</string> | 85 | <string name="notification_no_directory_link_description">ファイルマãƒãƒ¼ã‚¸ãƒ£ã®ã‚µã‚¤ãƒ‰ãƒ‘ãƒãƒ«ã§ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒ•ã‚©ãƒ«ãƒ€ã‚’æ‰‹å‹•ã§æŽ¢ã—ã¦ãã ã•ã„。</string> |
| 80 | <string name="manage_save_data">セーブデータを管ç†</string> | 86 | <string name="manage_save_data">セーブデータ</string> |
| 81 | <string name="manage_save_data_description">セーブデータãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸã€‚以下ã®ã‚ªãƒ—ションã‹ã‚‰é¸æŠžã—ã¦ãã ã•ã„。</string> | 87 | <string name="manage_save_data_description">セーブデータãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸã€‚æ“ä½œã‚’é¸æŠžã—ã¦ãã ã•ã„。</string> |
| 82 | <string name="import_export_saves_description">セーブファイルをインãƒãƒ¼ãƒˆ/エクスãƒãƒ¼ãƒˆ</string> | 88 | <string name="import_export_saves_description">セーブファイルをインãƒãƒ¼ãƒˆ/エクスãƒãƒ¼ãƒˆ</string> |
| 83 | <string name="save_file_imported_success">インãƒãƒ¼ãƒˆãŒå®Œäº†ã—ã¾ã—ãŸ</string> | 89 | <string name="save_file_imported_success">インãƒãƒ¼ãƒˆãŒå®Œäº†ã—ã¾ã—ãŸ</string> |
| 84 | <string name="save_file_invalid_zip_structure">セーブデータã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªæ§‹é€ ãŒç„¡åйã§ã™</string> | 90 | <string name="save_file_invalid_zip_structure">セーブデータã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªæ§‹é€ ãŒç„¡åй</string> |
| 85 | <string name="save_file_invalid_zip_structure_description">最åˆã®ã‚µãƒ–フォルダåã¯ã€ã‚²ãƒ¼ãƒ ã®ã‚¿ã‚¤ãƒˆãƒ«IDã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚</string> | 91 | <string name="save_file_invalid_zip_structure_description">最åˆã®ã‚µãƒ–フォルダåã¯ã€ã‚²ãƒ¼ãƒ ã®ã‚¿ã‚¤ãƒˆãƒ«IDã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚</string> |
| 86 | <string name="import_saves">インãƒãƒ¼ãƒˆ</string> | 92 | <string name="import_saves">インãƒãƒ¼ãƒˆ</string> |
| 87 | <string name="export_saves">エクスãƒãƒ¼ãƒˆ</string> | 93 | <string name="export_saves">エクスãƒãƒ¼ãƒˆ</string> |
| 88 | 94 | <string name="install_firmware">ファームウェア</string> | |
| 95 | <string name="install_firmware_description">ファームウェアã¯ZIPアーカイブã§ã‚ã‚‹å¿…è¦ãŒã‚りã€ä¸€éƒ¨ã®ã‚²ãƒ¼ãƒ ã‚’èµ·å‹•ã™ã‚‹ã®ã«å¿…è¦ã§ã™</string> | ||
| 96 | <string name="firmware_installing">ファームウェアをインストールä¸</string> | ||
| 97 | <string name="firmware_installed_success">インストールãŒå®Œäº†ã—ã¾ã—ãŸ</string> | ||
| 98 | <string name="firmware_installed_failure">インストール失敗</string> | ||
| 99 | <string name="share_log">デãƒãƒƒã‚°ãƒã‚°</string> | ||
| 100 | <string name="share_log_description">yuzuã®ãƒã‚°ãƒ•ァイルを共有ã—ã¦å•題をデãƒãƒƒã‚°ã—ã¾ã™</string> | ||
| 101 | <string name="share_log_missing">ãƒã‚°ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string> | ||
| 102 | <string name="install_game_content">è¿½åŠ ã‚³ãƒ³ãƒ†ãƒ³ãƒ„</string> | ||
| 103 | <string name="install_game_content_description">更新データやDLCをインストールã—ã¾ã™</string> | ||
| 104 | <string name="installing_game_content">コンテンツをインストールä¸...</string> | ||
| 105 | <string name="install_game_content_failure_file_extension">NSPã¨XCIå½¢å¼ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã®ã¿ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ã‚²ãƒ¼ãƒ ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒæœ‰åйãªã‚‚ã®ã§ã‚ã‚‹ã‹ã”確èªãã ã•ã„。</string> | ||
| 106 | <string name="install_game_content_failed_count">%1$d ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã‚¨ãƒ©ãƒ¼</string> | ||
| 107 | <string name="install_game_content_success">ゲームコンテンツã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã«æˆåŠŸã—ã¾ã—ãŸ</string> | ||
| 108 | <string name="install_game_content_success_install">%1$d ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã«æˆåŠŸã—ã¾ã—ãŸ</string> | ||
| 109 | <string name="install_game_content_success_overwrite">%1$d ã®ä¸Šæ›¸ãã«æˆåŠŸã—ã¾ã—ãŸ</string> | ||
| 110 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 111 | <string name="custom_driver_not_supported">カスタムドライãƒã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“</string> | ||
| 112 | <string name="manage_yuzu_data">yuzu データを管ç†</string> | ||
| 113 | <string name="share_save_file">セーブファイルを共有</string> | ||
| 89 | <!-- About screen strings --> | 114 | <!-- About screen strings --> |
| 90 | <string name="gaia_is_not_real">ガイアã¯å®Ÿåœ¨ã—ãªã„</string> | 115 | <string name="gaia_is_not_real">ガイアã¯å®Ÿåœ¨ã—ãªã„</string> |
| 91 | <string name="copied_to_clipboard">クリップボードã«ã‚³ãƒ”ーã—ã¾ã—ãŸ</string> | 116 | <string name="copied_to_clipboard">クリップボードã«ã‚³ãƒ”ーã—ã¾ã—ãŸ</string> |
| @@ -93,7 +118,15 @@ | |||
| 93 | <string name="contributors">貢献者</string> | 118 | <string name="contributors">貢献者</string> |
| 94 | <string name="contributors_description">yuzuãƒãƒ¼ãƒ ã®\u2764ã§ä½œã‚‰ã‚ŒãŸ</string> | 119 | <string name="contributors_description">yuzuãƒãƒ¼ãƒ ã®\u2764ã§ä½œã‚‰ã‚ŒãŸ</string> |
| 95 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 120 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 121 | <string name="licenses_description">yuzu for Androidã®ä½œæˆã‚’å¯èƒ½ã«ã—ãŸãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆ</string> | ||
| 96 | <string name="build">ビルド</string> | 122 | <string name="build">ビルド</string> |
| 123 | <string name="user_data">ユーザデータ</string> | ||
| 124 | <string name="exporting_user_data">ユーザデータをエクスãƒãƒ¼ãƒˆä¸...</string> | ||
| 125 | <string name="importing_user_data">ユーザデータをインãƒãƒ¼ãƒˆä¸...</string> | ||
| 126 | <string name="import_user_data">ユーザデータをインãƒãƒ¼ãƒˆ</string> | ||
| 127 | <string name="user_data_export_success">ユーザデータã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã«æˆåŠŸã—ã¾ã—ãŸ</string> | ||
| 128 | <string name="user_data_import_success">ユーザデータã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆã«æˆåŠŸã—ã¾ã—ãŸ</string> | ||
| 129 | <string name="user_data_export_cancelled">エクスãƒãƒ¼ãƒˆã‚’ã‚ャンセルã—ã¾ã—ãŸ</string> | ||
| 97 | <string name="support_link">https://discord.gg/u77vRWY</string> | 130 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 98 | <string name="website_link">https://yuzu-emu.org/</string> | 131 | <string name="website_link">https://yuzu-emu.org/</string> |
| 99 | <string name="github_link">https://github.com/yuzu-emu</string> | 132 | <string name="github_link">https://github.com/yuzu-emu</string> |
| @@ -105,72 +138,91 @@ | |||
| 105 | <string name="get_early_access_description">æœ€å…ˆç«¯ã®æ©Ÿèƒ½ã€ã‚¢ãƒƒãƒ—ãƒ‡ãƒ¼ãƒˆã®æ—©æœŸã‚¢ã‚¯ã‚»ã‚¹ãªã©</string> | 138 | <string name="get_early_access_description">æœ€å…ˆç«¯ã®æ©Ÿèƒ½ã€ã‚¢ãƒƒãƒ—ãƒ‡ãƒ¼ãƒˆã®æ—©æœŸã‚¢ã‚¯ã‚»ã‚¹ãªã©</string> |
| 106 | <string name="early_access_benefits">早期アクセスã®ãƒ¡ãƒªãƒƒãƒˆ</string> | 139 | <string name="early_access_benefits">早期アクセスã®ãƒ¡ãƒªãƒƒãƒˆ</string> |
| 107 | <string name="cutting_edge_features">æœ€å…ˆç«¯ã®æ©Ÿèƒ½</string> | 140 | <string name="cutting_edge_features">æœ€å…ˆç«¯ã®æ©Ÿèƒ½</string> |
| 108 | <string name="early_access_updates">ã‚¢ãƒƒãƒ—ãƒ‡ãƒ¼ãƒˆã®æ—©æœŸã‚¢ã‚¯ã‚»ã‚¹</string> | 141 | <string name="early_access_updates">アップデートã¸ã®æ—©æœŸã‚¢ã‚¯ã‚»ã‚¹</string> |
| 109 | <string name="no_manual_installation">手動インストールãŒä¸è¦</string> | 142 | <string name="no_manual_installation">手動インストールãŒä¸è¦</string> |
| 110 | <string name="prioritized_support">優先的ãªã‚µãƒãƒ¼ãƒˆ</string> | 143 | <string name="prioritized_support">優先サãƒãƒ¼ãƒˆ</string> |
| 111 | <string name="helping_game_preservation">ゲームã®ä¿å˜ã«è²¢çŒ®</string> | 144 | <string name="helping_game_preservation">ゲームã®ä¿å˜ã«è²¢çŒ®</string> |
| 112 | <string name="our_eternal_gratitude">ç§ãŸã¡ã®æ°¸é ã®æ„Ÿè¬</string> | 145 | <string name="our_eternal_gratitude">ç§ãŸã¡ã‹ã‚‰æ°¸é ã®æ„Ÿè¬</string> |
| 113 | <string name="are_you_interested">興味ãŒã‚りã¾ã™ã‹ï¼Ÿ</string> | 146 | <string name="are_you_interested">興味ãŒã‚りã¾ã™ã‹ï¼Ÿ</string> |
| 114 | 147 | ||
| 115 | <!-- General settings strings --> | 148 | <!-- General settings strings --> |
| 116 | <string name="frame_limit_enable">速度制é™ã‚’有効化</string> | 149 | <string name="frame_limit_enable">エミュレーション速度を制é™</string> |
| 117 | <string name="frame_limit_enable_description">有効ã«ã™ã‚‹ã¨ã€ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³é€Ÿåº¦ãŒä»»æ„ã®å‰²åˆã«åˆ¶é™ã•れã¾ã™ã€‚</string> | 150 | <string name="frame_limit_enable_description">エミュレーション速度を指定ã—ãŸå‰²åˆã«åˆ¶é™ã—ã¾ã™ã€‚</string> |
| 118 | <string name="frame_limit_slider">エミュレーション速度ã®åˆ¶é™</string> | 151 | <string name="frame_limit_slider">エミュレーション速度</string> |
| 119 | <string name="frame_limit_slider_description">エミュレーション速度を制é™ã™ã‚‹å‰²åˆã‚’指定ã—ã¾ã™ã€‚デフォルトã®100%ã§ã¯ã€ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã¯é€šå¸¸ã®é€Ÿåº¦ã«åˆ¶é™ã•れã¾ã™ã€‚値ãŒé«˜ã„ã¾ãŸã¯ä½Žã„ã»ã©ã€é€Ÿåº¦åˆ¶é™ãŒå¢—åŠ ã¾ãŸã¯æ¸›å°‘ã—ã¾ã™ã€‚</string> | 152 | <string name="frame_limit_slider_description">エミュレーション速度を制é™ã™ã‚‹ãƒ‘ーセンテージを指定ã—ã¾ã™ã€‚100%ã¯é€šå¸¸é€Ÿåº¦ã§ã™ã€‚値ã®å¢—減ã§é€Ÿåº¦ã‚‚増減ã—ã¾ã™ã€‚</string> |
| 120 | <string name="cpu_accuracy">CPU精度</string> | 153 | <string name="cpu_accuracy">CPU精度</string> |
| 121 | |||
| 122 | <!-- System settings strings --> | 154 | <!-- System settings strings --> |
| 123 | <string name="use_docked_mode">TVモード</string> | 155 | <string name="use_docked_mode">TVモード</string> |
| 124 | <string name="use_docked_mode_description">TVモードã§ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ãƒˆã—ã¾ã™ã€‚パフォーマンスãŒçŠ ç‰²ã«ãªã‚Šã¾ã™ãŒã€è§£åƒåº¦ãŒå‘上ã—ã¾ã™ã€‚</string> | 156 | <string name="use_docked_mode_description">高解åƒåº¦ã€ä½Žãƒ‘フォーマンス。無効時ã«ã¯æºå¸¯ãƒ¢ãƒ¼ãƒ‰ãŒä½¿ç”¨ã•れã¾ã™ï¼ˆä½Žè§£åƒåº¦ã€é«˜ãƒ‘フォーマンス)。</string> |
| 125 | <string name="emulated_region">地域</string> | 157 | <string name="emulated_region">地域</string> |
| 126 | <string name="emulated_language">言語</string> | 158 | <string name="emulated_language">言語</string> |
| 127 | <string name="select_rtc_date">RTCã®æ—¥ä»˜ã‚’é¸æŠž</string> | 159 | <string name="select_rtc_date">RTCã®æ—¥ä»˜ã‚’é¸æŠž</string> |
| 128 | <string name="select_rtc_time">RTCã®æ™‚åˆ»ã‚’é¸æŠž</string> | 160 | <string name="select_rtc_time">RTCã®æ™‚åˆ»ã‚’é¸æŠž</string> |
| 129 | <string name="use_custom_rtc">カスタムRTC</string> | 161 | <string name="use_custom_rtc">カスタムRTC</string> |
| 130 | <string name="use_custom_rtc_description">ç¾åœ¨ã®ã‚·ã‚¹ãƒ†ãƒ 時間ã¨ã¯åˆ¥ã«ã‚«ã‚¹ã‚¿ãƒ ã®ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ クãƒãƒƒã‚¯ã‚’è¨å®šã§ãã¾ã™ã€‚</string> | 162 | <string name="use_custom_rtc_description">ç¾åœ¨ã®ã‚·ã‚¹ãƒ†ãƒ 時間ã¨ã¯åˆ¥ã«ã€ä»»æ„ã®ãƒªã‚¢ãƒ«ã‚¿ã‚¤ãƒ クãƒãƒƒã‚¯ã‚’è¨å®šã§ãã¾ã™ã€‚</string> |
| 131 | <string name="set_custom_rtc">カスタムRTCã‚’è¨å®š</string> | 163 | <string name="set_custom_rtc">カスタムRTCã‚’è¨å®š</string> |
| 132 | 164 | ||
| 133 | <!-- Graphics settings strings --> | 165 | <!-- Graphics settings strings --> |
| 134 | <string name="renderer_api">API</string> | ||
| 135 | <string name="renderer_accuracy">精度</string> | 166 | <string name="renderer_accuracy">精度</string> |
| 136 | <string name="renderer_resolution">è§£åƒåº¦</string> | 167 | <string name="renderer_resolution">è§£åƒåº¦ï¼ˆæºå¸¯ãƒ¢ãƒ¼ãƒ‰/TVモード)</string> |
| 137 | <string name="renderer_vsync">åž‚ç›´åŒæœŸãƒ¢ãƒ¼ãƒ‰</string> | 168 | <string name="renderer_vsync">åž‚ç›´åŒæœŸãƒ¢ãƒ¼ãƒ‰</string> |
| 169 | <string name="renderer_screen_layout">ç”»é¢ã®å‘ã</string> | ||
| 138 | <string name="renderer_aspect_ratio">アスペクト比</string> | 170 | <string name="renderer_aspect_ratio">アスペクト比</string> |
| 139 | <string name="renderer_scaling_filter">ウィンドウé©å¿œãƒ•ィルター</string> | 171 | <string name="renderer_scaling_filter">ウィンドウé©å¿œãƒ•ィルター</string> |
| 140 | <string name="renderer_anti_aliasing">アンãƒã‚¨ã‚¤ãƒªã‚¢ã‚¹æ–¹å¼</string> | 172 | <string name="renderer_anti_aliasing">アンãƒã‚¨ã‚¤ãƒªã‚¢ã‚¹æ–¹å¼</string> |
| 141 | <string name="renderer_force_max_clock">最大クãƒãƒƒã‚¯ã‚’強制 (Adrenoã®ã¿)</string> | 173 | <string name="renderer_force_max_clock">最大クãƒãƒƒã‚¯ã‚’強制 (Adrenoã®ã¿)</string> |
| 142 | <string name="renderer_force_max_clock_description">GPUã‚’å¯èƒ½ãªé™ã‚Šæœ€å¤§ã‚¯ãƒãƒƒã‚¯ã§å‹•作ã•ã›ã¾ã™ (éŽç†±åˆ¶é™ã¯å¼•ãç¶šãé©ç”¨ã•れã¾ã™)。</string> | 174 | <string name="renderer_force_max_clock_description">GPUを最大é™å¯èƒ½ãªå‘¨æ³¢æ•°ã§å‹•作ã•ã›ã¾ã™ (éŽç†±åˆ¶é™ã¯å¼•ãç¶šãé©ç”¨ã•れã¾ã™)。</string> |
| 143 | <string name="renderer_asynchronous_shaders">éžåŒæœŸã‚·ã‚§ãƒ¼ãƒ€ãƒ¼</string> | 175 | <string name="renderer_asynchronous_shaders">éžåŒæœŸã‚·ã‚§ãƒ¼ãƒ€ãƒ¼</string> |
| 144 | <string name="renderer_asynchronous_shaders_description">シェーダーをéžåŒæœŸã§ã‚³ãƒ³ãƒ‘イルã—ã¾ã™ã€‚コマè½ã¡ãŒè»½æ¸›ã•れã¾ã™ãŒã€ä¸å…·åˆãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> | 176 | <string name="renderer_asynchronous_shaders_description">シェーダーをéžåŒæœŸã§ã‚³ãƒ³ãƒ‘イルã—ã¾ã™ã€‚コマè½ã¡ãŒè»½æ¸›ã•れã¾ã™ãŒã€ä¸å…·åˆãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> |
| 177 | <string name="renderer_reactive_flushing">峿™‚書ãè¾¼ã¿</string> | ||
| 178 | <string name="renderer_reactive_flushing_description">一部ã®ã‚²ãƒ¼ãƒ ã«ãŠã„ã¦ã€ãƒ‘ãƒ•ã‚©ãƒ¼ãƒžãƒ³ã‚¹ã‚’çŠ ç‰²ã«ã—ãªãŒã‚‰ã‚‚ã€ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ç²¾åº¦ã‚’å‘上ã•ã›ã¾ã™ã€‚</string> | ||
| 179 | <string name="use_disk_shader_cache">ディスクシェーダーã‚ャッシュ</string> | ||
| 180 | <string name="use_disk_shader_cache_description">生æˆã—ãŸã‚·ã‚§ãƒ¼ãƒ€ãƒ¼ã‚’端末ã«ä¿å˜ã—ã¦èªã¿è¾¼ã¿ã€ã‚³ãƒžè½ã¡ã‚’軽減ã—ã¾ã™ã€‚</string> | ||
| 181 | |||
| 182 | <!-- Debug settings strings --> | ||
| 183 | <string name="cpu">CPU</string> | ||
| 184 | <string name="cpu_debug_mode">CPU デãƒãƒƒã‚®ãƒ³ã‚°</string> | ||
| 185 | <string name="gpu">GPU</string> | ||
| 186 | <string name="renderer_api">API</string> | ||
| 145 | <string name="renderer_debug">グラフィックデãƒãƒƒã‚°</string> | 187 | <string name="renderer_debug">グラフィックデãƒãƒƒã‚°</string> |
| 146 | <string name="renderer_debug_description">オンã«ã™ã‚‹ã¨ã€ã‚°ãƒ©ãƒ•ィックAPI ã¯ä½Žé€Ÿã®ãƒ‡ãƒãƒƒã‚°ãƒ¢ãƒ¼ãƒ‰ã«å…¥ã‚Šã¾ã™ã€‚</string> | 188 | <string name="renderer_debug_description">グラフィックAPIを低速デãƒãƒƒã‚°ãƒ¢ãƒ¼ãƒ‰ã«è¨å®šã—ã¾ã™ã€‚</string> |
| 147 | <string name="use_disk_shader_cache">シェーダーã‚ャッシュを使用</string> | 189 | <string name="fastmem">Fastmem</string> |
| 148 | <string name="use_disk_shader_cache_description">生æˆã—ãŸã‚·ã‚§ãƒ¼ãƒ€ãƒ¼ã‚’ディスクã«ä¿å˜ã—ã¦èªã¿è¾¼ã‚€ã“ã¨ã§ã€ã‚³ãƒžè½ã¡ã‚’軽減ã—ã¾ã™ã€‚</string> | ||
| 149 | 190 | ||
| 150 | <!-- Audio settings strings --> | 191 | <!-- Audio settings strings --> |
| 192 | <string name="audio_output_engine">出力エンジン</string> | ||
| 151 | <string name="audio_volume">音é‡</string> | 193 | <string name="audio_volume">音é‡</string> |
| 152 | <string name="audio_volume_description">オーディオ出力ã®éŸ³é‡ã‚’指定ã—ã¾ã™</string> | 194 | <string name="audio_volume_description">オーディオ出力ã®éŸ³é‡ã‚’指定ã—ã¾ã™</string> |
| 153 | 195 | ||
| 154 | <!-- Miscellaneous --> | 196 | <!-- Miscellaneous --> |
| 155 | <string name="slider_default">デフォルト</string> | 197 | <string name="slider_default">デフォルト</string> |
| 156 | <string name="ini_saved">è¨å®šã‚’ä¿å˜ã—ã¾ã—ãŸ</string> | 198 | <string name="ini_saved">è¨å®šã‚’ä¿å˜ã—ã¾ã—ãŸ</string> |
| 157 | <string name="gameid_saved">%1$sã®è¨å®šã‚’ä¿å˜ã—ã¾ã—ãŸ</string> | 199 | <string name="gameid_saved">%1$s ã®è¨å®šã‚’ä¿å˜ã—ã¾ã—ãŸ</string> |
| 158 | <string name="error_saving">%1$s.ini ã®ä¿å˜ã‚¨ãƒ©ãƒ¼: %2$s</string> | 200 | <string name="error_saving">%1$s.ini ã®ä¿å˜ã‚¨ãƒ©ãƒ¼: %2$s</string> |
| 201 | <string name="unimplemented_menu">未実装ã®ãƒ¡ãƒ‹ãƒ¥ãƒ¼</string> | ||
| 159 | <string name="loading">èªã¿è¾¼ã¿ä¸â€¦</string> | 202 | <string name="loading">èªã¿è¾¼ã¿ä¸â€¦</string> |
| 203 | <string name="shutting_down">終了ä¸...</string> | ||
| 160 | <string name="reset_setting_confirmation">ã“ã®è¨å®šã‚’åˆæœŸå€¤ã«ãƒªã‚»ãƒƒãƒˆã—ã¾ã™ã‹?</string> | 204 | <string name="reset_setting_confirmation">ã“ã®è¨å®šã‚’åˆæœŸå€¤ã«ãƒªã‚»ãƒƒãƒˆã—ã¾ã™ã‹?</string> |
| 161 | <string name="reset_to_default">åˆæœŸè¨å®šã«æˆ»ã™</string> | 205 | <string name="reset_to_default">åˆæœŸè¨å®šã«æˆ»ã™</string> |
| 162 | <string name="reset_all_settings">ã™ã¹ã¦ã®è¨å®šã‚’リセットã—ã¾ã™ã‹ï¼Ÿ</string> | 206 | <string name="reset_all_settings">ã™ã¹ã¦ã®è¨å®šã‚’リセットã—ã¾ã™ã‹ï¼Ÿ</string> |
| 163 | <string name="reset_all_settings_description">ã™ã¹ã¦ã®è©³ç´°è¨å®šãŒåˆæœŸè¨å®šã«æˆ»ã•れã¾ã™ã€‚ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。</string> | 207 | <string name="reset_all_settings_description">ã™ã¹ã¦ã®è©³ç´°è¨å®šãŒåˆæœŸå€¤ã«æˆ»ã•れã¾ã™ã€‚ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。</string> |
| 164 | <string name="settings_reset">è¨å®šã‚’リセットã—ã¾ã—ãŸ</string> | 208 | <string name="settings_reset">è¨å®šã‚’リセットã—ã¾ã—ãŸ</string> |
| 165 | <string name="close">é–‰ã˜ã‚‹</string> | 209 | <string name="close">é–‰ã˜ã‚‹</string> |
| 166 | <string name="learn_more">è©³ç´°æƒ…å ±</string> | 210 | <string name="learn_more">è©³ç´°æƒ…å ±</string> |
| 211 | <string name="auto">自動</string> | ||
| 212 | <string name="submit">é€ä¿¡</string> | ||
| 213 | <string name="string_import">インãƒãƒ¼ãƒˆ</string> | ||
| 214 | <string name="export">エクスãƒãƒ¼ãƒˆ</string> | ||
| 215 | <string name="export_failed">エクスãƒãƒ¼ãƒˆå¤±æ•—</string> | ||
| 216 | <string name="import_failed">インãƒãƒ¼ãƒˆå¤±æ•—</string> | ||
| 217 | <string name="cancelling">ã‚ャンセルä¸</string> | ||
| 167 | 218 | ||
| 168 | <!-- GPU driver installation --> | 219 | <!-- GPU driver installation --> |
| 169 | <string name="select_gpu_driver">GPUドライãƒã‚’é¸æŠž</string> | 220 | <string name="select_gpu_driver">GPUドライãƒã‚’é¸æŠž</string> |
| 170 | <string name="select_gpu_driver_title">ç¾åœ¨ã®GPUドライãƒãƒ¼ã‚’ç½®ãæ›ãˆã¾ã™ã‹ï¼Ÿ</string> | 221 | <string name="select_gpu_driver_title">ç¾åœ¨ã®GPUドライãƒã‚’ç½®ãæ›ãˆã¾ã™ã‹ï¼Ÿ</string> |
| 171 | <string name="select_gpu_driver_install">インストール</string> | 222 | <string name="select_gpu_driver_install">インストール</string> |
| 172 | <string name="select_gpu_driver_default">デフォルト</string> | 223 | <string name="select_gpu_driver_default">デフォルト</string> |
| 173 | <string name="select_gpu_driver_use_default">デフォルトã®GPUドライãƒãƒ¼ã‚’使用ã—ã¾ã™</string> | 224 | <string name="select_gpu_driver_use_default">デフォルトã®ãƒ‰ãƒ©ã‚¤ãƒã‚’使用ã—ã¾ã™</string> |
| 225 | <string name="select_gpu_driver_error">é¸æŠžã•れãŸãƒ‰ãƒ©ã‚¤ãƒãŒç„¡åйã€ã‚·ã‚¹ãƒ†ãƒ ã®ãƒ‡ãƒ•ォルトを使用ã—ã¾ã™!</string> | ||
| 174 | <string name="system_gpu_driver">システムã®GPUドライãƒ</string> | 226 | <string name="system_gpu_driver">システムã®GPUドライãƒ</string> |
| 175 | <string name="installing_driver">インストールä¸â€¦</string> | 227 | <string name="installing_driver">インストールä¸â€¦</string> |
| 176 | 228 | ||
| @@ -181,33 +233,34 @@ | |||
| 181 | <string name="preferences_graphics">グラフィック</string> | 233 | <string name="preferences_graphics">グラフィック</string> |
| 182 | <string name="preferences_audio">サウンド</string> | 234 | <string name="preferences_audio">サウンド</string> |
| 183 | <string name="preferences_theme">テーマã¨è‰²</string> | 235 | <string name="preferences_theme">テーマã¨è‰²</string> |
| 236 | <string name="preferences_debug">デãƒãƒƒã‚°</string> | ||
| 184 | 237 | ||
| 185 | <!-- ROM loading errors --> | 238 | <!-- ROM loading errors --> |
| 186 | <string name="loader_error_encrypted">ROMãŒæš—å·åŒ–ã•れã¦ã„ã¾ã™</string> | 239 | <string name="loader_error_encrypted">ROMãŒæš—å·åŒ–ã•れã¦ã„ã¾ã™</string> |
| 187 | <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> | 240 | <string name="loader_error_encrypted_keys_description"><![CDATA[ゲームã®å¾©å·åŒ–ã«å¿…è¦ãª <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> ファイルãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。]]></string> |
| 188 | <string name="loader_error_encrypted_keys_description"><![CDATA[ゲームを復å·åŒ–ã™ã‚‹ãŸã‚ã« <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> ファイルãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。]]></string> | ||
| 189 | <string name="loader_error_video_core">ビデオコアã®åˆæœŸåŒ–ä¸ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ</string> | 241 | <string name="loader_error_video_core">ビデオコアã®åˆæœŸåŒ–ä¸ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ</string> |
| 190 | <string name="loader_error_video_core_description">ã“れã¯é€šå¸¸ã€äº’æ›æ€§ã®ãªã„GPUドライãƒãƒ¼ãŒåŽŸå› ã§ç™ºç”Ÿã—ã¾ã™ã€‚ カスタムGPUドライãƒãƒ¼ã‚’インストールã™ã‚‹ã¨ã€å•題ãŒè§£æ±ºã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> | 242 | <string name="loader_error_video_core_description">ã“れã¯é€šå¸¸ã€äº’æ›æ€§ã®ãªã„GPUドライãƒãƒ¼ãŒåŽŸå› ã§ç™ºç”Ÿã—ã¾ã™ã€‚ カスタムGPUドライãƒãƒ¼ã‚’インストールã™ã‚‹ã¨ã€å•題ãŒè§£æ±ºã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> |
| 191 | <string name="loader_error_invalid_format">ROMã®èªã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸ</string> | 243 | <string name="loader_error_invalid_format">ROMã®èªã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸ</string> |
| 192 | <string name="loader_error_file_not_found">ROMファイルãŒå˜åœ¨ã—ã¾ã›ã‚“</string> | 244 | <string name="loader_error_file_not_found">ROMファイルãŒå˜åœ¨ã—ã¾ã›ã‚“</string> |
| 193 | 245 | ||
| 194 | <!-- Emulation Menu --> | 246 | <!-- Emulation Menu --> |
| 195 | <string name="emulation_exit">エミュレーションを終了</string> | 247 | <string name="emulation_exit">終了</string> |
| 196 | <string name="emulation_done">完了</string> | 248 | <string name="emulation_done">完了</string> |
| 197 | <string name="emulation_fps_counter">FPSカウンター</string> | 249 | <string name="emulation_fps_counter">FPSカウンター</string> |
| 198 | <string name="emulation_toggle_controls">コントãƒãƒ¼ãƒ«ã‚’切り替ãˆ</string> | 250 | <string name="emulation_toggle_controls">ボタンã®è¡¨ç¤ºè¨å®š</string> |
| 199 | <string name="emulation_dpad_slide">åå—ã‚ーã®ã‚¹ãƒ©ã‚¤ãƒ‰æ“作</string> | 251 | <string name="emulation_rel_stick_center">スティックを固定ã—ãªã„</string> |
| 200 | <string name="emulation_haptics">振動</string> | 252 | <string name="emulation_dpad_slide">åå—ã‚ーをスライドæ“作</string> |
| 201 | <string name="emulation_show_overlay">オーãƒãƒ¼ãƒ¬ã‚¤ã‚’表示</string> | 253 | <string name="emulation_haptics">ã‚¿ãƒƒãƒæŒ¯å‹•</string> |
| 202 | <string name="emulation_toggle_all">ã™ã¹ã¦é¸æŠž</string> | 254 | <string name="emulation_show_overlay">ボタンを表示</string> |
| 203 | <string name="emulation_control_adjust">オーãƒãƒ¼ãƒ¬ã‚¤ã‚’調整</string> | 255 | <string name="emulation_toggle_all">ã™ã¹ã¦åˆ‡æ›¿</string> |
| 256 | <string name="emulation_control_adjust">見ãŸç›®ã‚’調整</string> | ||
| 204 | <string name="emulation_control_scale">大ãã•</string> | 257 | <string name="emulation_control_scale">大ãã•</string> |
| 205 | <string name="emulation_control_opacity">ä¸é€æ˜Žåº¦</string> | 258 | <string name="emulation_control_opacity">ä¸é€æ˜Žåº¦</string> |
| 206 | <string name="emulation_touch_overlay_reset">リセット</string> | 259 | <string name="emulation_touch_overlay_reset">リセット</string> |
| 207 | <string name="emulation_touch_overlay_edit">オーãƒãƒ¼ãƒ¬ã‚¤ã‚’編集</string> | 260 | <string name="emulation_touch_overlay_edit">ä½ç½®ã‚’編集</string> |
| 208 | <string name="emulation_pause">ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’ä¸€æ™‚åœæ¢</string> | 261 | <string name="emulation_pause">ä¸€æ™‚åœæ¢</string> |
| 209 | <string name="emulation_unpause">エミュレーションをå†é–‹</string> | 262 | <string name="emulation_unpause">å†é–‹</string> |
| 210 | <string name="emulation_input_overlay">オーãƒãƒ¼ãƒ¬ã‚¤ã‚ªãƒ—ション</string> | 263 | <string name="emulation_input_overlay">表示オプション</string> |
| 211 | 264 | ||
| 212 | <string name="load_settings">è¨å®šã‚’ãƒãƒ¼ãƒ‰ä¸â€¦</string> | 265 | <string name="load_settings">è¨å®šã‚’ãƒãƒ¼ãƒ‰ä¸â€¦</string> |
| 213 | 266 | ||
| @@ -220,10 +273,13 @@ | |||
| 220 | <string name="system_archive_not_found">システムアーカイブãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string> | 273 | <string name="system_archive_not_found">システムアーカイブãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</string> |
| 221 | <string name="system_archive_not_found_message">%s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。システムアーカイブをダンプã—ã¦ãã ã•ã„。\nエミュレーションを続行ã™ã‚‹ã¨ã€ã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã‚„ãƒã‚°ãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> | 274 | <string name="system_archive_not_found_message">%s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。システムアーカイブをダンプã—ã¦ãã ã•ã„。\nエミュレーションを続行ã™ã‚‹ã¨ã€ã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã‚„ãƒã‚°ãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> |
| 222 | <string name="system_archive_general">システムアーカイブ</string> | 275 | <string name="system_archive_general">システムアーカイブ</string> |
| 223 | <string name="save_load_error">セーブ/ãƒãƒ¼ãƒ‰ エラー</string> | 276 | <string name="save_load_error">セーブ/ãƒãƒ¼ãƒ‰ã‚¨ãƒ©ãƒ¼</string> |
| 224 | <string name="fatal_error">致命的ãªã‚¨ãƒ©ãƒ¼</string> | 277 | <string name="fatal_error">致命的ãªã‚¨ãƒ©ãƒ¼</string> |
| 225 | <string name="fatal_error_message">致命的ãªã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚詳細ã¯ãƒã‚°ã‚’確èªã—ã¦ãã ã•ã„。\nエミュレーションを続行ã™ã‚‹ã¨ã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã‚„ãƒã‚°ãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> | 278 | <string name="fatal_error_message">致命的ãªã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚詳細ã¯ãƒã‚°ã‚’確èªã—ã¦ãã ã•ã„。\nエミュレーションを続行ã™ã‚‹ã¨ã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã‚„ãƒã‚°ãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚</string> |
| 226 | <string name="performance_warning">ã“ã®è¨å®šã‚’オフã«ã™ã‚‹ã¨ã€ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã®ãƒ‘フォーマンスãŒè‘—ã—ã低下ã—ã¾ã™ï¼æœ€é«˜ã®ä½“験を得るãŸã‚ã«ã¯ã€ã“ã®è¨å®šã‚’有効ã«ã—ã¦ãŠãã“ã¨ã‚’ãŠå‹§ã‚ã—ã¾ã™ã€‚</string> | 279 | <string name="performance_warning">ã“ã®è¨å®šã‚’オフã«ã™ã‚‹ã¨ã€ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã®ãƒ‘フォーマンスãŒè‘—ã—ã低下ã—ã¾ã™ï¼æœ€é«˜ã®ä½“験を得るãŸã‚ã«ã¯ã€ã“ã®è¨å®šã‚’有効ã«ã—ã¦ãŠãã“ã¨ã‚’推奨ã—ã¾ã™ã€‚</string> |
| 280 | <string name="device_memory_inadequate">デãƒã‚¤ã‚¹ RAM: %1$s\n推奨: %2$s</string> | ||
| 281 | <string name="memory_formatted">%1$s %2$s</string> | ||
| 282 | <string name="no_game_present">èµ·å‹•ã§ãるゲームãŒã‚りã¾ã›ã‚“!</string> | ||
| 227 | 283 | ||
| 228 | <!-- Region Names --> | 284 | <!-- Region Names --> |
| 229 | <string name="region_japan">日本</string> | 285 | <string name="region_japan">日本</string> |
| @@ -234,7 +290,14 @@ | |||
| 234 | <string name="region_korea">韓国</string> | 290 | <string name="region_korea">韓国</string> |
| 235 | <string name="region_taiwan">å°æ¹¾</string> | 291 | <string name="region_taiwan">å°æ¹¾</string> |
| 236 | 292 | ||
| 237 | <!-- Language Names --> | 293 | <!-- Memory Sizes --> |
| 294 | <string name="memory_byte">Byte</string> | ||
| 295 | <string name="memory_kilobyte">KB</string> | ||
| 296 | <string name="memory_megabyte">MB</string> | ||
| 297 | <string name="memory_gigabyte">GB</string> | ||
| 298 | <string name="memory_terabyte">TB</string> | ||
| 299 | <string name="memory_petabyte">PB</string> | ||
| 300 | <string name="memory_exabyte">EB</string> | ||
| 238 | 301 | ||
| 239 | <!-- Renderer APIs --> | 302 | <!-- Renderer APIs --> |
| 240 | <string name="renderer_vulkan">Vulkan</string> | 303 | <string name="renderer_vulkan">Vulkan</string> |
| @@ -242,7 +305,7 @@ | |||
| 242 | 305 | ||
| 243 | <!-- Renderer Accuracy --> | 306 | <!-- Renderer Accuracy --> |
| 244 | <string name="renderer_accuracy_normal">標準</string> | 307 | <string name="renderer_accuracy_normal">標準</string> |
| 245 | <string name="renderer_accuracy_high">高ã„</string> | 308 | <string name="renderer_accuracy_high">高</string> |
| 246 | <string name="renderer_accuracy_extreme">最高 (低速)</string> | 309 | <string name="renderer_accuracy_extreme">最高 (低速)</string> |
| 247 | 310 | ||
| 248 | <!-- Resolutions --> | 311 | <!-- Resolutions --> |
| @@ -272,12 +335,17 @@ | |||
| 272 | <string name="anti_aliasing_fxaa">FXAA</string> | 335 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 273 | <string name="anti_aliasing_smaa">SMAA</string> | 336 | <string name="anti_aliasing_smaa">SMAA</string> |
| 274 | 337 | ||
| 338 | <!-- Screen Layouts --> | ||
| 339 | <string name="screen_layout_landscape">横長</string> | ||
| 340 | <string name="screen_layout_portrait">縦長</string> | ||
| 341 | <string name="screen_layout_auto">自動</string> | ||
| 342 | |||
| 275 | <!-- Aspect Ratios --> | 343 | <!-- Aspect Ratios --> |
| 276 | <string name="ratio_default">デフォルト (16:9)</string> | 344 | <string name="ratio_default">デフォルト (16:9)</string> |
| 277 | <string name="ratio_force_four_three">強制 4:3</string> | 345 | <string name="ratio_force_four_three">強制 4:3</string> |
| 278 | <string name="ratio_force_twenty_one_nine">強制 21:9</string> | 346 | <string name="ratio_force_twenty_one_nine">強制 21:9</string> |
| 279 | <string name="ratio_force_sixteen_ten">強制 16:10</string> | 347 | <string name="ratio_force_sixteen_ten">強制 16:10</string> |
| 280 | <string name="ratio_stretch">ウィンドウã«åˆã‚ã›ã‚‹</string> | 348 | <string name="ratio_stretch">ç”»é¢ã«åˆã‚ã›ã‚‹</string> |
| 281 | 349 | ||
| 282 | <!-- CPU Accuracy --> | 350 | <!-- CPU Accuracy --> |
| 283 | <string name="cpu_accuracy_accurate">æ£ç¢º</string> | 351 | <string name="cpu_accuracy_accurate">æ£ç¢º</string> |
| @@ -289,7 +357,7 @@ | |||
| 289 | <string name="gamepad_left_stick">Lスティック</string> | 357 | <string name="gamepad_left_stick">Lスティック</string> |
| 290 | <string name="gamepad_right_stick">Rスティック</string> | 358 | <string name="gamepad_right_stick">Rスティック</string> |
| 291 | <string name="gamepad_home">HOMEボタン</string> | 359 | <string name="gamepad_home">HOMEボタン</string> |
| 292 | <string name="gamepad_screenshot">スクリーンショット</string> | 360 | <string name="gamepad_screenshot">ã‚ャプãƒãƒ£ãƒ¼ãƒœã‚¿ãƒ³</string> |
| 293 | 361 | ||
| 294 | <!-- Disk shader cache --> | 362 | <!-- Disk shader cache --> |
| 295 | <string name="preparing_shaders">シェーダーを準備ã—ã¦ã„ã¾ã™</string> | 363 | <string name="preparing_shaders">シェーダーを準備ã—ã¦ã„ã¾ã™</string> |
| @@ -306,8 +374,22 @@ | |||
| 306 | <string name="theme_mode_light">ライト</string> | 374 | <string name="theme_mode_light">ライト</string> |
| 307 | <string name="theme_mode_dark">ダーク</string> | 375 | <string name="theme_mode_dark">ダーク</string> |
| 308 | 376 | ||
| 309 | <!-- Black backgrounds theme --> | 377 | <!-- Audio output engines --> |
| 310 | <string name="use_black_backgrounds">黒色ã®èƒŒæ™¯ã‚’使用</string> | 378 | <string name="cubeb">cubeb</string> |
| 311 | <string name="use_black_backgrounds_description">ダークテーマã®ä½¿ç”¨æ™‚ã¯ã€é»’色ã®èƒŒæ™¯ã‚’有効ã«ã—ã¦ãã ã•ã„。</string> | ||
| 312 | 379 | ||
| 313 | </resources> | 380 | <!-- Black backgrounds theme --> |
| 381 | <string name="use_black_backgrounds">完全ãªé»’を使用</string> | ||
| 382 | <string name="use_black_backgrounds_description">ダークテーマã®èƒŒæ™¯è‰²ã«é»’ãŒé©ç”¨ã•れã¾ã™ã€‚</string> | ||
| 383 | |||
| 384 | <!-- Picture-In-Picture --> | ||
| 385 | <string name="picture_in_picture">ピクãƒãƒ£ãƒ¼ã‚¤ãƒ³ãƒ”クãƒãƒ£ãƒ¼</string> | ||
| 386 | <string name="picture_in_picture_description">ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰æ™‚ã«ã‚¦ã‚¤ãƒ³ãƒ‰ã‚¦ã‚’最å°åŒ–ã™ã‚‹</string> | ||
| 387 | <string name="pause">䏿–</string> | ||
| 388 | <string name="play">プレイ</string> | ||
| 389 | <string name="mute">消音</string> | ||
| 390 | <string name="unmute">消音解除</string> | ||
| 391 | |||
| 392 | <!-- Licenses screen strings --> | ||
| 393 | <string name="licenses">ライセンス</string> | ||
| 394 | <string name="license_fidelityfx_fsr_description">AMDã®é«˜å“質アップスケーリング</string> | ||
| 395 | </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 index 214f95706..1b9160a23 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">ì´ ì†Œí”„íŠ¸ì›¨ì–´ëŠ” 닌í…ë„ ìŠ¤ìœ„ì¹˜ 게임 콘솔용 ê²Œìž„ì„ ì‹¤í–‰í•©ë‹ˆë‹¤. 게임 타ì´í‹€ì´ë‚˜ keys는 í¬í•¨ë˜ì–´ 있지 않습니다.<br /><br />시작하기 ì „ì— ìž¥ì¹˜ ì €ìž¥ì†Œì—서 <![CDATA[<b> prod.keys </b>]]> 파ì¼ì„ 찾아주세요.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">ìžì„¸ížˆ 알아보기</a>]]></string> | 4 | <string name="app_disclaimer">ì´ ì†Œí”„íŠ¸ì›¨ì–´ëŠ” Nintendo Switch ê²Œìž„ì„ ì‹¤í–‰í•©ë‹ˆë‹¤. 게임 타ì´í‹€ì´ë‚˜ 키는 í¬í•¨ë˜ì–´ 있지 않습니다.<br /><br />시작하기 ì „ì— ìž¥ì¹˜ ì €ìž¥ì†Œì—서 <![CDATA[<b> prod.keys </b>]]> 파ì¼ì„ 찾아주세요.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">ìžì„¸ížˆ 알아보기</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">ì—ë®¬ë ˆì´ì…˜ì´ 활성화ë¨</string> | 5 | <string name="emulation_notification_channel_name">ì—ë®¬ë ˆì´ì…˜ì´ 활성화ë¨</string> |
| 6 | <string name="emulation_notification_channel_description">ì—ë®¬ë ˆì´ì…˜ì´ 실행 ì¤‘ì¼ ë•Œ ì˜êµ¬ ì•Œë¦¼ì„ í‘œì‹œí•©ë‹ˆë‹¤.</string> | 6 | <string name="emulation_notification_channel_description">ì—ë®¬ë ˆì´ì…˜ì´ 실행 ì¤‘ì¼ ë•Œ ì§€ì†ì 으로 ì•Œë¦¼ì„ í‘œì‹œí•©ë‹ˆë‹¤.</string> |
| 7 | <string name="emulation_notification_running">yuzu가 실행 중입니다.</string> | 7 | <string name="emulation_notification_running">yuzu가 실행 중입니다.</string> |
| 8 | <string name="notice_notification_channel_name">알림 ë° ì˜¤ë¥˜</string> | 8 | <string name="notice_notification_channel_name">알림 ë° ì˜¤ë¥˜</string> |
| 9 | <string name="notice_notification_channel_description">ë¬¸ì œê°€ ë°œìƒí•˜ë©´ ì•Œë¦¼ì„ í‘œì‹œí•©ë‹ˆë‹¤.</string> | 9 | <string name="notice_notification_channel_description">ë¬¸ì œê°€ ë°œìƒí•˜ë©´ ì•Œë¦¼ì„ í‘œì‹œí•©ë‹ˆë‹¤.</string> |
| @@ -11,26 +11,25 @@ | |||
| 11 | 11 | ||
| 12 | <!-- Setup strings --> | 12 | <!-- Setup strings --> |
| 13 | <string name="welcome">환ì˜í•©ë‹ˆë‹¤!</string> | 13 | <string name="welcome">환ì˜í•©ë‹ˆë‹¤!</string> |
| 14 | <string name="welcome_description"><b>yuzu</b> 를 ì„¤ì •í•˜ê³ ì—ë®¬ë ˆì´ì…˜ìœ¼ë¡œ ì´ë™í•˜ëŠ” ë°©ë²•ì„ ì•Œì•„ë³´ì„¸ìš”.</string> | 14 | <string name="welcome_description"><b>yuzu</b>를 ì„¤ì •í•˜ê³ ì—ë®¬ë ˆì´ì…˜ì„ 시작하세요.</string> |
| 15 | <string name="get_started">시작하기</string> | 15 | <string name="get_started">시작하기</string> |
| 16 | <string name="keys">Keys</string> | 16 | <string name="keys">키 ì„¤ì •</string> |
| 17 | <string name="keys_description">아래 ë²„íŠ¼ì„ ì‚¬ìš©í•˜ì—¬ <b>prod.keys</b> 파ì¼ì„ ì„ íƒí•©ë‹ˆë‹¤.</string> | 17 | <string name="keys_description">아래 버튼으로 <b>prod.keys</b> 파ì¼ì„ ì„ íƒí•©ë‹ˆë‹¤.</string> |
| 18 | <string name="select_keys">keys ì„ íƒ</string> | 18 | <string name="select_keys">키 ì„ íƒ</string> |
| 19 | <string name="games">게임</string> | 19 | <string name="games">게임</string> |
| 20 | <string name="games_description">아래 버튼으로 <b>게임</b> í´ë”를 ì„ íƒí•©ë‹ˆë‹¤.</string> | 20 | <string name="games_description">아래 버튼으로 <b>게임</b> í´ë”를 ì„ íƒí•©ë‹ˆë‹¤.</string> |
| 21 | <string name="done">완료</string> | 21 | <string name="done">완료</string> |
| 22 | <string name="done_description">ëª¨ë“ ì¤€ë¹„ê°€ 완료ë˜ì—ˆìŠµë‹ˆë‹¤.\nê²Œìž„ì„ ì¦ê¸°ì„¸ìš”!</string> | 22 | <string name="done_description">ëª¨ë‘ ì¤€ë¹„ë˜ì—ˆìŠµë‹ˆë‹¤.\nê²Œìž„ì„ ì¦ê¸°ì„¸ìš”!</string> |
| 23 | <string name="text_continue">계ì†</string> | 23 | <string name="text_continue">계ì†</string> |
| 24 | <string name="next">다ìŒ</string> | 24 | <string name="next">다ìŒ</string> |
| 25 | <string name="back">뒤로</string> | 25 | <string name="back">ì´ì „</string> |
| 26 | <string name="add_games">게임 추가</string> | 26 | <string name="add_games">게임 추가</string> |
| 27 | <string name="add_games_description">게임 í´ë” ì„ íƒ</string> | 27 | <string name="add_games_description">게임 í´ë” ì„ íƒ</string> |
| 28 | |||
| 29 | <!-- Home strings --> | 28 | <!-- Home strings --> |
| 30 | <string name="home_games">게임</string> | 29 | <string name="home_games">게임</string> |
| 31 | <string name="home_search">검색</string> | 30 | <string name="home_search">검색</string> |
| 32 | <string name="home_settings">ì„¤ì •</string> | 31 | <string name="home_settings">ì„¤ì •</string> |
| 33 | <string name="empty_gamelist">파ì¼ì„ ì°¾ì„ ìˆ˜ 없거나 ì•„ì§ ê²Œìž„ ë””ë ‰í† ë¦¬ë¥¼ ì„ íƒí•˜ì§€ 않았습니다.</string> | 32 | <string name="empty_gamelist">파ì¼ì„ ì°¾ì„ ìˆ˜ 없거나 ì•„ì§ ê²Œìž„ ë””ë ‰í„°ë¦¬ë¥¼ ì„ íƒí•˜ì§€ 않았습니다.</string> |
| 34 | <string name="search_and_filter_games">게임 검색 ë° í•„í„°ë§</string> | 33 | <string name="search_and_filter_games">게임 검색 ë° í•„í„°ë§</string> |
| 35 | <string name="select_games_folder">게임 í´ë” ì„ íƒ</string> | 34 | <string name="select_games_folder">게임 í´ë” ì„ íƒ</string> |
| 36 | <string name="select_games_folder_description">yuzuê°€ 게임 목ë¡ì„ 채울 수 있ë„ë¡ í—ˆìš©</string> | 35 | <string name="select_games_folder_description">yuzuê°€ 게임 목ë¡ì„ 채울 수 있ë„ë¡ í—ˆìš©</string> |
| @@ -38,140 +37,160 @@ | |||
| 38 | <string name="add_games_warning_description">í´ë”를 ì„ íƒí•˜ì§€ 않으면 게임 목ë¡ì— ê²Œìž„ì´ í‘œì‹œë˜ì§€ 않습니다.</string> | 37 | <string name="add_games_warning_description">í´ë”를 ì„ íƒí•˜ì§€ 않으면 게임 목ë¡ì— ê²Œìž„ì´ í‘œì‹œë˜ì§€ 않습니다.</string> |
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 38 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 40 | <string name="home_search_games">게임 검색</string> | 39 | <string name="home_search_games">게임 검색</string> |
| 41 | <string name="games_dir_selected">게임 ë””ë ‰í„°ë¦¬ ì„ íƒ</string> | 40 | <string name="games_dir_selected">게임 ë””ë ‰í„°ë¦¬ë¥¼ ì„¤ì •í–ˆìŠµë‹ˆë‹¤.</string> |
| 42 | <string name="install_prod_keys">prod.keys 설치</string> | 41 | <string name="install_prod_keys">prod.keys 설치</string> |
| 43 | <string name="install_prod_keys_description">íŒë§¤ìš© 게임 암호 í•´ë…ì— ìš”êµ¬</string> | 42 | <string name="install_prod_keys_description">패키지 게임 암호 í•´ë…ì— í•„ìš”</string> |
| 44 | <string name="install_prod_keys_warning">keys 추가를 ê±´ë„ˆë›°ê² ìŠµë‹ˆê¹Œ?</string> | 43 | <string name="install_prod_keys_warning">키 추가를 ê±´ë„ˆë›°ê² ìŠµë‹ˆê¹Œ?</string> |
| 45 | <string name="install_prod_keys_warning_description">ì •í’ˆ ê²Œìž„ì„ ì—ë®¬ë ˆì´íŠ¸í•˜ë ¤ë©´ ìœ íš¨í•œ keysê°€ 필요합니다. 계ì†í•˜ë©´ ìžì²´ ì œìž‘ 앱만 ìž‘ë™í•©ë‹ˆë‹¤.</string> | 44 | <string name="install_prod_keys_warning_description">패키지 ê²Œìž„ì„ ì—ë®¬ë ˆì´íŠ¸í•˜ë ¤ë©´ ìœ íš¨í•œ 키 ê°’ì´ í•„ìš”í•©ë‹ˆë‹¤. ì´ ë‹¨ê³„ë¥¼ 건너뛰면 홈브류 게임만 ì‹¤í–‰í• ìˆ˜ 있습니다.</string> |
| 46 | <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> | 45 | <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> |
| 47 | <string name="notifications">알림</string> | 46 | <string name="notifications">알림</string> |
| 48 | <string name="notifications_description">아래 버튼으로 알림 ê¶Œí•œì„ ë¶€ì—¬í•©ë‹ˆë‹¤.</string> | 47 | <string name="notifications_description">아래 버튼으로 알림 ê¶Œí•œì„ ë¶€ì—¬í•©ë‹ˆë‹¤.</string> |
| 49 | <string name="give_permission">권한 부여</string> | 48 | <string name="give_permission">알림 켜기</string> |
| 50 | <string name="notification_warning">알림 권한 부여를 ê±´ë„ˆë›°ê² ìŠµë‹ˆê¹Œ?</string> | 49 | <string name="notification_warning">ì•Œë¦¼ì„ ë„ê² ìŠµë‹ˆê¹Œ?</string> |
| 51 | <string name="notification_warning_description">yuzu는 중요한 ì •ë³´ë¥¼ ì•Œë ¤ë“œë¦¬ì§€ 않습니다.</string> | 50 | <string name="notification_warning_description">yuzuê°€ 중요한 ì •ë³´ë¥¼ ì•Œë ¤ë“œë¦¬ì§€ 않습니다.</string> |
| 52 | <string name="permission_denied">권한 ê±°ë¶€ë¨</string> | 51 | <string name="permission_denied">권한 ê±°ë¶€ë¨</string> |
| 53 | <string name="permission_denied_description">ì´ ê¶Œí•œì„ ë„ˆë¬´ ë§Žì´ ê±°ë¶€í–ˆìœ¼ë¯€ë¡œ ì´ì œ 시스템 ì„¤ì •ì—서 수ë™ìœ¼ë¡œ ê¶Œí•œì„ ë¶€ì—¬í•´ì•¼ 합니다.</string> | 52 | <string name="permission_denied_description">권한 í—ˆìš©ì„ ë„ˆë¬´ ë§Žì´ ê±°ë¶€í•˜ì—¬ 시스템 ì„¤ì •ì—서 수ë™ìœ¼ë¡œ ê¶Œí•œì„ ë¶€ì—¬í•´ì•¼ 합니다.</string> |
| 54 | <string name="about">ì •ë³´</string> | 53 | <string name="about">ì •ë³´</string> |
| 55 | <string name="about_description">빌드 ë²„ì „, í¬ë ˆë”§ 등</string> | 54 | <string name="about_description">빌드 ë²„ì „, í¬ë ˆë”§ 등</string> |
| 56 | <string name="warning_help">ë„움ë§</string> | 55 | <string name="warning_help">ë„움ë§</string> |
| 57 | <string name="warning_skip">건너뛰기</string> | 56 | <string name="warning_skip">건너뛰기</string> |
| 58 | <string name="warning_cancel">취소</string> | 57 | <string name="warning_cancel">취소</string> |
| 59 | <string name="install_amiibo_keys">Amiibo keys 설치</string> | 58 | <string name="install_amiibo_keys">amiibo 키 설치</string> |
| 60 | <string name="install_amiibo_keys_description">게임ì—서 아미보 사용 시 í•„ìš”</string> | 59 | <string name="install_amiibo_keys_description">게임ì—서 amiibo 사용 시 í•„ìš”</string> |
| 61 | <string name="invalid_keys_file">ìž˜ëª»ëœ keys íŒŒì¼ ì„ íƒ</string> | 60 | <string name="invalid_keys_file">ìž˜ëª»ëœ í‚¤ 파ì¼ì´ ì„ íƒë¨</string> |
| 62 | <string name="install_keys_success">keysê°€ 성공ì 으로 설치ë¨</string> | 61 | <string name="install_keys_success">키 ê°’ì„ ì„¤ì¹˜í–ˆìŠµë‹ˆë‹¤.</string> |
| 63 | <string name="reading_keys_failure">암호화 keys ì½ê¸° 오류</string> | 62 | <string name="reading_keys_failure">암호화 키 ì½ê¸° 오류</string> |
| 64 | <string name="invalid_keys_error">ìž˜ëª»ëœ ì•”í˜¸í™” keys</string> | 63 | <string name="install_prod_keys_failure_extension_description">키 파ì¼ì˜ 확장ìžê°€ .keysì¸ì§€ 확ì¸í•˜ê³ 다시 시ë„하세요.</string> |
| 64 | <string name="install_amiibo_keys_failure_extension_description">키 파ì¼ì˜ 확장ìžê°€ .binì¸ì§€ 확ì¸í•˜ê³ 다시 시ë„하세요.</string> | ||
| 65 | <string name="invalid_keys_error">암호화 키가 올바르지 않ìŒ</string> | ||
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 66 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">ì„ íƒí•œ 파ì¼ì´ 잘못ë˜ì—ˆê±°ë‚˜ ì†ìƒë˜ì—ˆìŠµë‹ˆë‹¤. keys를 다시 ë¤í”„하세요.</string> | 67 | <string name="install_keys_failure_description">ì„ íƒí•œ 파ì¼ì´ 잘못ë˜ì—ˆê±°ë‚˜ ì†ìƒë˜ì—ˆìŠµë‹ˆë‹¤. 키를 다시 ë¤í”„하세요.</string> |
| 67 | <string name="install_gpu_driver">GPU 드ë¼ì´ë²„ 설치</string> | 68 | <string name="install_gpu_driver">GPU 드ë¼ì´ë²„ 설치</string> |
| 68 | <string name="install_gpu_driver_description">ìž ìž¬ì 으로 ë” ë‚˜ì€ ì„±ëŠ¥ ë˜ëŠ” ì •í™•ì„±ì„ ìœ„í•´ 대체 드ë¼ì´ë²„를 설치하세요.</string> | 69 | <string name="install_gpu_driver_description">ìž ìž¬ì 으로 ë” ë‚˜ì€ ì„±ëŠ¥ ë˜ëŠ” ì •í™•ì„±ì„ ìœ„í•´ 대체 드ë¼ì´ë²„를 설치하세요.</string> |
| 69 | <string name="advanced_settings">ê³ ê¸‰ ì„¤ì •</string> | 70 | <string name="advanced_settings">ê³ ê¸‰ ì„¤ì •</string> |
| 70 | <string name="settings_description">ì—ë®¬ë ˆì´í„° ì„¤ì • 구성</string> | 71 | <string name="settings_description">ì—ë®¬ë ˆì´í„° ì„¤ì • 구성</string> |
| 71 | <string name="search_recently_played">최근 í”Œë ˆì´í•œ 게임</string> | 72 | <string name="search_recently_played">최근 í”Œë ˆì´</string> |
| 72 | <string name="search_recently_added">최근 추가한 게임</string> | 73 | <string name="search_recently_added">최근 추가</string> |
| 73 | <string name="search_retail">íŒë§¤ìš©</string> | 74 | <string name="search_retail">패키지</string> |
| 74 | <string name="search_homebrew">홈브류</string> | 75 | <string name="search_homebrew">홈브류</string> |
| 75 | <string name="open_user_folder">yuzu í´ë” 열기</string> | 76 | <string name="open_user_folder">yuzu í´ë” 열기</string> |
| 76 | <string name="open_user_folder_description">yuzuì˜ ë‚´ë¶€ íŒŒì¼ ê´€ë¦¬</string> | 77 | <string name="open_user_folder_description">yuzuì˜ ë‚´ë¶€ íŒŒì¼ ê´€ë¦¬</string> |
| 77 | <string name="theme_and_color_description">앱 모양 ìˆ˜ì •</string> | 78 | <string name="theme_and_color_description">앱 ë””ìžì¸ 편집</string> |
| 78 | <string name="no_file_manager">íŒŒì¼ ê´€ë¦¬ìžë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ</string> | 79 | <string name="no_file_manager">íŒŒì¼ ê´€ë¦¬ìžë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ</string> |
| 79 | <string name="notification_no_directory_link">yuzu ë””ë ‰í† ë¦¬ë¥¼ ì—´ 수 ì—†ìŒ</string> | 80 | <string name="notification_no_directory_link">yuzu ë””ë ‰í„°ë¦¬ë¥¼ ì—´ 수 ì—†ìŒ</string> |
| 80 | <string name="notification_no_directory_link_description">íŒŒì¼ ê´€ë¦¬ìžì˜ 사ì´ë“œ 패ë„ì—서 ì‚¬ìš©ìž í´ë”를 수ë™ìœ¼ë¡œ 찾아주세요.</string> | 81 | <string name="notification_no_directory_link_description">íŒŒì¼ ê´€ë¦¬ìžì˜ 사ì´ë“œ 패ë„ì—서 ì‚¬ìš©ìž í´ë”를 수ë™ìœ¼ë¡œ 찾아주세요.</string> |
| 81 | <string name="manage_save_data">ì €ìž¥ ë°ì´í„° 관리</string> | 82 | <string name="manage_save_data">ì €ìž¥ ë°ì´í„° 관리</string> |
| 82 | <string name="manage_save_data_description">ë°ì´í„°ë¥¼ ì €ìž¥í–ˆìŠµë‹ˆë‹¤. 아래ì—서 ì˜µì…˜ì„ ì„ íƒí•˜ì„¸ìš”.</string> | 83 | <string name="manage_save_data_description">ì €ìž¥ ë°ì´í„°ë¥¼ 발견했습니다. 아래ì—서 ì˜µì…˜ì„ ì„ íƒí•˜ì„¸ìš”.</string> |
| 83 | <string name="import_export_saves_description">ì €ìž¥ íŒŒì¼ ê°€ì ¸ì˜¤ê¸° ë˜ëŠ” 내보내기</string> | 84 | <string name="import_export_saves_description">ì €ìž¥ íŒŒì¼ ê°€ì ¸ì˜¤ê¸° ë˜ëŠ” 내보내기</string> |
| 84 | <string name="save_file_imported_success">ê°€ì ¸ì˜¤ê¸° 성공</string> | 85 | <string name="save_file_imported_success">ë°ì´í„°ë¥¼ 불러왔습니다.</string> |
| 85 | <string name="save_file_invalid_zip_structure">ì €ìž¥ ë””ë ‰í„°ë¦¬ 구조가 잘못ë¨</string> | 86 | <string name="save_file_invalid_zip_structure">올바르지 ì•Šì€ ì €ìž¥ ë””ë ‰í„°ë¦¬ 구조</string> |
| 86 | <string name="save_file_invalid_zip_structure_description">첫 번째 하위 í´ë” ì´ë¦„ì€ ê²Œìž„ì˜ íƒ€ì´í‹€ ID여야 합니다.</string> | 87 | <string name="save_file_invalid_zip_structure_description">첫 번째 하위 í´ë” ì´ë¦„ì€ ê²Œìž„ì˜ íƒ€ì´í‹€ ID여야 합니다.</string> |
| 87 | <string name="import_saves">ê°€ì ¸ì˜¤ê¸°</string> | 88 | <string name="import_saves">ê°€ì ¸ì˜¤ê¸°</string> |
| 88 | <string name="export_saves">내보내기</string> | 89 | <string name="export_saves">내보내기</string> |
| 89 | 90 | <string name="install_firmware">펌웨어 설치</string> | |
| 91 | <string name="install_firmware_description">펌웨어는 ZIP 파ì¼ì´ë©° ì¼ë¶€ ê²Œìž„ì„ ë¶€íŒ…í•˜ëŠ” ë° í•„ìš”í•©ë‹ˆë‹¤.</string> | ||
| 92 | <string name="firmware_installing">펌웨어 설치</string> | ||
| 93 | <string name="firmware_installed_success">펌웨어를 설치했습니다.</string> | ||
| 94 | <string name="firmware_installed_failure">펌웨어 설치 실패</string> | ||
| 95 | <string name="share_log">디버그 로그 ê³µìœ </string> | ||
| 96 | <string name="share_log_description">yuzuì˜ ë¡œê·¸ 파ì¼ì„ ê³µìœ í•˜ì—¬ ë¬¸ì œ 디버깅하기</string> | ||
| 97 | <string name="share_log_missing">로그 파ì¼ì„ ì°¾ì„ ìˆ˜ 없습니다.</string> | ||
| 98 | <string name="install_game_content">게임 콘í…ì¸ ì„¤ì¹˜</string> | ||
| 99 | <string name="install_game_content_description">게임 ì—…ë°ì´íЏ ë˜ëŠ” DLC 설치</string> | ||
| 100 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 90 | <!-- About screen strings --> | 101 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">ê°€ì´ì•„는 진짜가 아님</string> | 102 | <string name="gaia_is_not_real">ê°€ì´ì•„는 진짜가 아님</string> |
| 92 | <string name="copied_to_clipboard">í´ë¦½ë³´ë“œì— 복사</string> | 103 | <string name="copied_to_clipboard">í´ë¦½ë³´ë“œì— 복사ë˜ì—ˆìŠµë‹ˆë‹¤.</string> |
| 93 | <string name="about_app_description">오픈 소스 스위치 ì—ë®¬ë ˆì´í„°</string> | 104 | <string name="about_app_description">오픈 소스 Switch ì—ë®¬ë ˆì´í„°</string> |
| 94 | <string name="contributors">기여ìž</string> | 105 | <string name="contributors">기여ìž</string> |
| 95 | <string name="contributors_description">yuzu íŒ€ì˜ \u2764로 ì œìž‘</string> | 106 | <string name="contributors_description">yuzu íŒ€ì˜ \u2764로 ì œìž‘</string> |
| 96 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 107 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 108 | <string name="licenses_description">Androidìš© yuzu를 가능하게 하는 프로ì 트</string> | ||
| 97 | <string name="build">빌드</string> | 109 | <string name="build">빌드</string> |
| 98 | <string name="support_link">https://discord.gg/u77vRWY</string> | 110 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 99 | <string name="website_link">https://yuzu-emu.org/</string> | 111 | <string name="website_link">https://yuzu-emu.org/</string> |
| 100 | <string name="github_link">https://github.com/yuzu-emu</string> | 112 | <string name="github_link">https://github.com/yuzu-emu</string> |
| 101 | 113 | ||
| 102 | <!-- Early access upgrade strings --> | 114 | <!-- Early access upgrade strings --> |
| 103 | <string name="early_access">미리 체험하기</string> | 115 | <string name="early_access">앞서 해보기</string> |
| 104 | <string name="get_early_access">미리 체험하기 ì‹ ì²</string> | 116 | <string name="get_early_access">앞서 해보기 ì‹ ì²</string> |
| 105 | <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> | 117 | <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> |
| 106 | <string name="get_early_access_description">최첨단 기능, 미리 체험하기 ì—…ë°ì´íЏ 등</string> | 118 | <string name="get_early_access_description">ìµœì‹ ê¸°ëŠ¥, ì—…ë°ì´íЏ 미리 ì²´í—˜ 등</string> |
| 107 | <string name="early_access_benefits">미리 체험하기 혜íƒ</string> | 119 | <string name="early_access_benefits">앞서 해보기 혜íƒ</string> |
| 108 | <string name="cutting_edge_features">최첨단 기능</string> | 120 | <string name="cutting_edge_features">ìµœì‹ ê¸°ëŠ¥</string> |
| 109 | <string name="early_access_updates">미리 체험하기 ì—…ë°ì´íЏ</string> | 121 | <string name="early_access_updates">ì—…ë°ì´íЏ 미리 ì²´í—˜</string> |
| 110 | <string name="no_manual_installation">ìˆ˜ë™ ì„¤ì¹˜ 불필요</string> | 122 | <string name="no_manual_installation">ìˆ˜ë™ ì„¤ì¹˜ 불필요</string> |
| 111 | <string name="prioritized_support">ìš°ì„ ì§€ì›</string> | 123 | <string name="prioritized_support">ìš°ì„ ì§€ì›</string> |
| 112 | <string name="helping_game_preservation">게임 ë³´ì¡´ ë„움주기</string> | 124 | <string name="helping_game_preservation">게임 ë³´ì¡´ ì§€ì›</string> |
| 113 | <string name="our_eternal_gratitude">ì˜ì›í•œ ê°ì‚¬ì˜ 마ìŒì„ ì „í•©ë‹ˆë‹¤</string> | 125 | <string name="our_eternal_gratitude">ìš°ë¦¬ì˜ ì˜ì›í•œ ê°ì‚¬ì˜ 마ìŒ</string> |
| 114 | <string name="are_you_interested">관심 있으세요?</string> | 126 | <string name="are_you_interested">관심 있으세요?</string> |
| 115 | 127 | ||
| 116 | <!-- General settings strings --> | 128 | <!-- General settings strings --> |
| 117 | <string name="frame_limit_enable">ì œí•œ ì†ë„ 활성화</string> | 129 | <string name="frame_limit_enable">ì†ë„ ì œí•œ</string> |
| 118 | <string name="frame_limit_enable_description">활성화하면 ì—ë®¬ë ˆì´ì…˜ ì†ë„ê°€ ì •ìƒ ì†ë„ì˜ ì§€ì •ëœ ë¹„ìœ¨ë¡œ ì œí•œë©ë‹ˆë‹¤.</string> | 130 | <string name="frame_limit_enable_description">ì—ë®¬ë ˆì´ì…˜ ì†ë„를 ì •ìƒ ì†ë„ì˜ ì§€ì •ëœ ë¹„ìœ¨ë¡œ ì œí•œí•©ë‹ˆë‹¤.</string> |
| 119 | <string name="frame_limit_slider">ì†ë„ ì œí•œ 비율</string> | 131 | <string name="frame_limit_slider">ì†ë„ ì œí•œ 비율</string> |
| 120 | <string name="frame_limit_slider_description">ì—ë®¬ë ˆì´ì…˜ ì†ë„를 ì œí•œí• ë¹„ìœ¨ì„ ì§€ì •í•©ë‹ˆë‹¤. ê¸°ë³¸ê°’ì¸ 100%로 ì„¤ì •í•˜ë©´ ì—ë®¬ë ˆì´ì…˜ì´ ì •ìƒ ì†ë„로 ì œí•œë©ë‹ˆë‹¤. ê°’ì´ ë†’ê±°ë‚˜ 낮으면 ì†ë„ ì œí•œì´ ì¦ê°€í•˜ê±°ë‚˜ ê°ì†Œí•©ë‹ˆë‹¤.</string> | 132 | <string name="frame_limit_slider_description">ì—ë®¬ë ˆì´ì…˜ ì†ë„ì˜ ì œí•œ ë¹„ìœ¨ì„ ì§€ì •í•©ë‹ˆë‹¤. 100%ê°€ ì •ìƒ ì†ë„입니다. ê°’ì´ ë†’ê±°ë‚˜ 낮으면 ì†ë„ ì œí•œì´ ì¦ê°€í•˜ê±°ë‚˜ ê°ì†Œí•©ë‹ˆë‹¤.</string> |
| 121 | <string name="cpu_accuracy">CPU ì •í™•ë„</string> | 133 | <string name="cpu_accuracy">CPU ì •í™•ë„</string> |
| 122 | |||
| 123 | <!-- System settings strings --> | 134 | <!-- System settings strings --> |
| 124 | <string name="use_docked_mode">ë„킹 모드</string> | 135 | <string name="use_docked_mode">ë… ëª¨ë“œ</string> |
| 125 | <string name="use_docked_mode_description">ë„킹 모드ì—서 ì—ë®¬ë ˆì´ì…˜í•˜ë©´ ì„±ëŠ¥ì´ ì €í•˜ë˜ëŠ” ëŒ€ì‹ í•´ìƒë„ê°€ í–¥ìƒë©ë‹ˆë‹¤.</string> | 136 | <string name="use_docked_mode_description">í•´ìƒë„를 높ì´ë©° ì„±ëŠ¥ì´ ì €í•˜ë©ë‹ˆë‹¤. 비활성화시 휴대 모드가 사용ë˜ë©° í•´ìƒë„는 ë‚®ì•„ì§€ê³ ì„±ëŠ¥ì€ í–¥ìƒë©ë‹ˆë‹¤.</string> |
| 126 | <string name="emulated_region">ì—ë®¬ë ˆì´íŠ¸ëœ ì§€ì—</string> | 137 | <string name="emulated_region">ì—ë®¬ë ˆì´íЏ ì§€ì—</string> |
| 127 | <string name="emulated_language">ì—ë®¬ë ˆì´íŠ¸ëœ ì–¸ì–´</string> | 138 | <string name="emulated_language">ì—ë®¬ë ˆì´íЏ 언어</string> |
| 128 | <string name="select_rtc_date">RTC ë‚ ì§œ ì„ íƒ</string> | 139 | <string name="select_rtc_date">RTC ë‚ ì§œ ì„ íƒ</string> |
| 129 | <string name="select_rtc_time">RTC 시간 ì„ íƒ</string> | 140 | <string name="select_rtc_time">RTC 시간 ì„ íƒ</string> |
| 130 | <string name="use_custom_rtc">커스텀 RTC 활성화</string> | 141 | <string name="use_custom_rtc">ì‚¬ìš©ìž ì§€ì • RTC</string> |
| 131 | <string name="use_custom_rtc_description">ì´ ì„¤ì •ì„ ì‚¬ìš©í•˜ë©´ 현재 시스템 시간과 별ë„로 ì‚¬ìš©ìž ì§€ì • 실시간 시계를 ì„¤ì •í• ìˆ˜ 있ìŒ</string> | 142 | <string name="use_custom_rtc_description">현재 시스템 시간과 별ë„로 ì‚¬ìš©ìž ì§€ì • 실시간 시계를 ì„¤ì •í• ìˆ˜ 있습니다.</string> |
| 132 | <string name="set_custom_rtc">커스텀 RTC ì„¤ì •</string> | 143 | <string name="set_custom_rtc">ì‚¬ìš©ìž ì§€ì • RTC ì„¤ì •</string> |
| 133 | 144 | ||
| 134 | <!-- Graphics settings strings --> | 145 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">API</string> | ||
| 136 | <string name="renderer_accuracy">ì •í™•ë„ ìˆ˜ì¤€</string> | 146 | <string name="renderer_accuracy">ì •í™•ë„ ìˆ˜ì¤€</string> |
| 137 | <string name="renderer_resolution">í•´ìƒë„</string> | 147 | <string name="renderer_resolution">í•´ìƒë„ (휴대 모드/ë… ëª¨ë“œ)</string> |
| 138 | <string name="renderer_vsync">수ì§ë™ê¸°í™” 모드</string> | 148 | <string name="renderer_vsync">수ì§ë™ê¸°í™” 모드</string> |
| 139 | <string name="renderer_aspect_ratio">화면비</string> | 149 | <string name="renderer_aspect_ratio">화면비</string> |
| 140 | <string name="renderer_scaling_filter">ì°½ ì ì‘ í•„í„°</string> | 150 | <string name="renderer_scaling_filter">윈ë„ìš° ì ì‘ í•„í„°</string> |
| 141 | <string name="renderer_anti_aliasing">안티-ì—ì¼ë¦¬ì–´ì‹± 방법</string> | 151 | <string name="renderer_anti_aliasing">안티ì—ì¼ë¦¬ì–´ì‹± 방법</string> |
| 142 | <string name="renderer_force_max_clock">최대 í´ëŸ ê°•ì œ ì„¤ì • (ì•„ë“œë ˆë…¸ë§Œ 해당)</string> | 152 | <string name="renderer_force_max_clock">최대 í´ëŸ ê°•ì œ ì„¤ì • (ì•„ë“œë ˆë…¸ ì „ìš©)</string> |
| 143 | <string name="renderer_force_max_clock_description">GPUê°€ 가능한 최대 í´ëŸìœ¼ë¡œ 실행ë˜ë„ë¡ ê°•ì œí•©ë‹ˆë‹¤ (ì—´ ì œì•½ ì¡°ê±´ì€ ì—¬ì „ížˆ ì ìš©ë©ë‹ˆë‹¤).</string> | 153 | <string name="renderer_force_max_clock_description">GPUê°€ 가능한 최대 í´ëŸìœ¼ë¡œ 실행ë˜ë„ë¡ ê°•ì œí•©ë‹ˆë‹¤ (ì—´ ì œì•½ ì¡°ê±´ì€ ì—¬ì „ížˆ ì ìš©ë©ë‹ˆë‹¤).</string> |
| 144 | <string name="renderer_asynchronous_shaders">비ë™ê¸° ì…°ì´ë” 사용</string> | 154 | <string name="renderer_asynchronous_shaders">비ë™ê¸° ì…°ì´ë” 사용</string> |
| 145 | <string name="renderer_asynchronous_shaders_description">ì…°ì´ë”를 비ë™ê¸°ì‹ìœ¼ë¡œ 컴파ì¼í•˜ë¯€ë¡œ ëŠê¹€ 현ìƒì´ 줄어들지만 글리치가 ë°œìƒí• 수 있습니다.</string> | 155 | <string name="renderer_asynchronous_shaders_description">ì…°ì´ë”를 비ë™ê¸°ì‹ìœ¼ë¡œ 컴파ì¼í•˜ì—¬ ëŠê¹€ 현ìƒì„ 줄ì´ì§€ë§Œ 글리치가 ë°œìƒí• 수 있습니다.</string> |
| 146 | <string name="renderer_debug">그래픽 디버깅 활성화</string> | 156 | <string name="renderer_reactive_flushing">ë°˜ì‘형 플러싱 사용</string> |
| 147 | <string name="renderer_debug_description">ì´ ì˜µì…˜ì„ ì„ íƒí•˜ë©´ 그래픽 APIê°€ ëŠë¦° 디버깅 모드로 ì „í™˜ë©ë‹ˆë‹¤.</string> | 157 | <string name="renderer_reactive_flushing_description">ì¼ë¶€ 게임ì—서 성능 ì €í•˜ë¥¼ ê°ìˆ˜í•˜ê³ ë Œë”ë§ ì •í™•ë„를 í–¥ìƒí•©ë‹ˆë‹¤.</string> |
| 148 | <string name="use_disk_shader_cache">ë””ìŠ¤í¬ ì…°ì´ë” ìºì‹œ 사용</string> | 158 | <string name="use_disk_shader_cache">ë””ìŠ¤í¬ ì…°ì´ë” ìºì‹œ</string> |
| 149 | <string name="use_disk_shader_cache_description">ìƒì„±ëœ ì…°ì´ë”를 디스í¬ì— ì €ìž¥í•˜ê³ ë¶ˆëŸ¬ì˜¤ê¸°í•˜ì—¬ ëŠê¹€ 현ìƒì„ 줄입니다.</string> | 159 | <string name="use_disk_shader_cache_description">ìƒì„±ëœ ì…°ì´ë”를 ë¡œì»¬ì— ì €ìž¥í•˜ê³ ë¡œë“œí•˜ì—¬ ëŠê¹€ 현ìƒì„ 줄입니다.</string> |
| 150 | 160 | ||
| 151 | <!-- Audio settings strings --> | 161 | <!-- Debug settings strings --> |
| 162 | <string name="cpu">CPU</string> | ||
| 163 | <string name="renderer_api">API</string> | ||
| 164 | <string name="renderer_debug">그래픽 디버깅</string> | ||
| 165 | <string name="renderer_debug_description">그래픽 API를 ëŠë¦° 디버깅 모드로 ì„¤ì •í•©ë‹ˆë‹¤.</string> | ||
| 152 | <string name="audio_volume">볼륨</string> | 166 | <string name="audio_volume">볼륨</string> |
| 153 | <string name="audio_volume_description">오디오 ì¶œë ¥ì˜ ë³¼ë¥¨ì„ ì§€ì •í•©ë‹ˆë‹¤.</string> | 167 | <string name="audio_volume_description">오디오 ì¶œë ¥ì˜ ë³¼ë¥¨ì„ ì§€ì •í•©ë‹ˆë‹¤.</string> |
| 154 | 168 | ||
| 155 | <!-- Miscellaneous --> | 169 | <!-- Miscellaneous --> |
| 156 | <string name="slider_default">기본값</string> | 170 | <string name="slider_default">기본값</string> |
| 157 | <string name="ini_saved">ì €ìž¥ëœ ì„¤ì •</string> | 171 | <string name="ini_saved">ì„¤ì •ì´ ì €ìž¥ë˜ì—ˆìŠµë‹ˆë‹¤.</string> |
| 158 | <string name="gameid_saved">%1$s를 위해 ì €ìž¥ëœ ì„¤ì •</string> | 172 | <string name="gameid_saved">%1$s ì „ìš© ì„¤ì •ì´ ì €ìž¥ë˜ì—ˆìŠµë‹ˆë‹¤.</string> |
| 159 | <string name="error_saving">%1$s.ini ì €ìž¥ 중 오류: %2$s</string> | 173 | <string name="error_saving">%1$s.ini ì €ìž¥ 중 오류 ë°œìƒ: %2$s</string> |
| 160 | <string name="loading">불러오기 중...</string> | 174 | <string name="loading">불러오는 중...</string> |
| 161 | <string name="reset_setting_confirmation">ì´ ì„¤ì •ì„ ê¸°ë³¸ê°’ìœ¼ë¡œ ë˜ëŒë¦¬ê² 습니까?</string> | 175 | <string name="reset_setting_confirmation">ì´ ì„¤ì •ì„ ê¸°ë³¸ê°’ìœ¼ë¡œ ìž¬ì„¤ì •í•˜ê² ìŠµë‹ˆê¹Œ?</string> |
| 162 | <string name="reset_to_default">기본값으로 ìž¬ì„¤ì •</string> | 176 | <string name="reset_to_default">기본값으로 ìž¬ì„¤ì •</string> |
| 163 | <string name="reset_all_settings">ëª¨ë“ ì„¤ì •ì„ ì´ˆê¸°í™”í•˜ê² ìŠµë‹ˆê¹Œ?</string> | 177 | <string name="reset_all_settings">ëª¨ë“ ì„¤ì •ì„ ì´ˆê¸°í™”í•˜ê² ìŠµë‹ˆê¹Œ?</string> |
| 164 | <string name="reset_all_settings_description">ëª¨ë“ ê³ ê¸‰ ì„¤ì •ì´ ê¸°ë³¸ 구성으로 ìž¬ì„¤ì •ë©ë‹ˆë‹¤. ì´ ì„¤ì •ì€ ë˜ëŒë¦´ 수 없습니다.</string> | 178 | <string name="reset_all_settings_description">ëª¨ë“ ê³ ê¸‰ ì„¤ì •ì´ ê¸°ë³¸ 구성으로 ìž¬ì„¤ì •ë©ë‹ˆë‹¤. ì´ ìž‘ì—…ì€ ë˜ëŒë¦´ 수 없습니다.</string> |
| 165 | <string name="settings_reset">ì„¤ì • 초기화</string> | 179 | <string name="settings_reset">ì„¤ì • 초기화</string> |
| 166 | <string name="close">닫기</string> | 180 | <string name="close">닫기</string> |
| 167 | <string name="learn_more">ìžì„¸ížˆ 알아보기</string> | 181 | <string name="learn_more">ìžì„¸ížˆ</string> |
| 168 | 182 | <string name="auto">ìžë™</string> | |
| 183 | <string name="submit">ì œì¶œ</string> | ||
| 184 | <string name="string_null">Null</string> | ||
| 185 | <string name="string_import">ê°€ì ¸ì˜¤ê¸°</string> | ||
| 186 | <string name="export">내보내기</string> | ||
| 169 | <!-- GPU driver installation --> | 187 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">GPU 드ë¼ì´ë²„ ì„ íƒ</string> | 188 | <string name="select_gpu_driver">GPU 드ë¼ì´ë²„ ì„ íƒ</string> |
| 171 | <string name="select_gpu_driver_title">현재 사용 ì¤‘ì¸ GPU 드ë¼ì´ë²„를 êµì²´í•˜ê² 습니까?</string> | 189 | <string name="select_gpu_driver_title">현재 ì‚¬ìš©ì¤‘ì¸ GPU 드ë¼ì´ë²„를 ë³€ê²½í•˜ê² ìŠµë‹ˆê¹Œ?</string> |
| 172 | <string name="select_gpu_driver_install">설치</string> | 190 | <string name="select_gpu_driver_install">설치</string> |
| 173 | <string name="select_gpu_driver_default">기본값</string> | 191 | <string name="select_gpu_driver_default">기본값</string> |
| 174 | <string name="select_gpu_driver_use_default">기본 GPU 드ë¼ì´ë²„ 사용</string> | 192 | <string name="select_gpu_driver_use_default">기본 GPU 드ë¼ì´ë²„를 사용합니다.</string> |
| 193 | <string name="select_gpu_driver_error">ìž˜ëª»ëœ ë“œë¼ì´ë¸Œê°€ ì„ íƒë˜ì—ˆìŠµë‹ˆë‹¤. 시스템 ê¸°ë³¸ê°’ì„ ì‚¬ìš©í•©ë‹ˆë‹¤.</string> | ||
| 175 | <string name="system_gpu_driver">시스템 GPU 드ë¼ì´ë²„</string> | 194 | <string name="system_gpu_driver">시스템 GPU 드ë¼ì´ë²„</string> |
| 176 | <string name="installing_driver">드ë¼ì´ë²„ 설치 중...</string> | 195 | <string name="installing_driver">드ë¼ì´ë²„ 설치 중...</string> |
| 177 | 196 | ||
| @@ -182,51 +201,50 @@ | |||
| 182 | <string name="preferences_graphics">그래픽</string> | 201 | <string name="preferences_graphics">그래픽</string> |
| 183 | <string name="preferences_audio">오디오</string> | 202 | <string name="preferences_audio">오디오</string> |
| 184 | <string name="preferences_theme">테마 ë° ìƒ‰ìƒ</string> | 203 | <string name="preferences_theme">테마 ë° ìƒ‰ìƒ</string> |
| 204 | <string name="preferences_debug">디버그</string> | ||
| 185 | 205 | ||
| 186 | <!-- ROM loading errors --> | 206 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">ë¡¬ì´ ì•”í˜¸í™”ë˜ì—ˆìŒ</string> | 207 | <string name="loader_error_encrypted">롬 파ì¼ì´ 암호화ë˜ì–´ìžˆìŒ</string> |
| 188 | <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> | 208 | <string name="loader_error_encrypted_keys_description"><![CDATA[ê²Œìž„ì„ í•´ë…í• ìˆ˜ 있ë„ë¡ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 파ì¼ì´ 설치ë˜ì–´ 있는지 확ì¸í•˜ì„¸ìš”.]]></string> |
| 189 | <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> | ||
| 190 | <string name="loader_error_video_core">비디오 코어를 초기화하는 ë™ì•ˆ 오류 ë°œìƒ</string> | 209 | <string name="loader_error_video_core">비디오 코어를 초기화하는 ë™ì•ˆ 오류 ë°œìƒ</string> |
| 191 | <string name="loader_error_video_core_description">ì´ ë¬¸ì œëŠ” ì¼ë°˜ì 으로 호환ë˜ì§€ 않는 GPU 드ë¼ì´ë²„로 ì¸í•´ ë°œìƒí•©ë‹ˆë‹¤. ì‚¬ìš©ìž ì§€ì • GPU 드ë¼ì´ë²„를 설치하면 ì´ ë¬¸ì œê°€ í•´ê²°ë 수 있습니다.</string> | 210 | <string name="loader_error_video_core_description">ì¼ë°˜ì 으로 ì´ ë¬¸ì œëŠ” 호환ë˜ì§€ 않는 GPU 드ë¼ì´ë²„로 ì¸í•´ ë°œìƒí•©ë‹ˆë‹¤. ì‚¬ìš©ìž ì§€ì • GPU 드ë¼ì´ë²„를 설치하면 ì´ ë¬¸ì œê°€ í•´ê²°ë 수 있습니다.</string> |
| 192 | <string name="loader_error_invalid_format">ë¡¬ì„ ë¶ˆëŸ¬ì˜¬ 수 ì—†ìŒ</string> | 211 | <string name="loader_error_invalid_format">롬 파ì¼ì„ 불러올 수 ì—†ìŒ</string> |
| 193 | <string name="loader_error_file_not_found">롬 파ì¼ì´ 존재하지 않ìŒ</string> | 212 | <string name="loader_error_file_not_found">롬 파ì¼ì´ 존재하지 않ìŒ</string> |
| 194 | 213 | ||
| 195 | <!-- Emulation Menu --> | 214 | <!-- Emulation Menu --> |
| 196 | <string name="emulation_exit">ì—ë®¬ë ˆì´ì…˜ 종료</string> | 215 | <string name="emulation_exit">ì—ë®¬ë ˆì´ì…˜ 종료</string> |
| 197 | <string name="emulation_done">완료</string> | 216 | <string name="emulation_done">완료</string> |
| 198 | <string name="emulation_fps_counter">FPS 카운터</string> | 217 | <string name="emulation_fps_counter">FPS 표시</string> |
| 199 | <string name="emulation_toggle_controls">í† ê¸€ ì œì–´</string> | 218 | <string name="emulation_toggle_controls">컨트롤러 ì„ íƒ</string> |
| 200 | <string name="emulation_rel_stick_center">ìƒëŒ€ 스틱 센터</string> | 219 | <string name="emulation_rel_stick_center">ìŠ¤í‹±ì˜ ì¤‘ì‹¬ ì´ë™</string> |
| 201 | <string name="emulation_dpad_slide">ì‹ìžíŒ¨ë“œ 슬ë¼ì´ë“œ</string> | 220 | <string name="emulation_dpad_slide">ì‹ìží‚¤ 슬ë¼ì´ë“œ</string> |
| 202 | <string name="emulation_haptics">햅틱</string> | 221 | <string name="emulation_haptics">터치 햅틱</string> |
| 203 | <string name="emulation_show_overlay">ì˜¤ë²„ë ˆì´ í‘œì‹œ</string> | 222 | <string name="emulation_show_overlay">컨트롤러 표시</string> |
| 204 | <string name="emulation_toggle_all">ëª¨ë‘ í† ê¸€</string> | 223 | <string name="emulation_toggle_all">ëª¨ë‘ ì„ íƒ</string> |
| 205 | <string name="emulation_control_adjust">ì˜¤ë²„ë ˆì´ ì¡°ì •</string> | 224 | <string name="emulation_control_adjust">컨트롤러 ì¡°ì •</string> |
| 206 | <string name="emulation_control_scale">스케ì¼</string> | 225 | <string name="emulation_control_scale">í¬ê¸°</string> |
| 207 | <string name="emulation_control_opacity">불투명ë„</string> | 226 | <string name="emulation_control_opacity">불투명ë„</string> |
| 208 | <string name="emulation_touch_overlay_reset">ì˜¤ë²„ë ˆì´ ìž¬ì„¤ì •</string> | 227 | <string name="emulation_touch_overlay_reset">컨트롤러 ì„¤ì • 초기화</string> |
| 209 | <string name="emulation_touch_overlay_edit">ì˜¤ë²„ë ˆì´ íŽ¸ì§‘</string> | 228 | <string name="emulation_touch_overlay_edit">컨트롤러 위치 편집</string> |
| 210 | <string name="emulation_pause">ì—ë®¬ë ˆì´ì…˜ ì¼ì‹œ 중지</string> | 229 | <string name="emulation_pause">ì—ë®¬ë ˆì´ì…˜ ì¼ì‹œ 중지</string> |
| 211 | <string name="emulation_unpause">ì—ë®¬ë ˆì´ì…˜ ì¼ì‹œ 중지 í•´ì œ</string> | 230 | <string name="emulation_unpause">ì—ë®¬ë ˆì´ì…˜ ì¼ì‹œ 중지 í•´ì œ</string> |
| 212 | <string name="emulation_input_overlay">ì˜¤ë²„ë ˆì´ ì˜µì…˜</string> | 231 | <string name="emulation_input_overlay">화면 ì˜¤ë²„ë ˆì´ ì„¤ì •</string> |
| 213 | 232 | ||
| 214 | <string name="load_settings">ì„¤ì • 불러오기 중...</string> | 233 | <string name="load_settings">ì„¤ì • 불러오는 중...</string> |
| 215 | 234 | ||
| 216 | <!-- Software keyboard --> | 235 | <!-- Software keyboard --> |
| 217 | <string name="software_keyboard">ê°€ìƒ í‚¤ë³´ë“œ</string> | 236 | <string name="software_keyboard">소프트웨어 키보드</string> |
| 218 | 237 | ||
| 219 | <!-- Errors and warnings --> | 238 | <!-- Errors and warnings --> |
| 220 | <string name="abort_button">ì •ë³´</string> | 239 | <string name="abort_button">중단</string> |
| 221 | <string name="continue_button">계ì†</string> | 240 | <string name="continue_button">계ì†</string> |
| 222 | <string name="system_archive_not_found">시스템 ì•„ì¹´ì´ë¸Œë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ</string> | 241 | <string name="system_archive_not_found">시스템 ì•„ì¹´ì´ë¸Œë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ</string> |
| 223 | <string name="system_archive_not_found_message">%sê°€ 누ë½ë˜ì—ˆìŠµë‹ˆë‹¤. 시스템 ì•„ì¹´ì´ë¸Œë¥¼ ë¤í”„하세요.\nì—ë®¬ë ˆì´ì…˜ì„ 계ì†í•˜ë©´ ì¶©ëŒ ë° ë²„ê·¸ê°€ ë°œìƒí• 수 있습니다.</string> | 242 | <string name="system_archive_not_found_message">%sê°€ 누ë½ë˜ì—ˆìŠµë‹ˆë‹¤. 시스템 ì•„ì¹´ì´ë¸Œë¥¼ ë¤í”„하세요.\nì—ë®¬ë ˆì´ì…˜ì„ 계ì†í•˜ë©´ ì¶©ëŒ ë° ë²„ê·¸ê°€ ë°œìƒí• 수 있습니다.</string> |
| 224 | <string name="system_archive_general">시스템 ì•„ì¹´ì´ë¸Œ</string> | 243 | <string name="system_archive_general">시스템 ì•„ì¹´ì´ë¸Œ</string> |
| 225 | <string name="save_load_error">ì €ìž¥í•˜ê¸°/불러오기 오류</string> | 244 | <string name="save_load_error">ì €ìž¥í•˜ê¸°/불러오기 오류</string> |
| 226 | <string name="fatal_error">치명ì ì¸ ì˜¤ë¥˜</string> | 245 | <string name="fatal_error">치명ì 오류</string> |
| 227 | <string name="fatal_error_message">치명ì ì¸ ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. ìžì„¸í•œ ë‚´ìš©ì€ ë¡œê·¸ë¥¼ 확ì¸í•˜ì‹ì‹œì˜¤.\nì—ë®¬ë ˆì´ì…˜ì„ 계ì†í•˜ë©´ ì¶©ëŒ ë° ë²„ê·¸ê°€ ë°œìƒí• 수 있습니다.</string> | 246 | <string name="fatal_error_message">치명ì 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. ìžì„¸í•œ ë‚´ìš©ì€ ë¡œê·¸ë¥¼ 확ì¸í•˜ì‹ì‹œì˜¤.\nì—ë®¬ë ˆì´ì…˜ì„ 계ì†í•˜ë©´ ì¶©ëŒ ë° ë²„ê·¸ê°€ ë°œìƒí• 수 있습니다.</string> |
| 228 | <string name="performance_warning">ì´ ì„¤ì •ì„ ë„ë©´ ì—ë®¬ë ˆì´ì…˜ ì„±ëŠ¥ì´ í¬ê²Œ ì €í•˜ë©ë‹ˆë‹¤! 최ìƒì˜ í™˜ê²½ì„ ìœ„í•´ ì´ ì„¤ì •ì„ í™œì„±í™”ëœ ìƒíƒœë¡œ ë‘는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤.</string> | 247 | <string name="performance_warning">ì´ ì„¤ì •ì„ ë„ë©´ ì—ë®¬ë ˆì´ì…˜ ì„±ëŠ¥ì´ í¬ê²Œ ì €í•˜ë©ë‹ˆë‹¤! 최ìƒì˜ í™˜ê²½ì„ ìœ„í•´ ì´ ì„¤ì •ì„ í™œì„±í™”ëœ ìƒíƒœë¡œ ë‘는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤.</string> |
| 229 | |||
| 230 | <!-- Region Names --> | 248 | <!-- Region Names --> |
| 231 | <string name="region_japan">ì¼ë³¸</string> | 249 | <string name="region_japan">ì¼ë³¸</string> |
| 232 | <string name="region_usa">미êµ</string> | 250 | <string name="region_usa">미êµ</string> |
| @@ -234,12 +252,11 @@ | |||
| 234 | <string name="region_australia">호주</string> | 252 | <string name="region_australia">호주</string> |
| 235 | <string name="region_china">중êµ</string> | 253 | <string name="region_china">중êµ</string> |
| 236 | <string name="region_korea">대한민êµ</string> | 254 | <string name="region_korea">대한민êµ</string> |
| 237 | <string name="region_taiwan">타ì´ì™„</string> | 255 | <string name="region_taiwan">대만</string> |
| 238 | |||
| 239 | <!-- Language Names --> | ||
| 240 | 256 | ||
| 257 | <string name="memory_gigabyte">ì˜êµ 하계 표준시(GB)</string> | ||
| 241 | <!-- Renderer APIs --> | 258 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">불칸</string> | 259 | <string name="renderer_vulkan">Vulcan</string> |
| 243 | <string name="renderer_none">ì—†ìŒ</string> | 260 | <string name="renderer_none">ì—†ìŒ</string> |
| 244 | 261 | ||
| 245 | <!-- Renderer Accuracy --> | 262 | <!-- Renderer Accuracy --> |
| @@ -256,17 +273,17 @@ | |||
| 256 | <string name="resolution_four">4X (2880p/4320p) (ëŠë¦¼)</string> | 273 | <string name="resolution_four">4X (2880p/4320p) (ëŠë¦¼)</string> |
| 257 | 274 | ||
| 258 | <!-- Renderer VSync --> | 275 | <!-- Renderer VSync --> |
| 259 | <string name="renderer_vsync_immediate">즉시 (ë”)</string> | 276 | <string name="renderer_vsync_immediate">ì¦‰ê° í‘œì‹œ (ë„기)</string> |
| 260 | <string name="renderer_vsync_mailbox">ë©”ì¼ë°•스</string> | 277 | <string name="renderer_vsync_mailbox">ë©”ì¼ë°•스</string> |
| 261 | <string name="renderer_vsync_fifo">FIFO (켬)</string> | 278 | <string name="renderer_vsync_fifo">FIFO (켜기)</string> |
| 262 | <string name="renderer_vsync_fifo_relaxed">FIFO 릴랙스</string> | 279 | <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string> |
| 263 | 280 | ||
| 264 | <!-- Scaling Filters --> | 281 | <!-- Scaling Filters --> |
| 265 | <string name="scaling_filter_nearest_neighbor">가장 가까운 ì´ì›ƒ</string> | 282 | <string name="scaling_filter_nearest_neighbor">ìµœê·¼ì ‘ ë³´ê°„</string> |
| 266 | <string name="scaling_filter_bilinear">ì´ì¤‘ì„ í˜•</string> | 283 | <string name="scaling_filter_bilinear">ìŒì„ 형 ë³´ê°„</string> |
| 267 | <string name="scaling_filter_bicubic">ê³ ë“±ì°¨ìˆ˜ë³´ê°„</string> | 284 | <string name="scaling_filter_bicubic">ìŒìž…ë°© ë³´ê°„</string> |
| 268 | <string name="scaling_filter_gaussian">가우시안</string> | 285 | <string name="scaling_filter_gaussian">가우시안</string> |
| 269 | <string name="scaling_filter_scale_force">스케ì¼í¬ìФ</string> | 286 | <string name="scaling_filter_scale_force">ScaleForce</string> |
| 270 | <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ ì´ˆê³ í•´ìƒë„</string> | 287 | <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ ì´ˆê³ í•´ìƒë„</string> |
| 271 | 288 | ||
| 272 | <!-- Anti-Aliasing --> | 289 | <!-- Anti-Aliasing --> |
| @@ -274,27 +291,29 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">FXAA</string> | 291 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 275 | <string name="anti_aliasing_smaa">SMAA</string> | 292 | <string name="anti_aliasing_smaa">SMAA</string> |
| 276 | 293 | ||
| 294 | <string name="screen_layout_auto">ìžë™</string> | ||
| 295 | |||
| 277 | <!-- Aspect Ratios --> | 296 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">기본 (16:9)</string> | 297 | <string name="ratio_default">기본 (16:9)</string> |
| 279 | <string name="ratio_force_four_three">ê°•ì œ 4:3</string> | 298 | <string name="ratio_force_four_three">ê°•ì œ 4:3</string> |
| 280 | <string name="ratio_force_twenty_one_nine">ê°•ì œ 21:9</string> | 299 | <string name="ratio_force_twenty_one_nine">ê°•ì œ 21:9</string> |
| 281 | <string name="ratio_force_sixteen_ten">ê°•ì œ 16:10</string> | 300 | <string name="ratio_force_sixteen_ten">ê°•ì œ 16:10</string> |
| 282 | <string name="ratio_stretch">ì°½ì— ë§žê²Œ 늘림</string> | 301 | <string name="ratio_stretch">í™”ë©´ì— ë§žì¶¤</string> |
| 283 | 302 | ||
| 284 | <!-- CPU Accuracy --> | 303 | <!-- CPU Accuracy --> |
| 285 | <string name="cpu_accuracy_accurate">ì •í™•í•¨</string> | 304 | <string name="cpu_accuracy_accurate">ì •í™•í•¨</string> |
| 286 | <string name="cpu_accuracy_unsafe">ì•ˆì „í•˜ì§€ 않ìŒ</string> | 305 | <string name="cpu_accuracy_unsafe">최ì í™” (ì•ˆì „í•˜ì§€ 않ìŒ)</string> |
| 287 | <string name="cpu_accuracy_paranoid">íŽ¸ì§‘ì¦ (ëŠë¦¼)</string> | 306 | <string name="cpu_accuracy_paranoid">최ì 화하지 ì•ŠìŒ (ëŠë¦¼)</string> |
| 288 | 307 | ||
| 289 | <!-- Gamepad Buttons --> | 308 | <!-- Gamepad Buttons --> |
| 290 | <string name="gamepad_d_pad">ì‹ìžíŒ¨ë“œ</string> | 309 | <string name="gamepad_d_pad">ì‹ìží‚¤</string> |
| 291 | <string name="gamepad_left_stick">L 스틱</string> | 310 | <string name="gamepad_left_stick">L 스틱</string> |
| 292 | <string name="gamepad_right_stick">R 스틱</string> | 311 | <string name="gamepad_right_stick">R 스틱</string> |
| 293 | <string name="gamepad_home">홈</string> | 312 | <string name="gamepad_home">홈</string> |
| 294 | <string name="gamepad_screenshot">스í¬ë¦°ìƒ·</string> | 313 | <string name="gamepad_screenshot">스í¬ë¦°ìƒ·</string> |
| 295 | 314 | ||
| 296 | <!-- Disk shader cache --> | 315 | <!-- Disk shader cache --> |
| 297 | <string name="preparing_shaders">ì…°ì´ë” 준비하기</string> | 316 | <string name="preparing_shaders">ì…°ì´ë” 준비하는 중</string> |
| 298 | <string name="building_shaders">ì…°ì´ë” 빌드 중</string> | 317 | <string name="building_shaders">ì…°ì´ë” 빌드 중</string> |
| 299 | 318 | ||
| 300 | <!-- Theme options --> | 319 | <!-- Theme options --> |
| @@ -303,13 +322,19 @@ | |||
| 303 | <string name="theme_material_you">Material You</string> | 322 | <string name="theme_material_you">Material You</string> |
| 304 | 323 | ||
| 305 | <!-- Theme Modes --> | 324 | <!-- Theme Modes --> |
| 306 | <string name="change_theme_mode">테마 모드 변경</string> | 325 | <string name="change_theme_mode">ë‹¤í¬ ëª¨ë“œ ì„¤ì •</string> |
| 307 | <string name="theme_mode_follow_system">팔로우 시스템</string> | 326 | <string name="theme_mode_follow_system">시스템 값 사용</string> |
| 308 | <string name="theme_mode_light">ë°ìŒ</string> | 327 | <string name="theme_mode_light">ë¼ì´íЏ 모드</string> |
| 309 | <string name="theme_mode_dark">ì–´ë‘움</string> | 328 | <string name="theme_mode_dark">ë‹¤í¬ ëª¨ë“œ</string> |
| 310 | 329 | ||
| 311 | <!-- Black backgrounds theme --> | 330 | <!-- Black backgrounds theme --> |
| 312 | <string name="use_black_backgrounds">ê²€ì€ìƒ‰ ë°°ê²½ 사용</string> | 331 | <string name="use_black_backgrounds">ê²€ì • ë°°ê²½</string> |
| 313 | <string name="use_black_backgrounds_description">ì–´ë‘ìš´ 테마를 ì‚¬ìš©í• ë•ŒëŠ” ê²€ì€ìƒ‰ ë°°ê²½ì„ ì 용합니다.</string> | 332 | <string name="use_black_backgrounds_description">ì–´ë‘ìš´ 테마를 ì‚¬ìš©í• ë•ŒëŠ” ê²€ì • ë°°ê²½ì„ ì 용합니다.</string> |
| 333 | |||
| 334 | <string name="mute">ìŒì†Œê±°</string> | ||
| 335 | <string name="unmute">ìŒì†Œê±° í•´ì œ</string> | ||
| 314 | 336 | ||
| 315 | </resources> | 337 | <!-- Licenses screen strings --> |
| 338 | <string name="licenses">ë¼ì´ì„¼ìФ</string> | ||
| 339 | <string name="license_fidelityfx_fsr_description">AMDì˜ ê³ í’ˆì§ˆ 업스케ì¼ë§</string> | ||
| 340 | </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 index 5443cef42..3162a9d41 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.<br /><br />Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string> | 4 | <string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.<br /><br />Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emulering er aktiv</string> | 5 | <string name="emulation_notification_channel_name">Emulering er aktiv</string> |
| @@ -25,7 +25,6 @@ | |||
| 25 | <string name="back">Tilbake</string> | 25 | <string name="back">Tilbake</string> |
| 26 | <string name="add_games">Legg til spill</string> | 26 | <string name="add_games">Legg til spill</string> |
| 27 | <string name="add_games_description">Velg din spillmappe</string> | 27 | <string name="add_games_description">Velg din spillmappe</string> |
| 28 | |||
| 29 | <!-- Home strings --> | 28 | <!-- Home strings --> |
| 30 | <string name="home_games">Spill</string> | 29 | <string name="home_games">Spill</string> |
| 31 | <string name="home_search">Søk</string> | 30 | <string name="home_search">Søk</string> |
| @@ -37,7 +36,7 @@ | |||
| 37 | <string name="add_games_warning">Hoppe over valg av spillmappe?</string> | 36 | <string name="add_games_warning">Hoppe over valg av spillmappe?</string> |
| 38 | <string name="add_games_warning_description">Spill vises ikke i Spill-listen hvis en mappe ikke er valgt.</string> | 37 | <string name="add_games_warning_description">Spill vises ikke i Spill-listen hvis en mappe ikke er valgt.</string> |
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 38 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 40 | <string name="home_search_games">Søk i spill</string> | 39 | <string name="home_search_games">Søk i spill|</string> |
| 41 | <string name="games_dir_selected">Spillkatalogen er valgt</string> | 40 | <string name="games_dir_selected">Spillkatalogen er valgt</string> |
| 42 | <string name="install_prod_keys">Installer prod.keys</string> | 41 | <string name="install_prod_keys">Installer prod.keys</string> |
| 43 | <string name="install_prod_keys_description">Nødvendig for å dekryptere spill</string> | 42 | <string name="install_prod_keys_description">Nødvendig for å dekryptere spill</string> |
| @@ -61,6 +60,8 @@ | |||
| 61 | <string name="invalid_keys_file">Ugyldig nøkkelfil valgt</string> | 60 | <string name="invalid_keys_file">Ugyldig nøkkelfil valgt</string> |
| 62 | <string name="install_keys_success">Nøkler vellykket installert</string> | 61 | <string name="install_keys_success">Nøkler vellykket installert</string> |
| 63 | <string name="reading_keys_failure">Feil ved lesing av krypteringsnøkler</string> | 62 | <string name="reading_keys_failure">Feil ved lesing av krypteringsnøkler</string> |
| 63 | <string name="install_prod_keys_failure_extension_description">Kontroller at nøkkelfilen har filtypen .keys, og prøv igjen.</string> | ||
| 64 | <string name="install_amiibo_keys_failure_extension_description">Kontroller at nøkkelfilen har filtypen .bin, og prøv igjen.</string> | ||
| 64 | <string name="invalid_keys_error">Ugyldige krypteringsnøkler</string> | 65 | <string name="invalid_keys_error">Ugyldige krypteringsnøkler</string> |
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 66 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">Den valgte filen er feil eller ødelagt. Vennligst dump nøklene på nytt.</string> | 67 | <string name="install_keys_failure_description">Den valgte filen er feil eller ødelagt. Vennligst dump nøklene på nytt.</string> |
| @@ -86,7 +87,17 @@ | |||
| 86 | <string name="save_file_invalid_zip_structure_description">Det første undermappenavnet må være spillets tittel-ID.</string> | 87 | <string name="save_file_invalid_zip_structure_description">Det første undermappenavnet må være spillets tittel-ID.</string> |
| 87 | <string name="import_saves">Importer</string> | 88 | <string name="import_saves">Importer</string> |
| 88 | <string name="export_saves">Eksporter</string> | 89 | <string name="export_saves">Eksporter</string> |
| 89 | 90 | <string name="install_firmware">Installer fastvare</string> | |
| 91 | <string name="install_firmware_description">Fastvaren må være i et ZIP-arkiv og er nødvendig for å starte noen spill.</string> | ||
| 92 | <string name="firmware_installing">Installering av fastvare</string> | ||
| 93 | <string name="firmware_installed_success">Fastvaren er vellykket installert</string> | ||
| 94 | <string name="firmware_installed_failure">Installasjon av fastvare mislyktes</string> | ||
| 95 | <string name="share_log">Del feilsøkingslogger</string> | ||
| 96 | <string name="share_log_description">Del yuzus loggfil for å feilsøke problemer</string> | ||
| 97 | <string name="share_log_missing">Ingen loggfil funnet</string> | ||
| 98 | <string name="install_game_content">Installer spillinnhold</string> | ||
| 99 | <string name="install_game_content_description">Installer spilloppdateringer eller DLC</string> | ||
| 100 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 90 | <!-- About screen strings --> | 101 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">Gaia er ikke ekte</string> | 102 | <string name="gaia_is_not_real">Gaia er ikke ekte</string> |
| 92 | <string name="copied_to_clipboard">Kopiert til utklippstavlen</string> | 103 | <string name="copied_to_clipboard">Kopiert til utklippstavlen</string> |
| @@ -94,6 +105,7 @@ | |||
| 94 | <string name="contributors">Bidragsytere</string> | 105 | <string name="contributors">Bidragsytere</string> |
| 95 | <string name="contributors_description">Laget med \u2764 fra yuzu-teamet</string> | 106 | <string name="contributors_description">Laget med \u2764 fra yuzu-teamet</string> |
| 96 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 107 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 108 | <string name="licenses_description">Prosjekter som gjør yuzu for Android mulig</string> | ||
| 97 | <string name="build">Bygg</string> | 109 | <string name="build">Bygg</string> |
| 98 | <string name="support_link">https://discord.gg/u77vRWY</string> | 110 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 99 | <string name="website_link">https://yuzu-emu.org/</string> | 111 | <string name="website_link">https://yuzu-emu.org/</string> |
| @@ -114,41 +126,43 @@ | |||
| 114 | <string name="are_you_interested">Er du interessert?</string> | 126 | <string name="are_you_interested">Er du interessert?</string> |
| 115 | 127 | ||
| 116 | <!-- General settings strings --> | 128 | <!-- General settings strings --> |
| 117 | <string name="frame_limit_enable">Aktiver hastighetsbegrensning</string> | 129 | <string name="frame_limit_enable">Begrense hastigheten</string> |
| 118 | <string name="frame_limit_enable_description">NÃ¥r aktivert, begrenses emuleringshastigheten til en angitt prosentandel av normal hastighet.</string> | 130 | <string name="frame_limit_enable_description">Begrenser emuleringshastigheten til en spesifisert prosentandel av normal hastighet.</string> |
| 119 | <string name="frame_limit_slider">Hastighetsbegrensning i prosent</string> | 131 | <string name="frame_limit_slider">Hastighetsbegrensning i prosent</string> |
| 120 | <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> | 132 | <string name="frame_limit_slider_description">Angir prosentandelen som skal begrense emuleringshastigheten. 100 % er normal hastighet. Høyere eller lavere verdier vil øke eller redusere hastighetsgrensen.</string> |
| 121 | <string name="cpu_accuracy">CPU-nøyaktighet</string> | 133 | <string name="cpu_accuracy">CPU-nøyaktighet</string> |
| 122 | |||
| 123 | <!-- System settings strings --> | 134 | <!-- System settings strings --> |
| 124 | <string name="use_docked_mode">Dokket modus</string> | 135 | <string name="use_docked_mode">Dokket modus</string> |
| 125 | <string name="use_docked_mode_description">Emulerer i dokket modus, noe som øker oppløsningen på bekostning av ytelsen.</string> | 136 | <string name="use_docked_mode_description">Øker oppløsningen, men reduserer ytelsen. Håndholdt modus brukes når den er deaktivert, noe som reduserer oppløsningen og øker ytelsen.</string> |
| 126 | <string name="emulated_region">Emulert region</string> | 137 | <string name="emulated_region">Emulert region</string> |
| 127 | <string name="emulated_language">Emulert språk</string> | 138 | <string name="emulated_language">Emulert språk</string> |
| 128 | <string name="select_rtc_date">Velg RTC-dato</string> | 139 | <string name="select_rtc_date">Velg RTC-dato</string> |
| 129 | <string name="select_rtc_time">Velg RTC-tid</string> | 140 | <string name="select_rtc_time">Velg RTC-tid</string> |
| 130 | <string name="use_custom_rtc">Aktiver egendefinert RTC</string> | 141 | <string name="use_custom_rtc">Tilpasset Sannhetstidsklokke</string> |
| 131 | <string name="use_custom_rtc_description">Med denne innstillingen kan du stille inn en egendefinert sanntidsklokke som er atskilt fra gjeldende systemtid.</string> | 142 | <string name="use_custom_rtc_description">Gjør det mulig å stille inn en egendefinert sanntidsklokke separat fra den gjeldende systemtiden.</string> |
| 132 | <string name="set_custom_rtc">Angi egendefinert RTC</string> | 143 | <string name="set_custom_rtc">Angi tilpasset RTC</string> |
| 133 | 144 | ||
| 134 | <!-- Graphics settings strings --> | 145 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">API</string> | ||
| 136 | <string name="renderer_accuracy">Nøyaktighetsnivå</string> | 146 | <string name="renderer_accuracy">Nøyaktighetsnivå</string> |
| 137 | <string name="renderer_resolution">Oppløsning</string> | 147 | <string name="renderer_resolution">Oppløsning (håndholdt/dokket)</string> |
| 138 | <string name="renderer_vsync">VSync-modus</string> | 148 | <string name="renderer_vsync">VSync-modus</string> |
| 139 | <string name="renderer_aspect_ratio">Størrelsesforhold</string> | 149 | <string name="renderer_aspect_ratio">Størrelsesforhold</string> |
| 140 | <string name="renderer_scaling_filter">Filter for vindustilpasning</string> | 150 | <string name="renderer_scaling_filter">Filter for vindustilpasning</string> |
| 141 | <string name="renderer_anti_aliasing">Anti-Aliasing-metode</string> | 151 | <string name="renderer_anti_aliasing">Anti-aliasing-metode</string> |
| 142 | <string name="renderer_force_max_clock">Tving fram maksimal klokkefrekvens (kun Adreno)</string> | 152 | <string name="renderer_force_max_clock">Tving fram maksimal klokkefrekvens (kun Adreno)</string> |
| 143 | <string name="renderer_force_max_clock_description">Tvinger GPU-en til å kjøre med maksimal klokkefrekvens (termiske begrensninger vil fortsatt gjelde).</string> | 153 | <string name="renderer_force_max_clock_description">Tvinger GPU-en til å kjøre med maksimal klokkefrekvens (termiske begrensninger vil fortsatt gjelde).</string> |
| 144 | <string name="renderer_asynchronous_shaders">Bruk asynkrone shaders</string> | 154 | <string name="renderer_asynchronous_shaders">Bruk asynkrone shaders</string> |
| 145 | <string name="renderer_asynchronous_shaders_description">Kompilerer shaders asynkront, noe som reduserer hakkingen, men kan føre til feil.</string> | 155 | <string name="renderer_asynchronous_shaders_description">Kompilerer shaders asynkront, noe som reduserer hakking, men kan føre til feil.</string> |
| 146 | <string name="renderer_debug">Aktiver feilsøking av grafikk</string> | 156 | <string name="renderer_reactive_flushing">Bruk reaktiv spyling</string> |
| 147 | <string name="renderer_debug_description">Når dette er merket av, går grafikk-API-et inn i en langsommere feilsøkingsmodus.</string> | 157 | <string name="renderer_reactive_flushing_description">Forbedrer gjengivelsesnøyaktigheten i enkelte spill på bekostning av ytelsen.</string> |
| 148 | <string name="use_disk_shader_cache">Bruk disk shader-cache</string> | 158 | <string name="use_disk_shader_cache">Disk shader-hurtigbuffer</string> |
| 149 | <string name="use_disk_shader_cache_description">Reduser hakking ved å lagre og laste inn genererte shaders på disken.</string> | 159 | <string name="use_disk_shader_cache_description">Reduserer hakking ved å lagre og laste inn genererte shaders lokalt.</string> |
| 150 | 160 | ||
| 151 | <!-- Audio settings strings --> | 161 | <!-- Debug settings strings --> |
| 162 | <string name="cpu">CPU</string> | ||
| 163 | <string name="renderer_api">API</string> | ||
| 164 | <string name="renderer_debug">Feilsøking av grafikk</string> | ||
| 165 | <string name="renderer_debug_description">Setter grafikk-API-et til en langsom feilsøkingsmodus.</string> | ||
| 152 | <string name="audio_volume">Volum</string> | 166 | <string name="audio_volume">Volum</string> |
| 153 | <string name="audio_volume_description">Angir volumet på lydutgangen.</string> | 167 | <string name="audio_volume_description">Angir volumet på lydutgangen.</string> |
| 154 | 168 | ||
| @@ -164,14 +178,19 @@ | |||
| 164 | <string name="reset_all_settings_description">Alle avanserte innstillinger tilbakestilles til standardkonfigurasjonen. Dette kan ikke angres.</string> | 178 | <string name="reset_all_settings_description">Alle avanserte innstillinger tilbakestilles til standardkonfigurasjonen. Dette kan ikke angres.</string> |
| 165 | <string name="settings_reset">Tilbakestilling av innstillinger</string> | 179 | <string name="settings_reset">Tilbakestilling av innstillinger</string> |
| 166 | <string name="close">Lukk</string> | 180 | <string name="close">Lukk</string> |
| 167 | <string name="learn_more">Lær Mer</string> | 181 | <string name="learn_more">Lær mer</string> |
| 168 | 182 | <string name="auto">Auto</string> | |
| 183 | <string name="submit">Send inn</string> | ||
| 184 | <string name="string_null">Null</string> | ||
| 185 | <string name="string_import">Importer</string> | ||
| 186 | <string name="export">Eksporter</string> | ||
| 169 | <!-- GPU driver installation --> | 187 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">Velg GPU-driver</string> | 188 | <string name="select_gpu_driver">Velg GPU-driver</string> |
| 171 | <string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string> | 189 | <string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string> |
| 172 | <string name="select_gpu_driver_install">Installer</string> | 190 | <string name="select_gpu_driver_install">Installer</string> |
| 173 | <string name="select_gpu_driver_default">Standard</string> | 191 | <string name="select_gpu_driver_default">Standard</string> |
| 174 | <string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string> | 192 | <string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string> |
| 193 | <string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string> | ||
| 175 | <string name="system_gpu_driver">Systemets GPU-driver</string> | 194 | <string name="system_gpu_driver">Systemets GPU-driver</string> |
| 176 | <string name="installing_driver">Installerer driver...</string> | 195 | <string name="installing_driver">Installerer driver...</string> |
| 177 | 196 | ||
| @@ -182,10 +201,10 @@ | |||
| 182 | <string name="preferences_graphics">Grafikk</string> | 201 | <string name="preferences_graphics">Grafikk</string> |
| 183 | <string name="preferences_audio">Lyd</string> | 202 | <string name="preferences_audio">Lyd</string> |
| 184 | <string name="preferences_theme">Tema og farge</string> | 203 | <string name="preferences_theme">Tema og farge</string> |
| 204 | <string name="preferences_debug">Feilsøk</string> | ||
| 185 | 205 | ||
| 186 | <!-- ROM loading errors --> | 206 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">ROM-en din er kryptert</string> | 207 | <string name="loader_error_encrypted">ROM-en din er kryptert</string> |
| 188 | <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> | ||
| 189 | <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> | 208 | <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> |
| 190 | <string name="loader_error_video_core">Det oppstod en feil ved initialisering av videokjernen</string> | 209 | <string name="loader_error_video_core">Det oppstod en feil ved initialisering av videokjernen</string> |
| 191 | <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> | 210 | <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> |
| @@ -196,25 +215,25 @@ | |||
| 196 | <string name="emulation_exit">Avslutt emulering</string> | 215 | <string name="emulation_exit">Avslutt emulering</string> |
| 197 | <string name="emulation_done">Ferdig</string> | 216 | <string name="emulation_done">Ferdig</string> |
| 198 | <string name="emulation_fps_counter">FPS-teller</string> | 217 | <string name="emulation_fps_counter">FPS-teller</string> |
| 199 | <string name="emulation_toggle_controls">Veksle kontroller</string> | 218 | <string name="emulation_toggle_controls">Veksle mellom kontrollene</string> |
| 200 | <string name="emulation_rel_stick_center">Relativt senter for stikken</string> | 219 | <string name="emulation_rel_stick_center">Relativt pinnesenter</string> |
| 201 | <string name="emulation_dpad_slide">DPad-skyveplate</string> | 220 | <string name="emulation_dpad_slide">D-pad-skyving</string> |
| 202 | <string name="emulation_haptics">Haptikk</string> | 221 | <string name="emulation_haptics">Berøringshaptikk</string> |
| 203 | <string name="emulation_show_overlay">Vis overlegg</string> | 222 | <string name="emulation_show_overlay">Vis overlegg</string> |
| 204 | <string name="emulation_toggle_all">Slå av alt</string> | 223 | <string name="emulation_toggle_all">Veksle mellom alle</string> |
| 205 | <string name="emulation_control_adjust">Juster overlegg</string> | 224 | <string name="emulation_control_adjust">Juster overlegg</string> |
| 206 | <string name="emulation_control_scale">Skaler</string> | 225 | <string name="emulation_control_scale">Skaler</string> |
| 207 | <string name="emulation_control_opacity">Gjennomsiktighet</string> | 226 | <string name="emulation_control_opacity">Gjennomsiktighet</string> |
| 208 | <string name="emulation_touch_overlay_reset">Tilbakestill overlegg</string> | 227 | <string name="emulation_touch_overlay_reset">Tilbakestill overlegg</string> |
| 209 | <string name="emulation_touch_overlay_edit">Rediger overlegg</string> | 228 | <string name="emulation_touch_overlay_edit">Rediger overlegg</string> |
| 210 | <string name="emulation_pause">Pause Emulering</string> | 229 | <string name="emulation_pause">Pause emulering</string> |
| 211 | <string name="emulation_unpause">Opphev pausing av emulering</string> | 230 | <string name="emulation_unpause">Ta emuleringen ut av pause</string> |
| 212 | <string name="emulation_input_overlay">Alternativer for overlegg</string> | 231 | <string name="emulation_input_overlay">Overlay-alternativer</string> |
| 213 | 232 | ||
| 214 | <string name="load_settings">Laster inn innstillinger...</string> | 233 | <string name="load_settings">Laster inn innstillinger...</string> |
| 215 | 234 | ||
| 216 | <!-- Software keyboard --> | 235 | <!-- Software keyboard --> |
| 217 | <string name="software_keyboard">Programvare Tastatur</string> | 236 | <string name="software_keyboard">Programvaretastatur</string> |
| 218 | 237 | ||
| 219 | <!-- Errors and warnings --> | 238 | <!-- Errors and warnings --> |
| 220 | <string name="abort_button">Avbryt</string> | 239 | <string name="abort_button">Avbryt</string> |
| @@ -226,7 +245,6 @@ | |||
| 226 | <string name="fatal_error">Fatal Feil</string> | 245 | <string name="fatal_error">Fatal Feil</string> |
| 227 | <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> | 246 | <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> |
| 228 | <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> | 247 | <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> |
| 229 | |||
| 230 | <!-- Region Names --> | 248 | <!-- Region Names --> |
| 231 | <string name="region_japan">Japan</string> | 249 | <string name="region_japan">Japan</string> |
| 232 | <string name="region_usa">USA</string> | 250 | <string name="region_usa">USA</string> |
| @@ -236,8 +254,7 @@ | |||
| 236 | <string name="region_korea">Korea</string> | 254 | <string name="region_korea">Korea</string> |
| 237 | <string name="region_taiwan">Taiwan</string> | 255 | <string name="region_taiwan">Taiwan</string> |
| 238 | 256 | ||
| 239 | <!-- Language Names --> | 257 | <string name="memory_gigabyte">GB</string> |
| 240 | |||
| 241 | <!-- Renderer APIs --> | 258 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">Vulkan</string> | 259 | <string name="renderer_vulkan">Vulkan</string> |
| 243 | <string name="renderer_none">Ingen</string> | 260 | <string name="renderer_none">Ingen</string> |
| @@ -274,12 +291,14 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">FXAA</string> | 291 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 275 | <string name="anti_aliasing_smaa">SMAA</string> | 292 | <string name="anti_aliasing_smaa">SMAA</string> |
| 276 | 293 | ||
| 294 | <string name="screen_layout_auto">Auto</string> | ||
| 295 | |||
| 277 | <!-- Aspect Ratios --> | 296 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">Standard (16:9)</string> | 297 | <string name="ratio_default">Standard (16:9)</string> |
| 279 | <string name="ratio_force_four_three">Tving 4:3</string> | 298 | <string name="ratio_force_four_three">Tving 4:3</string> |
| 280 | <string name="ratio_force_twenty_one_nine">Tving 21:9</string> | 299 | <string name="ratio_force_twenty_one_nine">Tving 21:9</string> |
| 281 | <string name="ratio_force_sixteen_ten">Tving 16:10</string> | 300 | <string name="ratio_force_sixteen_ten">Tving 16:10</string> |
| 282 | <string name="ratio_stretch">Strekk til Vindu</string> | 301 | <string name="ratio_stretch">Strekk til vindu</string> |
| 283 | 302 | ||
| 284 | <!-- CPU Accuracy --> | 303 | <!-- CPU Accuracy --> |
| 285 | <string name="cpu_accuracy_accurate">Nøyaktig</string> | 304 | <string name="cpu_accuracy_accurate">Nøyaktig</string> |
| @@ -287,9 +306,9 @@ | |||
| 287 | <string name="cpu_accuracy_paranoid">Paranoid (Langsom)</string> | 306 | <string name="cpu_accuracy_paranoid">Paranoid (Langsom)</string> |
| 288 | 307 | ||
| 289 | <!-- Gamepad Buttons --> | 308 | <!-- Gamepad Buttons --> |
| 290 | <string name="gamepad_d_pad">D-Pad</string> | 309 | <string name="gamepad_d_pad">D-pad</string> |
| 291 | <string name="gamepad_left_stick">Venstre Pinne</string> | 310 | <string name="gamepad_left_stick">Venstre spak</string> |
| 292 | <string name="gamepad_right_stick">Høyre Pinne</string> | 311 | <string name="gamepad_right_stick">Høyre spak</string> |
| 293 | <string name="gamepad_home">Hjem</string> | 312 | <string name="gamepad_home">Hjem</string> |
| 294 | <string name="gamepad_screenshot">Skjermbilde</string> | 313 | <string name="gamepad_screenshot">Skjermbilde</string> |
| 295 | 314 | ||
| @@ -298,7 +317,7 @@ | |||
| 298 | <string name="building_shaders">Bygging av shaders</string> | 317 | <string name="building_shaders">Bygging av shaders</string> |
| 299 | 318 | ||
| 300 | <!-- Theme options --> | 319 | <!-- Theme options --> |
| 301 | <string name="change_app_theme">Endre appens tema</string> | 320 | <string name="change_app_theme">Endre app-tema</string> |
| 302 | <string name="theme_default">Standard</string> | 321 | <string name="theme_default">Standard</string> |
| 303 | <string name="theme_material_you">Material You</string> | 322 | <string name="theme_material_you">Material You</string> |
| 304 | 323 | ||
| @@ -309,7 +328,13 @@ | |||
| 309 | <string name="theme_mode_dark">Mørk</string> | 328 | <string name="theme_mode_dark">Mørk</string> |
| 310 | 329 | ||
| 311 | <!-- Black backgrounds theme --> | 330 | <!-- Black backgrounds theme --> |
| 312 | <string name="use_black_backgrounds">Bruk svart bakgrunn</string> | 331 | <string name="use_black_backgrounds">Svart bakgrunn</string> |
| 313 | <string name="use_black_backgrounds_description">Bruk svart bakgrunn når du bruker det mørke temaet.</string> | 332 | <string name="use_black_backgrounds_description">Bruk svart bakgrunn når du bruker det mørke temaet.</string> |
| 314 | 333 | ||
| 315 | </resources> | 334 | <string name="mute">Lydløs</string> |
| 335 | <string name="unmute">Slå på lyden</string> | ||
| 336 | |||
| 337 | <!-- Licenses screen strings --> | ||
| 338 | <string name="licenses">Lisenser</string> | ||
| 339 | <string name="license_fidelityfx_fsr_description">Oppskalering av høy kvalitet fra AMD</string> | ||
| 340 | </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 index 899e233d0..f4d9920c2 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.<br /><br />Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string> | 4 | <string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.<br /><br />Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emulacja jest uruchomiona</string> | 5 | <string name="emulation_notification_channel_name">Emulacja jest uruchomiona</string> |
| @@ -25,7 +25,6 @@ | |||
| 25 | <string name="back">Wstecz</string> | 25 | <string name="back">Wstecz</string> |
| 26 | <string name="add_games">Dodaj gry</string> | 26 | <string name="add_games">Dodaj gry</string> |
| 27 | <string name="add_games_description">Wybierz folder zawierajÄ…cy Twoje gry</string> | 27 | <string name="add_games_description">Wybierz folder zawierajÄ…cy Twoje gry</string> |
| 28 | |||
| 29 | <!-- Home strings --> | 28 | <!-- Home strings --> |
| 30 | <string name="home_games">Gry</string> | 29 | <string name="home_games">Gry</string> |
| 31 | <string name="home_search">Szukaj</string> | 30 | <string name="home_search">Szukaj</string> |
| @@ -61,6 +60,8 @@ | |||
| 61 | <string name="invalid_keys_file">Wybrano niepoprawne klucze</string> | 60 | <string name="invalid_keys_file">Wybrano niepoprawne klucze</string> |
| 62 | <string name="install_keys_success">Klucze zainstalowane pomyślnie</string> | 61 | <string name="install_keys_success">Klucze zainstalowane pomyślnie</string> |
| 63 | <string name="reading_keys_failure">Błąd podczas odczytu kluczy</string> | 62 | <string name="reading_keys_failure">Błąd podczas odczytu kluczy</string> |
| 63 | <string name="install_prod_keys_failure_extension_description">Upewnij się że twoje klucze mają rozszerzenie .keys i spróbuj ponownie.</string> | ||
| 64 | <string name="install_amiibo_keys_failure_extension_description">Upewnij się że twoje klucze mają rozszerzenie .bin i spróbuj ponownie.</string> | ||
| 64 | <string name="invalid_keys_error">Niepoprawne klucze</string> | 65 | <string name="invalid_keys_error">Niepoprawne klucze</string> |
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 66 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">Wybrany plik jest niepoprawny lub uszkodzony. Zrzuć ponownie swoje klucze.</string> | 67 | <string name="install_keys_failure_description">Wybrany plik jest niepoprawny lub uszkodzony. Zrzuć ponownie swoje klucze.</string> |
| @@ -86,7 +87,17 @@ | |||
| 86 | <string name="save_file_invalid_zip_structure_description">Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry.</string> | 87 | <string name="save_file_invalid_zip_structure_description">Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry.</string> |
| 87 | <string name="import_saves">Importuj</string> | 88 | <string name="import_saves">Importuj</string> |
| 88 | <string name="export_saves">Eksportuj</string> | 89 | <string name="export_saves">Eksportuj</string> |
| 89 | 90 | <string name="install_firmware">Zainstaluj firmware</string> | |
| 91 | <string name="install_firmware_description">Firmware musi być w postaci archiwum ZIP, niektóre gry wymagają go do uruchomienia/prawidłowego działania</string> | ||
| 92 | <string name="firmware_installing">InstalujÄ™ firmware</string> | ||
| 93 | <string name="firmware_installed_success">Zainstalowano pomyślnie</string> | ||
| 94 | <string name="firmware_installed_failure">Błąd podczas instalacji firmware</string> | ||
| 95 | <string name="share_log">Udostępnij logi debugowania</string> | ||
| 96 | <string name="share_log_description">Podziel się logami yuzu, pomoże to twórcom w poprawie działania emulatora</string> | ||
| 97 | <string name="share_log_missing">Nie znaleziono plików logów</string> | ||
| 98 | <string name="install_game_content">Zainstaluj zawartość gry</string> | ||
| 99 | <string name="install_game_content_description">Zainstaluj aktualizacjÄ™ gry lub dodatek DLC</string> | ||
| 100 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 90 | <!-- About screen strings --> | 101 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">Gaia isn\'t real</string> | 102 | <string name="gaia_is_not_real">Gaia isn\'t real</string> |
| 92 | <string name="copied_to_clipboard">Skopiowano do schowka</string> | 103 | <string name="copied_to_clipboard">Skopiowano do schowka</string> |
| @@ -94,6 +105,7 @@ | |||
| 94 | <string name="contributors">Współtwórcy</string> | 105 | <string name="contributors">Współtwórcy</string> |
| 95 | <string name="contributors_description">Stworzone z \u2764 przez zespół yuzu</string> | 106 | <string name="contributors_description">Stworzone z \u2764 przez zespół yuzu</string> |
| 96 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 107 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 108 | <string name="licenses_description">Projekty dzięki którym yuzu mógł zostać stworzony</string> | ||
| 97 | <string name="build">Wersja</string> | 109 | <string name="build">Wersja</string> |
| 98 | <string name="support_link">https://discord.gg/u77vRWY</string> | 110 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 99 | <string name="website_link">https://yuzu-emu.org/</string> | 111 | <string name="website_link">https://yuzu-emu.org/</string> |
| @@ -114,27 +126,25 @@ | |||
| 114 | <string name="are_you_interested">JesteÅ› zainteresowany?</string> | 126 | <string name="are_you_interested">JesteÅ› zainteresowany?</string> |
| 115 | 127 | ||
| 116 | <!-- General settings strings --> | 128 | <!-- General settings strings --> |
| 117 | <string name="frame_limit_enable">Włącz limit szybkości emulacji</string> | 129 | <string name="frame_limit_enable">Limit szybkość</string> |
| 118 | <string name="frame_limit_enable_description">Włącz, aby ustawić procentowy limit szybkości emulacji</string> | 130 | <string name="frame_limit_enable_description">Włącz, aby ustawić procentowy limit szybkości emulacji</string> |
| 119 | <string name="frame_limit_slider">Procentowy limit szybkości emulacji</string> | 131 | <string name="frame_limit_slider">Procentowy limit szybkości emulacji</string> |
| 120 | <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> | 132 | <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> |
| 121 | <string name="cpu_accuracy">Dokładność procesora CPU</string> | 133 | <string name="cpu_accuracy">Dokładność procesora CPU</string> |
| 122 | |||
| 123 | <!-- System settings strings --> | 134 | <!-- System settings strings --> |
| 124 | <string name="use_docked_mode">Tryb zadokowany</string> | 135 | <string name="use_docked_mode">Tryb zadokowany</string> |
| 125 | <string name="use_docked_mode_description">Emulacja w trybie stacji dokującej, zwiększa rozdzielczość kosztem wydajności.</string> | 136 | <string name="use_docked_mode_description">Zwiększa rozdzielczość kosztem wydajności. Kiedy wyłączone, używany jest tryb Handheld, który obniża rozdzielczość i dzięki temu zwiększa wydajność.</string> |
| 126 | <string name="emulated_region">Region emulacji</string> | 137 | <string name="emulated_region">Region emulacji</string> |
| 127 | <string name="emulated_language">Język emulacji</string> | 138 | <string name="emulated_language">Język emulacji</string> |
| 128 | <string name="select_rtc_date">Ustaw datÄ™ RTC</string> | 139 | <string name="select_rtc_date">Ustaw datÄ™ RTC</string> |
| 129 | <string name="select_rtc_time">Ustaw czas RTC</string> | 140 | <string name="select_rtc_time">Ustaw czas RTC</string> |
| 130 | <string name="use_custom_rtc">Włącz niestandardowy zegar RTC</string> | 141 | <string name="use_custom_rtc">Niestandardowy RTC</string> |
| 131 | <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> | 142 | <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> |
| 132 | <string name="set_custom_rtc">Ustaw niestandardowy czas RTC</string> | 143 | <string name="set_custom_rtc">Ustaw niestandardowy czas RTC</string> |
| 133 | 144 | ||
| 134 | <!-- Graphics settings strings --> | 145 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">Interfejs graficzny</string> | ||
| 136 | <string name="renderer_accuracy">Poziom precyzji emulacji</string> | 146 | <string name="renderer_accuracy">Poziom precyzji emulacji</string> |
| 137 | <string name="renderer_resolution">Rozdzielczość</string> | 147 | <string name="renderer_resolution">Rozdzielczość (Handheld/Zadokowany)</string> |
| 138 | <string name="renderer_vsync">Synchronizacja pionowa VSync</string> | 148 | <string name="renderer_vsync">Synchronizacja pionowa VSync</string> |
| 139 | <string name="renderer_aspect_ratio">Proporcje ekranu</string> | 149 | <string name="renderer_aspect_ratio">Proporcje ekranu</string> |
| 140 | <string name="renderer_scaling_filter">Filtr adaptacji rozdzielczości</string> | 150 | <string name="renderer_scaling_filter">Filtr adaptacji rozdzielczości</string> |
| @@ -143,12 +153,16 @@ | |||
| 143 | <string name="renderer_force_max_clock_description">Wymusza uruchomienie maksymalnego taktowania układu graficznego (zabezpieczenia termiczne będą dalej aktywne).</string> | 153 | <string name="renderer_force_max_clock_description">Wymusza uruchomienie maksymalnego taktowania układu graficznego (zabezpieczenia termiczne będą dalej aktywne).</string> |
| 144 | <string name="renderer_asynchronous_shaders">Wyłącz synchronizację shaderów</string> | 154 | <string name="renderer_asynchronous_shaders">Wyłącz synchronizację shaderów</string> |
| 145 | <string name="renderer_asynchronous_shaders_description">Kompiluj oświetlenie bez synchronizacji, poprawi wydajność ale może powodować błędy.</string> | 155 | <string name="renderer_asynchronous_shaders_description">Kompiluj oświetlenie bez synchronizacji, poprawi wydajność ale może powodować błędy.</string> |
| 146 | <string name="renderer_debug">Włącz debugowanie grafiki</string> | 156 | <string name="renderer_reactive_flushing">Użyj spłukiwania reaktywnego - reactive flushing</string> |
| 147 | <string name="renderer_debug_description">Kiedy włączone, interfejs graficzny korzysta z wolnego trybu debugowania błędów.</string> | 157 | <string name="renderer_reactive_flushing_description">Poprawia jakość renderowania w kilku grach, kosztem wydajności.</string> |
| 148 | <string name="use_disk_shader_cache">Użyj pamięci podręcznej shaderów na dysku</string> | 158 | <string name="use_disk_shader_cache">Pamięć podręczna shaderów</string> |
| 149 | <string name="use_disk_shader_cache_description">Zmniejsza przycięcia przez przechowywanie gotowych wygenerowanych plików oświetlenia w pamięci urządzenia.</string> | 159 | <string name="use_disk_shader_cache_description">Zmniejsza przycięcia przez przechowywanie gotowych wygenerowanych plików oświetlenia w pamięci urządzenia.</string> |
| 150 | 160 | ||
| 151 | <!-- Audio settings strings --> | 161 | <!-- Debug settings strings --> |
| 162 | <string name="cpu">CPU</string> | ||
| 163 | <string name="renderer_api">Interfejs graficzny</string> | ||
| 164 | <string name="renderer_debug">Debugowanie grafiki</string> | ||
| 165 | <string name="renderer_debug_description">Kiedy włączone, interfejs graficzny korzysta z wolnego trybu debugowania błędów.</string> | ||
| 152 | <string name="audio_volume">Głośność</string> | 166 | <string name="audio_volume">Głośność</string> |
| 153 | <string name="audio_volume_description">Ustala poziom głośności wyjścia dźwięku.</string> | 167 | <string name="audio_volume_description">Ustala poziom głośności wyjścia dźwięku.</string> |
| 154 | 168 | ||
| @@ -161,17 +175,21 @@ | |||
| 161 | <string name="reset_setting_confirmation">Przywrócić wartość tego ustawienia do wartości domyślnej?</string> | 175 | <string name="reset_setting_confirmation">Przywrócić wartość tego ustawienia do wartości domyślnej?</string> |
| 162 | <string name="reset_to_default">Przywróć ustawienia domyślne</string> | 176 | <string name="reset_to_default">Przywróć ustawienia domyślne</string> |
| 163 | <string name="reset_all_settings">Przywrócić WSZYSTKIE ustawienia?</string> | 177 | <string name="reset_all_settings">Przywrócić WSZYSTKIE ustawienia?</string> |
| 164 | <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> | 178 | <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> |
| 165 | <string name="settings_reset">Reset ustawień</string> | 179 | <string name="settings_reset">Reset ustawień</string> |
| 166 | <string name="close">Zamknij</string> | 180 | <string name="close">Zamknij</string> |
| 167 | <string name="learn_more">Dowiedz się więcej</string> | 181 | <string name="learn_more">Dowiedz się więcej</string> |
| 168 | 182 | <string name="auto">Automatyczny</string> | |
| 183 | <string name="submit">Zatwierdź</string> | ||
| 184 | <string name="string_import">Importuj</string> | ||
| 185 | <string name="export">Eksportuj</string> | ||
| 169 | <!-- GPU driver installation --> | 186 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">Wybierz sterownik GPU </string> | 187 | <string name="select_gpu_driver">Wybierz sterownik GPU </string> |
| 171 | <string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string> | 188 | <string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string> |
| 172 | <string name="select_gpu_driver_install">Zainstaluj</string> | 189 | <string name="select_gpu_driver_install">Zainstaluj</string> |
| 173 | <string name="select_gpu_driver_default">Domyślne</string> | 190 | <string name="select_gpu_driver_default">Domyślne</string> |
| 174 | <string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string> | 191 | <string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string> |
| 192 | <string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string> | ||
| 175 | <string name="system_gpu_driver">Systemowy sterownik GPU</string> | 193 | <string name="system_gpu_driver">Systemowy sterownik GPU</string> |
| 176 | <string name="installing_driver">Instalowanie sterownika...</string> | 194 | <string name="installing_driver">Instalowanie sterownika...</string> |
| 177 | 195 | ||
| @@ -182,10 +200,10 @@ | |||
| 182 | <string name="preferences_graphics">Grafika</string> | 200 | <string name="preferences_graphics">Grafika</string> |
| 183 | <string name="preferences_audio">Dźwięk</string> | 201 | <string name="preferences_audio">Dźwięk</string> |
| 184 | <string name="preferences_theme">Motyw i kolor</string> | 202 | <string name="preferences_theme">Motyw i kolor</string> |
| 203 | <string name="preferences_debug">Debug</string> | ||
| 185 | 204 | ||
| 186 | <!-- ROM loading errors --> | 205 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">Twój ROM jest zakodowany</string> | 206 | <string name="loader_error_encrypted">Twój ROM jest zakodowany</string> |
| 188 | <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> | ||
| 189 | <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> | 207 | <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> |
| 190 | <string name="loader_error_video_core">Błąd inicjacji podsystemu graficznego</string> | 208 | <string name="loader_error_video_core">Błąd inicjacji podsystemu graficznego</string> |
| 191 | <string name="loader_error_video_core_description">Zazwyczaj spowodowane niekompatybilnym sterownikiem GPU, instalacja niestandardowego sterownika może rozwiązać ten problem.</string> | 209 | <string name="loader_error_video_core_description">Zazwyczaj spowodowane niekompatybilnym sterownikiem GPU, instalacja niestandardowego sterownika może rozwiązać ten problem.</string> |
| @@ -198,23 +216,23 @@ | |||
| 198 | <string name="emulation_fps_counter">Licznik FPS</string> | 216 | <string name="emulation_fps_counter">Licznik FPS</string> |
| 199 | <string name="emulation_toggle_controls">Wybierz przyciski</string> | 217 | <string name="emulation_toggle_controls">Wybierz przyciski</string> |
| 200 | <string name="emulation_rel_stick_center">Wycentruj gałki</string> | 218 | <string name="emulation_rel_stick_center">Wycentruj gałki</string> |
| 201 | <string name="emulation_dpad_slide">Ruchomy DPad</string> | 219 | <string name="emulation_dpad_slide">Ruchomy D-pad</string> |
| 202 | <string name="emulation_haptics">Wibracje haptyczne</string> | 220 | <string name="emulation_haptics">Wibracje haptyczne</string> |
| 203 | <string name="emulation_show_overlay">Pokaż przyciski</string> | 221 | <string name="emulation_show_overlay">Pokaż przyciski</string> |
| 204 | <string name="emulation_toggle_all">Zaznacz wszystkie</string> | 222 | <string name="emulation_toggle_all">Włącz wszystkie</string> |
| 205 | <string name="emulation_control_adjust">Dostosuj nakładkę</string> | 223 | <string name="emulation_control_adjust">Dostosuj nakładkę</string> |
| 206 | <string name="emulation_control_scale">Skala</string> | 224 | <string name="emulation_control_scale">Skala</string> |
| 207 | <string name="emulation_control_opacity">Przeźroczystość</string> | 225 | <string name="emulation_control_opacity">Przeźroczystość</string> |
| 208 | <string name="emulation_touch_overlay_reset">Resetuj</string> | 226 | <string name="emulation_touch_overlay_reset">Resetuj nakładkę</string> |
| 209 | <string name="emulation_touch_overlay_edit">Edytuj nakładkę</string> | 227 | <string name="emulation_touch_overlay_edit">Edytuj nakładkę</string> |
| 210 | <string name="emulation_pause">Wstrzymaj emulacjÄ™</string> | 228 | <string name="emulation_pause">Wstrzymaj emulacjÄ™</string> |
| 211 | <string name="emulation_unpause">Wznów emulację</string> | 229 | <string name="emulation_unpause">Wznów emulację</string> |
| 212 | <string name="emulation_input_overlay">Opcje nakładki</string> | 230 | <string name="emulation_input_overlay">Opcje nakładki</string> |
| 213 | 231 | ||
| 214 | <string name="load_settings">Wczytywanie ustawień...</string> | 232 | <string name="load_settings">Wczytuję ustawienia...</string> |
| 215 | 233 | ||
| 216 | <!-- Software keyboard --> | 234 | <!-- Software keyboard --> |
| 217 | <string name="software_keyboard">Klawiatura systemowa</string> | 235 | <string name="software_keyboard">Klawiatura programowa</string> |
| 218 | 236 | ||
| 219 | <!-- Errors and warnings --> | 237 | <!-- Errors and warnings --> |
| 220 | <string name="abort_button">Przerwij</string> | 238 | <string name="abort_button">Przerwij</string> |
| @@ -226,7 +244,6 @@ | |||
| 226 | <string name="fatal_error">Błąd krytyczny</string> | 244 | <string name="fatal_error">Błąd krytyczny</string> |
| 227 | <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> | 245 | <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> |
| 228 | <string name="performance_warning">Wyłączenie tej opcji znacząco ograniczy wydajność! Dla najlepszego doświadczenia, zaleca się zostawienie tej opcji włączonej.</string> | 246 | <string name="performance_warning">Wyłączenie tej opcji znacząco ograniczy wydajność! Dla najlepszego doświadczenia, zaleca się zostawienie tej opcji włączonej.</string> |
| 229 | |||
| 230 | <!-- Region Names --> | 247 | <!-- Region Names --> |
| 231 | <string name="region_japan">Japonia</string> | 248 | <string name="region_japan">Japonia</string> |
| 232 | <string name="region_usa">USA</string> | 249 | <string name="region_usa">USA</string> |
| @@ -236,8 +253,7 @@ | |||
| 236 | <string name="region_korea">Korea</string> | 253 | <string name="region_korea">Korea</string> |
| 237 | <string name="region_taiwan">Tajwan</string> | 254 | <string name="region_taiwan">Tajwan</string> |
| 238 | 255 | ||
| 239 | <!-- Language Names --> | 256 | <string name="memory_gigabyte">GB</string> |
| 240 | |||
| 241 | <!-- Renderer APIs --> | 257 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">Vulkan</string> | 258 | <string name="renderer_vulkan">Vulkan</string> |
| 243 | <string name="renderer_none">Żadny</string> | 259 | <string name="renderer_none">Żadny</string> |
| @@ -274,12 +290,14 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">FXAA</string> | 290 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 275 | <string name="anti_aliasing_smaa">SMAA</string> | 291 | <string name="anti_aliasing_smaa">SMAA</string> |
| 276 | 292 | ||
| 293 | <string name="screen_layout_auto">Automatyczny</string> | ||
| 294 | |||
| 277 | <!-- Aspect Ratios --> | 295 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">Domyślne (16:9)</string> | 296 | <string name="ratio_default">Domyślne (16:9)</string> |
| 279 | <string name="ratio_force_four_three">WymuÅ› 4:3</string> | 297 | <string name="ratio_force_four_three">WymuÅ› 4:3</string> |
| 280 | <string name="ratio_force_twenty_one_nine">WymuÅ› 21:9</string> | 298 | <string name="ratio_force_twenty_one_nine">WymuÅ› 21:9</string> |
| 281 | <string name="ratio_force_sixteen_ten">WymuÅ› 16:10</string> | 299 | <string name="ratio_force_sixteen_ten">WymuÅ› 16:10</string> |
| 282 | <string name="ratio_stretch">RozciÄ…gnij do Okna</string> | 300 | <string name="ratio_stretch">RozciÄ…gnij do okna</string> |
| 283 | 301 | ||
| 284 | <!-- CPU Accuracy --> | 302 | <!-- CPU Accuracy --> |
| 285 | <string name="cpu_accuracy_accurate">Dokładny</string> | 303 | <string name="cpu_accuracy_accurate">Dokładny</string> |
| @@ -287,7 +305,7 @@ | |||
| 287 | <string name="cpu_accuracy_paranoid">Paranoid (Wolny)</string> | 305 | <string name="cpu_accuracy_paranoid">Paranoid (Wolny)</string> |
| 288 | 306 | ||
| 289 | <!-- Gamepad Buttons --> | 307 | <!-- Gamepad Buttons --> |
| 290 | <string name="gamepad_d_pad">D-Pad</string> | 308 | <string name="gamepad_d_pad">D-pad</string> |
| 291 | <string name="gamepad_left_stick">Lewa gałka</string> | 309 | <string name="gamepad_left_stick">Lewa gałka</string> |
| 292 | <string name="gamepad_right_stick">Prawa gałka</string> | 310 | <string name="gamepad_right_stick">Prawa gałka</string> |
| 293 | <string name="gamepad_home">Home</string> | 311 | <string name="gamepad_home">Home</string> |
| @@ -298,18 +316,21 @@ | |||
| 298 | <string name="building_shaders">Budowanie shaderów</string> | 316 | <string name="building_shaders">Budowanie shaderów</string> |
| 299 | 317 | ||
| 300 | <!-- Theme options --> | 318 | <!-- Theme options --> |
| 301 | <string name="change_app_theme">Zmień motyw aplikacji</string> | 319 | <string name="change_app_theme">Ustaw motyw aplikacji</string> |
| 302 | <string name="theme_default">Domyślny</string> | 320 | <string name="theme_default">Domyślny</string> |
| 303 | <string name="theme_material_you">Material You</string> | 321 | <string name="theme_material_you">Material You</string> |
| 304 | 322 | ||
| 305 | <!-- Theme Modes --> | 323 | <!-- Theme Modes --> |
| 306 | <string name="change_theme_mode">Zmiana trybu motywu</string> | 324 | <string name="change_theme_mode">Zmień tryb motywu</string> |
| 307 | <string name="theme_mode_follow_system">Podążaj za systemowym</string> | 325 | <string name="theme_mode_follow_system">Podążaj za systemowym</string> |
| 308 | <string name="theme_mode_light">Jasny</string> | 326 | <string name="theme_mode_light">Jasny</string> |
| 309 | <string name="theme_mode_dark">Ciemny</string> | 327 | <string name="theme_mode_dark">Ciemny</string> |
| 310 | 328 | ||
| 311 | <!-- Black backgrounds theme --> | 329 | <!-- Black backgrounds theme --> |
| 312 | <string name="use_black_backgrounds">Używaj czarnego tła</string> | 330 | <string name="use_black_backgrounds">Czarne tła</string> |
| 313 | <string name="use_black_backgrounds_description">Kiedy używany ciemny motyw, tła zostają zastąpione czernią.</string> | 331 | <string name="use_black_backgrounds_description">Kiedy używany ciemny motyw, tła zostają zastąpione czernią.</string> |
| 314 | 332 | ||
| 315 | </resources> | 333 | <!-- Licenses screen strings --> |
| 334 | <string name="licenses">Licencje</string> | ||
| 335 | <string name="license_fidelityfx_fsr_description">Rozciąganie wysokiej jakości od AMD</string> | ||
| 336 | </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 index caa095364..8888fc750 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml | |||
| @@ -1,30 +1,31 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluÃdas nem jogos ou chaves. <br /><br />Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.<br /><br /><![CDATA[2Learn more2]]></string> | 4 | <string name="app_disclaimer">Este software executa jogos do console Nintendo Switch. Não estão inclusos nem jogos ou chaves. <br /><br />Antes de começar, por favor localize o arquivo <![CDATA[1 prod.keys 1]]> no armazenamento de seu dispositivo.<br /><br /><![CDATA[2Saiba mais2]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emulação está Ativa</string> | 5 | <string name="emulation_notification_channel_name">Emulação está Ativa</string> |
| 6 | <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação está a correr.</string> | 6 | <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação estiver em andamento.</string> |
| 7 | <string name="emulation_notification_running">Yuzu está em execução </string> | 7 | <string name="emulation_notification_running">Yuzu está em execução </string> |
| 8 | <string name="notice_notification_channel_name">Notificações e erros</string> | 8 | <string name="notice_notification_channel_name">Notificações e erros</string> |
| 9 | <string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string> | 9 | <string name="notice_notification_channel_description">Mostra notificações quando algo dá errado.</string> |
| 10 | <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string> | 10 | <string name="notification_permission_not_granted">Acesso às notificações não concedido!</string> |
| 11 | 11 | ||
| 12 | <!-- Setup strings --> | 12 | <!-- Setup strings --> |
| 13 | <string name="welcome">Bemvindo! </string> | 13 | <string name="welcome">Bem-vindo! </string> |
| 14 | <string name="welcome_description">Aprende como configurar <b>yuzu</b> e arranca a emulação.</string> | 14 | <string name="welcome_description">Aprenda como configurar o <b>yuzu</b> e mergulhe na emulação.</string> |
| 15 | <string name="get_started">Começa</string> | 15 | <string name="get_started">Primeiros passos</string> |
| 16 | <string name="keys">Chaves</string> | 16 | <string name="keys">Keys</string> |
| 17 | <string name="keys_description">Seleciona o teu ficheiro <b>prod.keys</b> com o botão abaixo.</string> | 17 | <string name="keys_description">Selecione seu arquivo <b>prod.keys</b> com o botão abaixo.</string> |
| 18 | <string name="select_keys">Seleciona as Chaves</string> | 18 | <string name="select_keys">Selecione as Keys</string> |
| 19 | <string name="games">Jogos</string> | 19 | <string name="games">Jogos</string> |
| 20 | <string name="games_description">Seleciona a tua pasta <b>Games</b> com o botão abaixo.</string> | 20 | <string name="games_description">Seleciona sua pasta <b>Jogos</b> com o botão abaixo.</string> |
| 21 | <string name="done">Feito</string> | 21 | <string name="done">Feito</string> |
| 22 | <string name="done_description">Tudo pronto.\nDisfruta dos teus jogos!</string> | 22 | <string name="done_description">Tudo pronto.\nAproveite seus jogos!</string> |
| 23 | <string name="text_continue">Continuar</string> | 23 | <string name="text_continue">Continuar</string> |
| 24 | <string name="next">Próximo</string> | 24 | <string name="next">Próximo</string> |
| 25 | <string name="back">Voltar</string> | 25 | <string name="back">Voltar</string> |
| 26 | <string name="add_games">Adiciona Jogos</string> | 26 | <string name="add_games">Adicionar Jogos</string> |
| 27 | <string name="add_games_description">Seleciona a tua pasta de Jogos</string> | 27 | <string name="add_games_description">Selecione sua pasta de Jogos</string> |
| 28 | <string name="step_complete">Completo!</string> | ||
| 28 | 29 | ||
| 29 | <!-- Home strings --> | 30 | <!-- Home strings --> |
| 30 | <string name="home_games">Jogos</string> | 31 | <string name="home_games">Jogos</string> |
| @@ -37,7 +38,8 @@ | |||
| 37 | <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string> | 38 | <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string> |
| 38 | <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> | 39 | <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> |
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 40 | <string name="home_search_games">Procurar Jogos</string> | 41 | <string name="home_search_games">Procurar jogos</string> |
| 42 | <string name="search_settings">Procurar nas definições</string> | ||
| 41 | <string name="games_dir_selected">Pasta de Jogos selecionada</string> | 43 | <string name="games_dir_selected">Pasta de Jogos selecionada</string> |
| 42 | <string name="install_prod_keys">Instala prod.keys</string> | 44 | <string name="install_prod_keys">Instala prod.keys</string> |
| 43 | <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string> | 45 | <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string> |
| @@ -61,15 +63,18 @@ | |||
| 61 | <string name="invalid_keys_file">Ficheiro de chaves inválido</string> | 63 | <string name="invalid_keys_file">Ficheiro de chaves inválido</string> |
| 62 | <string name="install_keys_success">Chaves instaladas com sucesso</string> | 64 | <string name="install_keys_success">Chaves instaladas com sucesso</string> |
| 63 | <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string> | 65 | <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string> |
| 66 | <string name="install_prod_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .keys e tente novamente.</string> | ||
| 67 | <string name="install_amiibo_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .bin e tente novamente.</string> | ||
| 64 | <string name="invalid_keys_error">Chaves de encriptação inválidas</string> | 68 | <string name="invalid_keys_error">Chaves de encriptação inválidas</string> |
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 69 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string> | 70 | <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string> |
| 67 | <string name="install_gpu_driver">Instala driver para GPU</string> | 71 | <string name="install_gpu_driver">Instala driver para GPU</string> |
| 68 | <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string> | 72 | <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string> |
| 69 | <string name="advanced_settings">Definições avançadas</string> | 73 | <string name="advanced_settings">Definições avançadas</string> |
| 74 | <string name="advanced_settings_game">Definições avançadas: %1$s</string> | ||
| 70 | <string name="settings_description">Configura definições do emulador</string> | 75 | <string name="settings_description">Configura definições do emulador</string> |
| 71 | <string name="search_recently_played">Jogos recentes</string> | 76 | <string name="search_recently_played">Jogado recentemente</string> |
| 72 | <string name="search_recently_added">Adicionados recentemente</string> | 77 | <string name="search_recently_added">Adicionado recentemente</string> |
| 73 | <string name="search_retail">Jogos comerciais</string> | 78 | <string name="search_retail">Jogos comerciais</string> |
| 74 | <string name="search_homebrew">Homebrew</string> | 79 | <string name="search_homebrew">Homebrew</string> |
| 75 | <string name="open_user_folder">Abre a pasta Yuzu</string> | 80 | <string name="open_user_folder">Abre a pasta Yuzu</string> |
| @@ -86,6 +91,33 @@ | |||
| 86 | <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> | 91 | <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> |
| 87 | <string name="import_saves">Importar</string> | 92 | <string name="import_saves">Importar</string> |
| 88 | <string name="export_saves">Exportar</string> | 93 | <string name="export_saves">Exportar</string> |
| 94 | <string name="install_firmware">Instalar firmware</string> | ||
| 95 | <string name="install_firmware_description">O firmware deve estar em um arquivo ZIP e é necessário para iniciar alguns jogos.</string> | ||
| 96 | <string name="firmware_installing">Instalando firmware</string> | ||
| 97 | <string name="firmware_installed_success">Firmware instalado com sucesso.</string> | ||
| 98 | <string name="firmware_installed_failure">Falha na instalação do firmware</string> | ||
| 99 | <string name="firmware_installed_failure_description">Cofirma que os ficheiros firmware nca estão no root do finheiro zip e tenta de novo.</string> | ||
| 100 | <string name="share_log">Compartilhe registros de debug.</string> | ||
| 101 | <string name="share_log_description">Compartilhe o arquivo de registro do yuzu para obter ajuda com problemas</string> | ||
| 102 | <string name="share_log_missing">Arquivo de registro não encontrado</string> | ||
| 103 | <string name="install_game_content">Instalar conteúdo de jogos</string> | ||
| 104 | <string name="install_game_content_description">Instalar atualizações de jogos ou DLC</string> | ||
| 105 | <string name="installing_game_content">A instalar conteúdo...</string> | ||
| 106 | <string name="install_game_content_failure">Erro ao instalar ficheiro(s) para NAND</string> | ||
| 107 | <string name="install_game_content_failure_description">Por favor confitma que o conteúdo(s) é válido e que as prod.keys estão instaladas.</string> | ||
| 108 | <string name="install_game_content_failure_base">A instalação de jogos base não é permitida para evitar possÃveis conflitos.</string> | ||
| 109 | <string name="install_game_content_failure_file_extension">Sò conteúdos NSP e XCI são suportados. Por favor verifica que o conteúdo(s) do jogo são válidos.</string> | ||
| 110 | <string name="install_game_content_failed_count">%1$d erro(s) de instalação</string> | ||
| 111 | <string name="install_game_content_success">Conteúdo(s) de jogo instalados com sucesso</string> | ||
| 112 | <string name="install_game_content_success_install">%1$d instalado com sucesso</string> | ||
| 113 | <string name="install_game_content_success_overwrite">%1$d substituÃda com êxito</string> | ||
| 114 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 115 | <string name="custom_driver_not_supported">Drivers personalizados não suportados</string> | ||
| 116 | <string name="custom_driver_not_supported_description">Carrea«gamento de drivers personalizados não é suportado pr este dispositivo. \nCheck verifica esta opção de futuro para confirmar se o suporte foi adicionado!</string> | ||
| 117 | <string name="manage_yuzu_data">Administrar dados yuzu</string> | ||
| 118 | <string name="manage_yuzu_data_description">Importa/exporta firmware, chaves, dados do usuário e mais!</string> | ||
| 119 | <string name="share_save_file">Partilha ficheiro duardado</string> | ||
| 120 | <string name="export_save_failed">Erro ao exportar dados guardados</string> | ||
| 89 | 121 | ||
| 90 | <!-- About screen strings --> | 122 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">Gaia não é real</string> | 123 | <string name="gaia_is_not_real">Gaia não é real</string> |
| @@ -94,7 +126,18 @@ | |||
| 94 | <string name="contributors">Contribuidores</string> | 126 | <string name="contributors">Contribuidores</string> |
| 95 | <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string> | 127 | <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string> |
| 96 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 128 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 129 | <string name="licenses_description">Projetos que tornam o yuzu para Android possÃvel</string> | ||
| 97 | <string name="build">Versão</string> | 130 | <string name="build">Versão</string> |
| 131 | <string name="user_data">Dado de utilizados</string> | ||
| 132 | <string name="user_data_description">Importar/exportar todos dados da aplicação data.\n\n Ao importar dados do utilizados, todos os dados existentes do utilizados serão excluÃdos!</string> | ||
| 133 | <string name="exporting_user_data">A exportar dados de utilizados...</string> | ||
| 134 | <string name="importing_user_data">A importar dados de utilizador...</string> | ||
| 135 | <string name="import_user_data">Importar dados de utilizados...</string> | ||
| 136 | <string name="invalid_yuzu_backup">Backup yuzu inválido</string> | ||
| 137 | <string name="user_data_export_success">Dados de utilizados exportados com sucesso</string> | ||
| 138 | <string name="user_data_import_success">Dados de utilizador importado com sucesso</string> | ||
| 139 | <string name="user_data_export_cancelled">Exportação cancelada</string> | ||
| 140 | <string name="user_data_import_failed_description">Verifiqua se as pastas de dados do utilizados estão na raiz da pasta zip e contêm um arquivo de configuração em config/config.ini e tenta novamente.</string> | ||
| 98 | <string name="support_link">https://discord.gg/u77vRWY</string> | 141 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 99 | <string name="website_link">https://yuzu-emu.org/</string> | 142 | <string name="website_link">https://yuzu-emu.org/</string> |
| 100 | <string name="github_link">https://github.com/yuzu-emu</string> | 143 | <string name="github_link">https://github.com/yuzu-emu</string> |
| @@ -114,41 +157,53 @@ | |||
| 114 | <string name="are_you_interested">Estás interessado?</string> | 157 | <string name="are_you_interested">Estás interessado?</string> |
| 115 | 158 | ||
| 116 | <!-- General settings strings --> | 159 | <!-- General settings strings --> |
| 117 | <string name="frame_limit_enable">Ativar limite de velocidade</string> | 160 | <string name="frame_limit_enable">Limite de velocidade</string> |
| 118 | <string name="frame_limit_enable_description">Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal.</string> | 161 | <string name="frame_limit_enable_description">Limita a velocidade da emulação a uma porcentagem especÃfica da velocidade normal.</string> |
| 119 | <string name="frame_limit_slider">Percentagem do limite de velocidade</string> | 162 | <string name="frame_limit_slider">Percentagem do limite de velocidade</string> |
| 120 | <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> | 163 | <string name="frame_limit_slider_description">Especifica a porcentagem para limitar a velocidade de emulação. 100% é o normal. Valores mais altos ou mais baixos irão aumentar ou diminuir o limite de velocidade.</string> |
| 121 | <string name="cpu_accuracy">Precisão do CPU</string> | 164 | <string name="cpu_accuracy">Precisão do CPU</string> |
| 165 | <string name="value_with_units">%1$s%2$s</string> | ||
| 122 | 166 | ||
| 123 | <!-- System settings strings --> | 167 | <!-- System settings strings --> |
| 124 | <string name="use_docked_mode">Modo ancorado</string> | 168 | <string name="use_docked_mode">Modo Ancorado</string> |
| 125 | <string name="use_docked_mode_description">Emula em modo ancorado, que aumenta a resolução ás custas da performance.</string> | 169 | <string name="use_docked_mode_description">Aumenta a resolução, diminuindo o desempenho. O Modo Portátil é utilizado quando estiver desabilitado, diminuindo a resolução e melhorando o desempenho.</string> |
| 126 | <string name="emulated_region">Região da emulação</string> | 170 | <string name="emulated_region">Região da emulação</string> |
| 127 | <string name="emulated_language">Idioma da emulação</string> | 171 | <string name="emulated_language">Idioma da emulação</string> |
| 128 | <string name="select_rtc_date">Seleciona a data RTC</string> | 172 | <string name="select_rtc_date">Selecione a data do sistema</string> |
| 129 | <string name="select_rtc_time">Seleciona a hora RTC</string> | 173 | <string name="select_rtc_time">Selecione a hora do sistema</string> |
| 130 | <string name="use_custom_rtc">Ativa RTC personalizado</string> | 174 | <string name="use_custom_rtc">Data e hora personalizada</string> |
| 131 | <string name="use_custom_rtc_description">Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema</string> | 175 | <string name="use_custom_rtc_description">Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo.</string> |
| 132 | <string name="set_custom_rtc">Define RTC personalizado</string> | 176 | <string name="set_custom_rtc">Defina um relógio em tempo real personalizado</string> |
| 133 | 177 | ||
| 134 | <!-- Graphics settings strings --> | 178 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">API</string> | ||
| 136 | <string name="renderer_accuracy">NÃvel de precisão</string> | 179 | <string name="renderer_accuracy">NÃvel de precisão</string> |
| 137 | <string name="renderer_resolution">Resolução</string> | 180 | <string name="renderer_resolution">Resolução (Portátil/Ancorado)</string> |
| 138 | <string name="renderer_vsync">Modo VSync</string> | 181 | <string name="renderer_vsync">Modo VSync</string> |
| 139 | <string name="renderer_aspect_ratio">Proporção do ecrã</string> | 182 | <string name="renderer_screen_layout">Oriantação</string> |
| 183 | <string name="renderer_aspect_ratio">Proporção da tela</string> | ||
| 140 | <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string> | 184 | <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string> |
| 141 | <string name="renderer_anti_aliasing">Método de Anti-Aliasing </string> | 185 | <string name="renderer_anti_aliasing">Método de Anti-Serrilhado</string> |
| 142 | <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string> | 186 | <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string> |
| 143 | <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string> | 187 | <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string> |
| 144 | <string name="renderer_asynchronous_shaders">Usa shaders assÃncronos </string> | 188 | <string name="renderer_asynchronous_shaders">Usa shaders assÃncronos </string> |
| 145 | <string name="renderer_asynchronous_shaders_description">Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas.</string> | 189 | <string name="renderer_asynchronous_shaders_description">Compila os shaders de forma assÃncrona, reduzindo travamentos, mas pode apresentar problemas.</string> |
| 190 | <string name="renderer_reactive_flushing">Usar flushing reativo</string> | ||
| 191 | <string name="renderer_reactive_flushing_description">Melhora a precisão da renderização em alguns jogos ao custo de desempenho.</string> | ||
| 192 | <string name="use_disk_shader_cache">Cache de shaders em disco</string> | ||
| 193 | <string name="use_disk_shader_cache_description">Reduz travamentos ao armazenar e carregar localmente os shaders.</string> | ||
| 194 | |||
| 195 | <!-- Debug settings strings --> | ||
| 196 | <string name="cpu">CPU</string> | ||
| 197 | <string name="cpu_debug_mode">Depuração da CPU</string> | ||
| 198 | <string name="cpu_debug_mode_description">Coloca a CPU em um modo de depuração lento.</string> | ||
| 199 | <string name="gpu">GPU</string> | ||
| 200 | <string name="renderer_api">API</string> | ||
| 146 | <string name="renderer_debug">Ativar depuração de gráficos</string> | 201 | <string name="renderer_debug">Ativar depuração de gráficos</string> |
| 147 | <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string> | 202 | <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string> |
| 148 | <string name="use_disk_shader_cache">Usar cache de shaders em disco</string> | 203 | <string name="fastmem">Fastmem</string> |
| 149 | <string name="use_disk_shader_cache_description">Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento.</string> | ||
| 150 | 204 | ||
| 151 | <!-- Audio settings strings --> | 205 | <!-- Audio settings strings --> |
| 206 | <string name="audio_output_engine">Motor de saÃda</string> | ||
| 152 | <string name="audio_volume">Volume</string> | 207 | <string name="audio_volume">Volume</string> |
| 153 | <string name="audio_volume_description">Especifica o volume de saÃda.</string> | 208 | <string name="audio_volume_description">Especifica o volume de saÃda.</string> |
| 154 | 209 | ||
| @@ -157,14 +212,24 @@ | |||
| 157 | <string name="ini_saved">Definições guardadas</string> | 212 | <string name="ini_saved">Definições guardadas</string> |
| 158 | <string name="gameid_saved">Definições guardadas para %1$s</string> | 213 | <string name="gameid_saved">Definições guardadas para %1$s</string> |
| 159 | <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string> | 214 | <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string> |
| 215 | <string name="unimplemented_menu">Menu não implementado</string> | ||
| 160 | <string name="loading">A carregar...</string> | 216 | <string name="loading">A carregar...</string> |
| 217 | <string name="shutting_down">A desligar...</string> | ||
| 161 | <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string> | 218 | <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string> |
| 162 | <string name="reset_to_default">Reverter para padrão</string> | 219 | <string name="reset_to_default">Reverter para padrão</string> |
| 163 | <string name="reset_all_settings">Redefinir todas as definições?</string> | 220 | <string name="reset_all_settings">Redefinir todas as definições?</string> |
| 164 | <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> | 221 | <string name="reset_all_settings_description">Todas as configurações avançadas retornarão ao padrão. Isto não pode ser desfeito.</string> |
| 165 | <string name="settings_reset">Redefinir definições</string> | 222 | <string name="settings_reset">Redefinir definições</string> |
| 166 | <string name="close">Fechar</string> | 223 | <string name="close">Fechar</string> |
| 167 | <string name="learn_more">Saiba mais</string> | 224 | <string name="learn_more">Saiba mais</string> |
| 225 | <string name="auto">Automático</string> | ||
| 226 | <string name="submit">Enviar</string> | ||
| 227 | <string name="string_null">Nenhum (desativado)</string> | ||
| 228 | <string name="string_import">Importar</string> | ||
| 229 | <string name="export">Exportar</string> | ||
| 230 | <string name="export_failed">Exportação falhada</string> | ||
| 231 | <string name="import_failed">IMportação falhada</string> | ||
| 232 | <string name="cancelling">A cancelar</string> | ||
| 168 | 233 | ||
| 169 | <!-- GPU driver installation --> | 234 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">Seleciona a driver para o GPU</string> | 235 | <string name="select_gpu_driver">Seleciona a driver para o GPU</string> |
| @@ -172,6 +237,7 @@ | |||
| 172 | <string name="select_gpu_driver_install">Instalar</string> | 237 | <string name="select_gpu_driver_install">Instalar</string> |
| 173 | <string name="select_gpu_driver_default">Padrão</string> | 238 | <string name="select_gpu_driver_default">Padrão</string> |
| 174 | <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> | 239 | <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> |
| 240 | <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string> | ||
| 175 | <string name="system_gpu_driver">Driver do GPU padrão</string> | 241 | <string name="system_gpu_driver">Driver do GPU padrão</string> |
| 176 | <string name="installing_driver">A instalar o Driver...</string> | 242 | <string name="installing_driver">A instalar o Driver...</string> |
| 177 | 243 | ||
| @@ -182,10 +248,11 @@ | |||
| 182 | <string name="preferences_graphics">Gráficos</string> | 248 | <string name="preferences_graphics">Gráficos</string> |
| 183 | <string name="preferences_audio">Ãudio</string> | 249 | <string name="preferences_audio">Ãudio</string> |
| 184 | <string name="preferences_theme">Cor e tema.</string> | 250 | <string name="preferences_theme">Cor e tema.</string> |
| 251 | <string name="preferences_debug">Depuração</string> | ||
| 185 | 252 | ||
| 186 | <!-- ROM loading errors --> | 253 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">A tua ROM está encriptada</string> | 254 | <string name="loader_error_encrypted">A tua ROM está encriptada</string> |
| 188 | <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> | 255 | <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor, siga os guias para despejar novamente o seu <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">cartucho de jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">tÃtulos instalados</a>.]]></string> |
| 189 | <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> | 256 | <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> |
| 190 | <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vÃdeo.</string> | 257 | <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vÃdeo.</string> |
| 191 | <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> | 258 | <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> |
| @@ -193,25 +260,25 @@ | |||
| 193 | <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string> | 260 | <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string> |
| 194 | 261 | ||
| 195 | <!-- Emulation Menu --> | 262 | <!-- Emulation Menu --> |
| 196 | <string name="emulation_exit">Sair da emulação</string> | 263 | <string name="emulation_exit">Parar emulação</string> |
| 197 | <string name="emulation_done">Feito</string> | 264 | <string name="emulation_done">Feito</string> |
| 198 | <string name="emulation_fps_counter">Contador de FPS</string> | 265 | <string name="emulation_fps_counter">Contador de FPS</string> |
| 199 | <string name="emulation_toggle_controls">Alterar Controlos</string> | 266 | <string name="emulation_toggle_controls">Alterar controles</string> |
| 200 | <string name="emulation_rel_stick_center">Centro do Analógico Relativo</string> | 267 | <string name="emulation_rel_stick_center">Centro Relativo de Analógico</string> |
| 201 | <string name="emulation_dpad_slide">Deslizar do DPad</string> | 268 | <string name="emulation_dpad_slide">Deslizamento dos Botões Direcionais</string> |
| 202 | <string name="emulation_haptics">Hápticos </string> | 269 | <string name="emulation_haptics">Vibração ao tocar</string> |
| 203 | <string name="emulation_show_overlay">Mostrar sobreposição </string> | 270 | <string name="emulation_show_overlay">Mostrar overlay</string> |
| 204 | <string name="emulation_toggle_all">Alterar todos</string> | 271 | <string name="emulation_toggle_all">Marcar/Desmarcar tudo</string> |
| 205 | <string name="emulation_control_adjust">Ajustar a sobreposição </string> | 272 | <string name="emulation_control_adjust">Ajustar overlay</string> |
| 206 | <string name="emulation_control_scale">Escala</string> | 273 | <string name="emulation_control_scale">Escala</string> |
| 207 | <string name="emulation_control_opacity">Opacidade</string> | 274 | <string name="emulation_control_opacity">Opacidade</string> |
| 208 | <string name="emulation_touch_overlay_reset">Redefinir Sobreposição </string> | 275 | <string name="emulation_touch_overlay_reset">Restaurar overlay padrão</string> |
| 209 | <string name="emulation_touch_overlay_edit">Editar sobreposição </string> | 276 | <string name="emulation_touch_overlay_edit">Editar overlay</string> |
| 210 | <string name="emulation_pause">Pausa emulação</string> | 277 | <string name="emulation_pause">Pausar emulação</string> |
| 211 | <string name="emulation_unpause">Retomar emulação</string> | 278 | <string name="emulation_unpause">Retomar emulação</string> |
| 212 | <string name="emulation_input_overlay">Opções de sobreposição </string> | 279 | <string name="emulation_input_overlay">Opções de overlay</string> |
| 213 | 280 | ||
| 214 | <string name="load_settings">Configurações a carregar...</string> | 281 | <string name="load_settings">Carregando configurações...</string> |
| 215 | 282 | ||
| 216 | <!-- Software keyboard --> | 283 | <!-- Software keyboard --> |
| 217 | <string name="software_keyboard">Teclado de software</string> | 284 | <string name="software_keyboard">Teclado de software</string> |
| @@ -226,6 +293,9 @@ | |||
| 226 | <string name="fatal_error">Erro fatal</string> | 293 | <string name="fatal_error">Erro fatal</string> |
| 227 | <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string> | 294 | <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string> |
| 228 | <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> | 295 | <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> |
| 296 | <string name="device_memory_inadequate">RAM do dispositivo: %1$s\nRecommended: %2$s</string> | ||
| 297 | <string name="memory_formatted">%1$s %2$s</string> | ||
| 298 | <string name="no_game_present">Nenhum jogo inicializável presente!</string> | ||
| 229 | 299 | ||
| 230 | <!-- Region Names --> | 300 | <!-- Region Names --> |
| 231 | <string name="region_japan">Japão</string> | 301 | <string name="region_japan">Japão</string> |
| @@ -236,7 +306,14 @@ | |||
| 236 | <string name="region_korea">Coréia</string> | 306 | <string name="region_korea">Coréia</string> |
| 237 | <string name="region_taiwan">Taiwan</string> | 307 | <string name="region_taiwan">Taiwan</string> |
| 238 | 308 | ||
| 239 | <!-- Language Names --> | 309 | <!-- Memory Sizes --> |
| 310 | <string name="memory_byte">Byte</string> | ||
| 311 | <string name="memory_kilobyte">KB</string> | ||
| 312 | <string name="memory_megabyte">MB</string> | ||
| 313 | <string name="memory_gigabyte">GB</string> | ||
| 314 | <string name="memory_terabyte">TB</string> | ||
| 315 | <string name="memory_petabyte">PB</string> | ||
| 316 | <string name="memory_exabyte">EB</string> | ||
| 240 | 317 | ||
| 241 | <!-- Renderer APIs --> | 318 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">Vulcano</string> | 319 | <string name="renderer_vulkan">Vulcano</string> |
| @@ -274,12 +351,17 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">FXAA</string> | 351 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 275 | <string name="anti_aliasing_smaa">SMAA</string> | 352 | <string name="anti_aliasing_smaa">SMAA</string> |
| 276 | 353 | ||
| 354 | <!-- Screen Layouts --> | ||
| 355 | <string name="screen_layout_landscape">Landscape</string> | ||
| 356 | <string name="screen_layout_portrait">Portrait</string> | ||
| 357 | <string name="screen_layout_auto">Automático</string> | ||
| 358 | |||
| 277 | <!-- Aspect Ratios --> | 359 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">Padrão (16:9)</string> | 360 | <string name="ratio_default">Padrão (16:9)</string> |
| 279 | <string name="ratio_force_four_three">Forçar 4:3</string> | 361 | <string name="ratio_force_four_three">Forçar 4:3</string> |
| 280 | <string name="ratio_force_twenty_one_nine">Forçar 21:9</string> | 362 | <string name="ratio_force_twenty_one_nine">Forçar 21:9</string> |
| 281 | <string name="ratio_force_sixteen_ten">Forçar 16:10</string> | 363 | <string name="ratio_force_sixteen_ten">Forçar 16:10</string> |
| 282 | <string name="ratio_stretch">Esticar para a janela</string> | 364 | <string name="ratio_stretch">Esticar à janela</string> |
| 283 | 365 | ||
| 284 | <!-- CPU Accuracy --> | 366 | <!-- CPU Accuracy --> |
| 285 | <string name="cpu_accuracy_accurate">Preciso</string> | 367 | <string name="cpu_accuracy_accurate">Preciso</string> |
| @@ -287,7 +369,7 @@ | |||
| 287 | <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string> | 369 | <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string> |
| 288 | 370 | ||
| 289 | <!-- Gamepad Buttons --> | 371 | <!-- Gamepad Buttons --> |
| 290 | <string name="gamepad_d_pad">D-pad</string> | 372 | <string name="gamepad_d_pad">Botões Direcionais</string> |
| 291 | <string name="gamepad_left_stick">Analógico esquerdo</string> | 373 | <string name="gamepad_left_stick">Analógico esquerdo</string> |
| 292 | <string name="gamepad_right_stick">Analógico direito</string> | 374 | <string name="gamepad_right_stick">Analógico direito</string> |
| 293 | <string name="gamepad_home">Botão Home</string> | 375 | <string name="gamepad_home">Botão Home</string> |
| @@ -298,18 +380,32 @@ | |||
| 298 | <string name="building_shaders">A criar shaders</string> | 380 | <string name="building_shaders">A criar shaders</string> |
| 299 | 381 | ||
| 300 | <!-- Theme options --> | 382 | <!-- Theme options --> |
| 301 | <string name="change_app_theme">Muda o Tema da App</string> | 383 | <string name="change_app_theme">Mudar o tema do aplicativo</string> |
| 302 | <string name="theme_default">Padrão</string> | 384 | <string name="theme_default">Padrão</string> |
| 303 | <string name="theme_material_you">Material You</string> | 385 | <string name="theme_material_you">Material You</string> |
| 304 | 386 | ||
| 305 | <!-- Theme Modes --> | 387 | <!-- Theme Modes --> |
| 306 | <string name="change_theme_mode">Altera o Modo do Tema</string> | 388 | <string name="change_theme_mode">Alterar o tema</string> |
| 307 | <string name="theme_mode_follow_system">Igual ao Sistema</string> | 389 | <string name="theme_mode_follow_system">Igual ao Sistema</string> |
| 308 | <string name="theme_mode_light">Claro</string> | 390 | <string name="theme_mode_light">Claro</string> |
| 309 | <string name="theme_mode_dark">Escuro</string> | 391 | <string name="theme_mode_dark">Escuro</string> |
| 310 | 392 | ||
| 393 | <!-- Audio output engines --> | ||
| 394 | <string name="cubeb">cubeb</string> | ||
| 395 | |||
| 311 | <!-- Black backgrounds theme --> | 396 | <!-- Black backgrounds theme --> |
| 312 | <string name="use_black_backgrounds">Usa Fundos Negros</string> | 397 | <string name="use_black_backgrounds">Plano de fundo preto</string> |
| 313 | <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string> | 398 | <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string> |
| 314 | 399 | ||
| 315 | </resources> | 400 | <!-- Picture-In-Picture --> |
| 401 | <string name="picture_in_picture">Picture in Picture</string> | ||
| 402 | <string name="picture_in_picture_description">Minimizar a janela quando colocada em segundo plano</string> | ||
| 403 | <string name="pause">Pausa</string> | ||
| 404 | <string name="play">Correr</string> | ||
| 405 | <string name="mute">Mudo</string> | ||
| 406 | <string name="unmute">Unmute</string> | ||
| 407 | |||
| 408 | <!-- Licenses screen strings --> | ||
| 409 | <string name="licenses">Licenças</string> | ||
| 410 | <string name="license_fidelityfx_fsr_description">Upscaling de alta qualidade da AMD</string> | ||
| 411 | </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 index 0a1a47fbb..6afea9b03 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluÃdas nem jogos ou chaves. <br /><br />Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.<br /><br /><![CDATA[2Learn more2]]></string> | 4 | <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluÃdas nem jogos ou chaves. <br /><br />Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.<br /><br /><![CDATA[2Learn more2]]></string> |
| 5 | <string name="emulation_notification_channel_name">Emulação está Ativa</string> | 5 | <string name="emulation_notification_channel_name">Emulação está Ativa</string> |
| @@ -25,6 +25,7 @@ | |||
| 25 | <string name="back">Voltar</string> | 25 | <string name="back">Voltar</string> |
| 26 | <string name="add_games">Adiciona Jogos</string> | 26 | <string name="add_games">Adiciona Jogos</string> |
| 27 | <string name="add_games_description">Seleciona a tua pasta de Jogos</string> | 27 | <string name="add_games_description">Seleciona a tua pasta de Jogos</string> |
| 28 | <string name="step_complete">Completo!</string> | ||
| 28 | 29 | ||
| 29 | <!-- Home strings --> | 30 | <!-- Home strings --> |
| 30 | <string name="home_games">Jogos</string> | 31 | <string name="home_games">Jogos</string> |
| @@ -37,7 +38,8 @@ | |||
| 37 | <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string> | 38 | <string name="add_games_warning">Ignorar a seleção da pasta de jogos?</string> |
| 38 | <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> | 39 | <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> |
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 40 | <string name="home_search_games">Procurar Jogos</string> | 41 | <string name="home_search_games">Procurar jogos</string> |
| 42 | <string name="search_settings">Procurar nas definições</string> | ||
| 41 | <string name="games_dir_selected">Pasta de Jogos selecionada</string> | 43 | <string name="games_dir_selected">Pasta de Jogos selecionada</string> |
| 42 | <string name="install_prod_keys">Instala prod.keys</string> | 44 | <string name="install_prod_keys">Instala prod.keys</string> |
| 43 | <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string> | 45 | <string name="install_prod_keys_description">Necessário para desencriptar jogos comerciais</string> |
| @@ -61,15 +63,18 @@ | |||
| 61 | <string name="invalid_keys_file">Ficheiro de chaves inválido</string> | 63 | <string name="invalid_keys_file">Ficheiro de chaves inválido</string> |
| 62 | <string name="install_keys_success">Chaves instaladas com sucesso</string> | 64 | <string name="install_keys_success">Chaves instaladas com sucesso</string> |
| 63 | <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string> | 65 | <string name="reading_keys_failure">Erro ao ler chaves de encriptação</string> |
| 66 | <string name="install_prod_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .keys e tente novamente.</string> | ||
| 67 | <string name="install_amiibo_keys_failure_extension_description">Verifique se seu arquivo keys possui a extensão .bin e tente novamente.</string> | ||
| 64 | <string name="invalid_keys_error">Chaves de encriptação inválidas</string> | 68 | <string name="invalid_keys_error">Chaves de encriptação inválidas</string> |
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 69 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string> | 70 | <string name="install_keys_failure_description">O ficheiro selecionado está corrompido. Por favor recarrega as tuas chaves.</string> |
| 67 | <string name="install_gpu_driver">Instala driver para GPU</string> | 71 | <string name="install_gpu_driver">Instala driver para GPU</string> |
| 68 | <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string> | 72 | <string name="install_gpu_driver_description">Instala drivers alternativos para desempenho ou precisão potencialmente melhores</string> |
| 69 | <string name="advanced_settings">Configurações avançadas</string> | 73 | <string name="advanced_settings">Configurações avançadas</string> |
| 74 | <string name="advanced_settings_game">Definições avançadas: %1$s</string> | ||
| 70 | <string name="settings_description">Configura configurações do emulador</string> | 75 | <string name="settings_description">Configura configurações do emulador</string> |
| 71 | <string name="search_recently_played">Jogos recentes</string> | 76 | <string name="search_recently_played">Jogado recentemente</string> |
| 72 | <string name="search_recently_added">Adicionados recentemente</string> | 77 | <string name="search_recently_added">Adicionado recentemente</string> |
| 73 | <string name="search_retail">Jogos comerciais</string> | 78 | <string name="search_retail">Jogos comerciais</string> |
| 74 | <string name="search_homebrew">Homebrew</string> | 79 | <string name="search_homebrew">Homebrew</string> |
| 75 | <string name="open_user_folder">Abre a pasta Yuzu</string> | 80 | <string name="open_user_folder">Abre a pasta Yuzu</string> |
| @@ -86,6 +91,33 @@ | |||
| 86 | <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> | 91 | <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> |
| 87 | <string name="import_saves">Importar</string> | 92 | <string name="import_saves">Importar</string> |
| 88 | <string name="export_saves">Exportar</string> | 93 | <string name="export_saves">Exportar</string> |
| 94 | <string name="install_firmware">Instalar firmware</string> | ||
| 95 | <string name="install_firmware_description">O firmware deve estar em um arquivo ZIP e é necessário para iniciar alguns jogos.</string> | ||
| 96 | <string name="firmware_installing">Instalando firmware</string> | ||
| 97 | <string name="firmware_installed_success">Firmware instalado com sucesso.</string> | ||
| 98 | <string name="firmware_installed_failure">Falha na instalação do firmware</string> | ||
| 99 | <string name="firmware_installed_failure_description">Cofirma que os ficheiros firmware nca estão no root do finheiro zip e tenta de novo.</string> | ||
| 100 | <string name="share_log">Compartilhe registros de debug.</string> | ||
| 101 | <string name="share_log_description">Compartilhe o arquivo de registro do yuzu para obter ajuda com problemas</string> | ||
| 102 | <string name="share_log_missing">Arquivo de registro não encontrado</string> | ||
| 103 | <string name="install_game_content">Instalar conteúdo adicional</string> | ||
| 104 | <string name="install_game_content_description">Instale atualizações de jogos ou DLC</string> | ||
| 105 | <string name="installing_game_content">A instalar conteúdo...</string> | ||
| 106 | <string name="install_game_content_failure">Erro ao instalar ficheiro(s) para NAND</string> | ||
| 107 | <string name="install_game_content_failure_description">Por favor confitma que o conteúdo(s) é válido e que as prod.keys estão instaladas.</string> | ||
| 108 | <string name="install_game_content_failure_base">A instalação de jogos base não é permitida para evitar possÃveis conflitos.</string> | ||
| 109 | <string name="install_game_content_failure_file_extension">Sò conteúdos NSP e XCI são suportados. Por favor verifica que o conteúdo(s) do jogo são válidos.</string> | ||
| 110 | <string name="install_game_content_failed_count">%1$d erro(s) de instalação</string> | ||
| 111 | <string name="install_game_content_success">Conteúdo(s) de jogo instalados com sucesso</string> | ||
| 112 | <string name="install_game_content_success_install">%1$d instalado com sucesso</string> | ||
| 113 | <string name="install_game_content_success_overwrite">%1$d substituÃda com êxito</string> | ||
| 114 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 115 | <string name="custom_driver_not_supported">Drivers personalizados não suportados</string> | ||
| 116 | <string name="custom_driver_not_supported_description">Carrea«gamento de drivers personalizados não é suportado pr este dispositivo. \nCheck verifica esta opção de futuro para confirmar se o suporte foi adicionado!</string> | ||
| 117 | <string name="manage_yuzu_data">Administrar dados yuzu</string> | ||
| 118 | <string name="manage_yuzu_data_description">Importa/exporta firmware, chaves, dados do usuário e mais!</string> | ||
| 119 | <string name="share_save_file">Partilha ficheiro duardado</string> | ||
| 120 | <string name="export_save_failed">Erro ao exportar dados guardados</string> | ||
| 89 | 121 | ||
| 90 | <!-- About screen strings --> | 122 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">Gaia não é real</string> | 123 | <string name="gaia_is_not_real">Gaia não é real</string> |
| @@ -94,7 +126,18 @@ | |||
| 94 | <string name="contributors">Contribuidores</string> | 126 | <string name="contributors">Contribuidores</string> |
| 95 | <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string> | 127 | <string name="contributors_description">Feito com \u2764 da equipa do Yuzu</string> |
| 96 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 128 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 129 | <string name="licenses_description">Projetos que tornam o yuzu para Android possÃvel</string> | ||
| 97 | <string name="build">Versão</string> | 130 | <string name="build">Versão</string> |
| 131 | <string name="user_data">Dado de utilizados</string> | ||
| 132 | <string name="user_data_description">Importar/exportar todos dados da aplicação data.\n\n Ao importar dados do utilizados, todos os dados existentes do utilizados serão excluÃdos!</string> | ||
| 133 | <string name="exporting_user_data">A exportar dados de utilizados...</string> | ||
| 134 | <string name="importing_user_data">A importar dados de utilizador...</string> | ||
| 135 | <string name="import_user_data">Importar dados de utilizados...</string> | ||
| 136 | <string name="invalid_yuzu_backup">Backup yuzu inválido</string> | ||
| 137 | <string name="user_data_export_success">Dados de utilizados exportados com sucesso</string> | ||
| 138 | <string name="user_data_import_success">Dados de utilizador importado com sucesso</string> | ||
| 139 | <string name="user_data_export_cancelled">Exportação cancelada</string> | ||
| 140 | <string name="user_data_import_failed_description">Verifiqua se as pastas de dados do utilizados estão na raiz da pasta zip e contêm um arquivo de configuração em config/config.ini e tenta novamente.</string> | ||
| 98 | <string name="support_link">https://discord.gg/u77vRWY</string> | 141 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 99 | <string name="website_link">https://yuzu-emu.org/</string> | 142 | <string name="website_link">https://yuzu-emu.org/</string> |
| 100 | <string name="github_link">https://github.com/yuzu-emu</string> | 143 | <string name="github_link">https://github.com/yuzu-emu</string> |
| @@ -114,41 +157,53 @@ | |||
| 114 | <string name="are_you_interested">Estás interessado?</string> | 157 | <string name="are_you_interested">Estás interessado?</string> |
| 115 | 158 | ||
| 116 | <!-- General settings strings --> | 159 | <!-- General settings strings --> |
| 117 | <string name="frame_limit_enable">Ativar limite de velocidade</string> | 160 | <string name="frame_limit_enable">Limite de velocidade</string> |
| 118 | <string name="frame_limit_enable_description">Quando ativada, a velocidade da emulação será limitada à percentagem definida da velocidade normal.</string> | 161 | <string name="frame_limit_enable_description">Limita a velocidade da emulação a uma porcentagem especÃfica da velocidade normal.</string> |
| 119 | <string name="frame_limit_slider">Percentagem do limite de velocidade</string> | 162 | <string name="frame_limit_slider">Percentagem do limite de velocidade</string> |
| 120 | <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> | 163 | <string name="frame_limit_slider_description">Especifica a porcentagem para limitar a velocidade de emulação. 100% é o normal. Valores mais altos ou mais baixos irão aumentar ou diminuir o limite de velocidade.</string> |
| 121 | <string name="cpu_accuracy">Precisão do CPU</string> | 164 | <string name="cpu_accuracy">Precisão do CPU</string> |
| 165 | <string name="value_with_units">%1$s%2$s</string> | ||
| 122 | 166 | ||
| 123 | <!-- System settings strings --> | 167 | <!-- System settings strings --> |
| 124 | <string name="use_docked_mode">Modo ancorado</string> | 168 | <string name="use_docked_mode">Modo Ancorado</string> |
| 125 | <string name="use_docked_mode_description">Emula em modo ancorado, que aumenta a resolução ás custas da performance.</string> | 169 | <string name="use_docked_mode_description">Aumenta a resolução, diminuindo o desempenho. O Modo Portátil é utilizado quando estiver desabilitado, diminuindo a resolução e melhorando o desempenho.</string> |
| 126 | <string name="emulated_region">Região da emulação</string> | 170 | <string name="emulated_region">Região da emulação</string> |
| 127 | <string name="emulated_language">Idioma da emulação</string> | 171 | <string name="emulated_language">Idioma da emulação</string> |
| 128 | <string name="select_rtc_date">Seleciona a data RTC</string> | 172 | <string name="select_rtc_date">Selecione a data do sistema</string> |
| 129 | <string name="select_rtc_time">Seleciona a hora RTC</string> | 173 | <string name="select_rtc_time">Selecione a hora do sistema</string> |
| 130 | <string name="use_custom_rtc">Ativa RTC personalizado</string> | 174 | <string name="use_custom_rtc">RTC personalizado</string> |
| 131 | <string name="use_custom_rtc_description">Esta configuração permite definir um RTC personalizado diferente da hora atual do sistema</string> | 175 | <string name="use_custom_rtc_description">Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo.</string> |
| 132 | <string name="set_custom_rtc">Define RTC personalizado</string> | 176 | <string name="set_custom_rtc">Defina um relógio em tempo real personalizado</string> |
| 133 | 177 | ||
| 134 | <!-- Graphics settings strings --> | 178 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">API</string> | ||
| 136 | <string name="renderer_accuracy">NÃvel de precisão</string> | 179 | <string name="renderer_accuracy">NÃvel de precisão</string> |
| 137 | <string name="renderer_resolution">Resolução</string> | 180 | <string name="renderer_resolution">Resolução (Portátil/Ancorado)</string> |
| 138 | <string name="renderer_vsync">Modo VSync</string> | 181 | <string name="renderer_vsync">Modo VSync</string> |
| 139 | <string name="renderer_aspect_ratio">Proporção do ecrã</string> | 182 | <string name="renderer_screen_layout">Oriantação</string> |
| 183 | <string name="renderer_aspect_ratio">Proporção da tela</string> | ||
| 140 | <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string> | 184 | <string name="renderer_scaling_filter">Filtro de Adaptação da Janela</string> |
| 141 | <string name="renderer_anti_aliasing">Método de Anti-Aliasing </string> | 185 | <string name="renderer_anti_aliasing">Método de Anti-Serrilhado</string> |
| 142 | <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string> | 186 | <string name="renderer_force_max_clock">Força velocidade máxima (Adreno only)</string> |
| 143 | <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string> | 187 | <string name="renderer_force_max_clock_description">Força o GPU a correr à velocidade máxima (restrições térmicas serão aplicadas)</string> |
| 144 | <string name="renderer_asynchronous_shaders">Usa shaders assÃncronos </string> | 188 | <string name="renderer_asynchronous_shaders">Usa shaders assÃncronos </string> |
| 145 | <string name="renderer_asynchronous_shaders_description">Compila shaders assincronamente, que aumentará a fluidez, mas poderá causar falhas.</string> | 189 | <string name="renderer_asynchronous_shaders_description">Compila os shaders de forma assÃncrona, reduzindo travamentos, mas pode apresentar problemas.</string> |
| 190 | <string name="renderer_reactive_flushing">Usar flushing reativo</string> | ||
| 191 | <string name="renderer_reactive_flushing_description">Melhora a precisão da renderização em alguns jogos ao custo de desempenho.</string> | ||
| 192 | <string name="use_disk_shader_cache">Cache de shaders em disco</string> | ||
| 193 | <string name="use_disk_shader_cache_description">Reduz travamentos ao armazenar e carregar localmente os shaders.</string> | ||
| 194 | |||
| 195 | <!-- Debug settings strings --> | ||
| 196 | <string name="cpu">CPU</string> | ||
| 197 | <string name="cpu_debug_mode">Depuração da CPU</string> | ||
| 198 | <string name="cpu_debug_mode_description">Coloca a CPU em um modo de depuração lento.</string> | ||
| 199 | <string name="gpu">GPU</string> | ||
| 200 | <string name="renderer_api">API</string> | ||
| 146 | <string name="renderer_debug">Ativar depuração de gráficos</string> | 201 | <string name="renderer_debug">Ativar depuração de gráficos</string> |
| 147 | <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string> | 202 | <string name="renderer_debug_description">Quando selecionado, a API gráfica entra num modo de depuração mais lento.</string> |
| 148 | <string name="use_disk_shader_cache">Usar cache do disk shader</string> | 203 | <string name="fastmem">Fastmem</string> |
| 149 | <string name="use_disk_shader_cache_description">Aumenta a fluidez ao guardar e carregar shaders gerados para o armazenamento.</string> | ||
| 150 | 204 | ||
| 151 | <!-- Audio settings strings --> | 205 | <!-- Audio settings strings --> |
| 206 | <string name="audio_output_engine">Motor de saÃda</string> | ||
| 152 | <string name="audio_volume">Volume</string> | 207 | <string name="audio_volume">Volume</string> |
| 153 | <string name="audio_volume_description">Especifica o volume de saÃda.</string> | 208 | <string name="audio_volume_description">Especifica o volume de saÃda.</string> |
| 154 | 209 | ||
| @@ -157,14 +212,24 @@ | |||
| 157 | <string name="ini_saved">Configurações guardadas</string> | 212 | <string name="ini_saved">Configurações guardadas</string> |
| 158 | <string name="gameid_saved">Configurações guardadas para %1$s</string> | 213 | <string name="gameid_saved">Configurações guardadas para %1$s</string> |
| 159 | <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string> | 214 | <string name="error_saving">Erro ao guardar %1$s.ini: %2$s</string> |
| 215 | <string name="unimplemented_menu">Menu não implementado</string> | ||
| 160 | <string name="loading">A carregar...</string> | 216 | <string name="loading">A carregar...</string> |
| 217 | <string name="shutting_down">A desligar...</string> | ||
| 161 | <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string> | 218 | <string name="reset_setting_confirmation">Queres reverter esta definição para os valores padrão?</string> |
| 162 | <string name="reset_to_default">Reverter para padrão</string> | 219 | <string name="reset_to_default">Reverter para padrão</string> |
| 163 | <string name="reset_all_settings">Redefinir todas as configurações?</string> | 220 | <string name="reset_all_settings">Redefinir todas as configurações?</string> |
| 164 | <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> | 221 | <string name="reset_all_settings_description">Todas as configurações avançadas retornarão ao padrão. Isto não pode ser desfeito.</string> |
| 165 | <string name="settings_reset">Redefinir configurações </string> | 222 | <string name="settings_reset">Redefinir configurações </string> |
| 166 | <string name="close">Fechar</string> | 223 | <string name="close">Fechar</string> |
| 167 | <string name="learn_more">Saber Mais</string> | 224 | <string name="learn_more">Saber mais</string> |
| 225 | <string name="auto">Automático</string> | ||
| 226 | <string name="submit">Enviar</string> | ||
| 227 | <string name="string_null">Nenhum (desativado)</string> | ||
| 228 | <string name="string_import">Importar</string> | ||
| 229 | <string name="export">Exportar</string> | ||
| 230 | <string name="export_failed">Exportação falhada</string> | ||
| 231 | <string name="import_failed">IMportação falhada</string> | ||
| 232 | <string name="cancelling">A cancelar</string> | ||
| 168 | 233 | ||
| 169 | <!-- GPU driver installation --> | 234 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">Seleciona a driver para o GPU</string> | 235 | <string name="select_gpu_driver">Seleciona a driver para o GPU</string> |
| @@ -172,6 +237,7 @@ | |||
| 172 | <string name="select_gpu_driver_install">Instalar</string> | 237 | <string name="select_gpu_driver_install">Instalar</string> |
| 173 | <string name="select_gpu_driver_default">Padrão</string> | 238 | <string name="select_gpu_driver_default">Padrão</string> |
| 174 | <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> | 239 | <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> |
| 240 | <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string> | ||
| 175 | <string name="system_gpu_driver">Driver do GPU padrão</string> | 241 | <string name="system_gpu_driver">Driver do GPU padrão</string> |
| 176 | <string name="installing_driver">A instalar o Driver...</string> | 242 | <string name="installing_driver">A instalar o Driver...</string> |
| 177 | 243 | ||
| @@ -182,10 +248,11 @@ | |||
| 182 | <string name="preferences_graphics">Gráficos</string> | 248 | <string name="preferences_graphics">Gráficos</string> |
| 183 | <string name="preferences_audio">Audio</string> | 249 | <string name="preferences_audio">Audio</string> |
| 184 | <string name="preferences_theme">Cor e tema.</string> | 250 | <string name="preferences_theme">Cor e tema.</string> |
| 251 | <string name="preferences_debug">Depurar</string> | ||
| 185 | 252 | ||
| 186 | <!-- ROM loading errors --> | 253 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">A tua ROM está encriptada</string> | 254 | <string name="loader_error_encrypted">A tua ROM está encriptada</string> |
| 188 | <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> | 255 | <string name="loader_error_encrypted_roms_description"><![CDATA[Por favor, siga os guias para despejar novamente o seu <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">cartucho de jogo</a> or <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">tÃtulos instalados</a>.]]></string> |
| 189 | <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> | 256 | <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> |
| 190 | <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vÃdeo.</string> | 257 | <string name="loader_error_video_core">Ocorreu um erro ao iniciar o núcleo de vÃdeo.</string> |
| 191 | <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> | 258 | <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> |
| @@ -193,28 +260,28 @@ | |||
| 193 | <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string> | 260 | <string name="loader_error_file_not_found">O ficheiro da ROM não existe</string> |
| 194 | 261 | ||
| 195 | <!-- Emulation Menu --> | 262 | <!-- Emulation Menu --> |
| 196 | <string name="emulation_exit">Sair da emulação</string> | 263 | <string name="emulation_exit">Parar emulação</string> |
| 197 | <string name="emulation_done">Feito</string> | 264 | <string name="emulation_done">Feito</string> |
| 198 | <string name="emulation_fps_counter">Contador de FPS</string> | 265 | <string name="emulation_fps_counter">Contador de FPS</string> |
| 199 | <string name="emulation_toggle_controls">Alterar Controlos</string> | 266 | <string name="emulation_toggle_controls">Alterar controles</string> |
| 200 | <string name="emulation_rel_stick_center">Centro do Analógico Relativo</string> | 267 | <string name="emulation_rel_stick_center">Centro Relativo de Analógico</string> |
| 201 | <string name="emulation_dpad_slide">Deslizar do DPad</string> | 268 | <string name="emulation_dpad_slide">Deslizamento dos Botões Direcionais</string> |
| 202 | <string name="emulation_haptics">Hápticos </string> | 269 | <string name="emulation_haptics">Vibração ao tocar</string> |
| 203 | <string name="emulation_show_overlay">Mostrar sobreposição </string> | 270 | <string name="emulation_show_overlay">Mostrar overlay</string> |
| 204 | <string name="emulation_toggle_all">Alterar todos</string> | 271 | <string name="emulation_toggle_all">Marcar/Desmarcar tudo</string> |
| 205 | <string name="emulation_control_adjust">Ajustar a sobreposição </string> | 272 | <string name="emulation_control_adjust">Ajustar overlay</string> |
| 206 | <string name="emulation_control_scale">Escala</string> | 273 | <string name="emulation_control_scale">Escala</string> |
| 207 | <string name="emulation_control_opacity">Opacidade</string> | 274 | <string name="emulation_control_opacity">Opacidade</string> |
| 208 | <string name="emulation_touch_overlay_reset">Redefinir Sobreposição </string> | 275 | <string name="emulation_touch_overlay_reset">Restaurar overlay padrão</string> |
| 209 | <string name="emulation_touch_overlay_edit">Editar sobreposição </string> | 276 | <string name="emulation_touch_overlay_edit">Editar overlay</string> |
| 210 | <string name="emulation_pause">Pausa emulação</string> | 277 | <string name="emulation_pause">Pausar emulação</string> |
| 211 | <string name="emulation_unpause">Retomar emulação</string> | 278 | <string name="emulation_unpause">Despausar emulação</string> |
| 212 | <string name="emulation_input_overlay">Opções de sobreposição </string> | 279 | <string name="emulation_input_overlay">Opções de overlay</string> |
| 213 | 280 | ||
| 214 | <string name="load_settings">Configurações a carregar...</string> | 281 | <string name="load_settings">Carregando configurações...</string> |
| 215 | 282 | ||
| 216 | <!-- Software keyboard --> | 283 | <!-- Software keyboard --> |
| 217 | <string name="software_keyboard">Teclado de Software</string> | 284 | <string name="software_keyboard">Teclado de software</string> |
| 218 | 285 | ||
| 219 | <!-- Errors and warnings --> | 286 | <!-- Errors and warnings --> |
| 220 | <string name="abort_button">Abortar</string> | 287 | <string name="abort_button">Abortar</string> |
| @@ -226,6 +293,9 @@ | |||
| 226 | <string name="fatal_error">Erro fatal</string> | 293 | <string name="fatal_error">Erro fatal</string> |
| 227 | <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string> | 294 | <string name="fatal_error_message">Ocorreu um erro fatal. Verifica o teu registro para detalhes. \nContinuar a emulação pode causar erros.</string> |
| 228 | <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> | 295 | <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> |
| 296 | <string name="device_memory_inadequate">RAM do dispositivo: %1$s\nRecommended: %2$s</string> | ||
| 297 | <string name="memory_formatted">%1$s %2$s</string> | ||
| 298 | <string name="no_game_present">Nenhum jogo inicializável presente!</string> | ||
| 229 | 299 | ||
| 230 | <!-- Region Names --> | 300 | <!-- Region Names --> |
| 231 | <string name="region_japan">Japão</string> | 301 | <string name="region_japan">Japão</string> |
| @@ -236,7 +306,14 @@ | |||
| 236 | <string name="region_korea">Coreia</string> | 306 | <string name="region_korea">Coreia</string> |
| 237 | <string name="region_taiwan">Taiwan</string> | 307 | <string name="region_taiwan">Taiwan</string> |
| 238 | 308 | ||
| 239 | <!-- Language Names --> | 309 | <!-- Memory Sizes --> |
| 310 | <string name="memory_byte">Byte</string> | ||
| 311 | <string name="memory_kilobyte">KB</string> | ||
| 312 | <string name="memory_megabyte">MB</string> | ||
| 313 | <string name="memory_gigabyte">GB</string> | ||
| 314 | <string name="memory_terabyte">TB</string> | ||
| 315 | <string name="memory_petabyte">PB</string> | ||
| 316 | <string name="memory_exabyte">EB</string> | ||
| 240 | 317 | ||
| 241 | <!-- Renderer APIs --> | 318 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">Vulcano</string> | 319 | <string name="renderer_vulkan">Vulcano</string> |
| @@ -274,12 +351,17 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">FXAA</string> | 351 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 275 | <string name="anti_aliasing_smaa">SMAA</string> | 352 | <string name="anti_aliasing_smaa">SMAA</string> |
| 276 | 353 | ||
| 354 | <!-- Screen Layouts --> | ||
| 355 | <string name="screen_layout_landscape">Landscape</string> | ||
| 356 | <string name="screen_layout_portrait">Portrait</string> | ||
| 357 | <string name="screen_layout_auto">Automático</string> | ||
| 358 | |||
| 277 | <!-- Aspect Ratios --> | 359 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">Padrão (16:9)</string> | 360 | <string name="ratio_default">Padrão (16:9)</string> |
| 279 | <string name="ratio_force_four_three">Forçar 4:3</string> | 361 | <string name="ratio_force_four_three">Forçar 4:3</string> |
| 280 | <string name="ratio_force_twenty_one_nine">Forçar 21:9</string> | 362 | <string name="ratio_force_twenty_one_nine">Forçar 21:9</string> |
| 281 | <string name="ratio_force_sixteen_ten">Forçar 16:10</string> | 363 | <string name="ratio_force_sixteen_ten">Forçar 16:10</string> |
| 282 | <string name="ratio_stretch">Esticar à Janela</string> | 364 | <string name="ratio_stretch">Esticar à janela</string> |
| 283 | 365 | ||
| 284 | <!-- CPU Accuracy --> | 366 | <!-- CPU Accuracy --> |
| 285 | <string name="cpu_accuracy_accurate">Preciso</string> | 367 | <string name="cpu_accuracy_accurate">Preciso</string> |
| @@ -287,9 +369,9 @@ | |||
| 287 | <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string> | 369 | <string name="cpu_accuracy_paranoid">Paranoid (Lento)</string> |
| 288 | 370 | ||
| 289 | <!-- Gamepad Buttons --> | 371 | <!-- Gamepad Buttons --> |
| 290 | <string name="gamepad_d_pad">D-Pad</string> | 372 | <string name="gamepad_d_pad">Botões Direcionais</string> |
| 291 | <string name="gamepad_left_stick">Analógico Esquerdo</string> | 373 | <string name="gamepad_left_stick">Analógico esquerdo</string> |
| 292 | <string name="gamepad_right_stick">Analógico Direito</string> | 374 | <string name="gamepad_right_stick">Analógico direito</string> |
| 293 | <string name="gamepad_home">Home</string> | 375 | <string name="gamepad_home">Home</string> |
| 294 | <string name="gamepad_screenshot">Captura de ecrã</string> | 376 | <string name="gamepad_screenshot">Captura de ecrã</string> |
| 295 | 377 | ||
| @@ -298,18 +380,32 @@ | |||
| 298 | <string name="building_shaders">A criar shaders</string> | 380 | <string name="building_shaders">A criar shaders</string> |
| 299 | 381 | ||
| 300 | <!-- Theme options --> | 382 | <!-- Theme options --> |
| 301 | <string name="change_app_theme">Muda o Tema da App</string> | 383 | <string name="change_app_theme">Mudar o tema do aplicativo</string> |
| 302 | <string name="theme_default">Padrão</string> | 384 | <string name="theme_default">Padrão</string> |
| 303 | <string name="theme_material_you">Material You</string> | 385 | <string name="theme_material_you">Material You</string> |
| 304 | 386 | ||
| 305 | <!-- Theme Modes --> | 387 | <!-- Theme Modes --> |
| 306 | <string name="change_theme_mode">Altera o Modo do Tema</string> | 388 | <string name="change_theme_mode">Alterar o tema</string> |
| 307 | <string name="theme_mode_follow_system">Igual ao Sistema</string> | 389 | <string name="theme_mode_follow_system">Igual ao Sistema</string> |
| 308 | <string name="theme_mode_light">Claro</string> | 390 | <string name="theme_mode_light">Claro</string> |
| 309 | <string name="theme_mode_dark">Escuro</string> | 391 | <string name="theme_mode_dark">Escuro</string> |
| 310 | 392 | ||
| 393 | <!-- Audio output engines --> | ||
| 394 | <string name="cubeb">cubeb</string> | ||
| 395 | |||
| 311 | <!-- Black backgrounds theme --> | 396 | <!-- Black backgrounds theme --> |
| 312 | <string name="use_black_backgrounds">Usa Fundos Escuros</string> | 397 | <string name="use_black_backgrounds">Plano de fundo preto</string> |
| 313 | <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string> | 398 | <string name="use_black_backgrounds_description">Quando usar tema escuro, aplicar fundos escuros</string> |
| 314 | 399 | ||
| 315 | </resources> | 400 | <!-- Picture-In-Picture --> |
| 401 | <string name="picture_in_picture">Picture in Picture</string> | ||
| 402 | <string name="picture_in_picture_description">Minimizar a janela quando colocada em segundo plano</string> | ||
| 403 | <string name="pause">Pausa</string> | ||
| 404 | <string name="play">Correr</string> | ||
| 405 | <string name="mute">Mute</string> | ||
| 406 | <string name="unmute">Unmute</string> | ||
| 407 | |||
| 408 | <!-- Licenses screen strings --> | ||
| 409 | <string name="licenses">Licenças</string> | ||
| 410 | <string name="license_fidelityfx_fsr_description">Upscaling de alta qualidade da AMD</string> | ||
| 411 | </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 index 0bef035d6..c614257a8 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Ðто программное обеÑпечение позволÑет запуÑкать игры Ð´Ð»Ñ Ð¸Ð³Ñ€Ð¾Ð²Ð¾Ð¹ конÑоли Nintendo Switch. Мы не предоÑтавлÑем Ñами игры или ключи.<br /><br />Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище уÑтройÑтва..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string> | 4 | <string name="app_disclaimer">Ðто программное обеÑпечение позволÑет запуÑкать игры Ð´Ð»Ñ Ð¸Ð³Ñ€Ð¾Ð²Ð¾Ð¹ конÑоли Nintendo Switch. Мы не предоÑтавлÑем Ñами игры или ключи.<br /><br />Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище уÑтройÑтва..<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">ÐмулÑÑ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð°</string> | 5 | <string name="emulation_notification_channel_name">ÐмулÑÑ†Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð°</string> |
| @@ -7,7 +7,7 @@ | |||
| 7 | <string name="emulation_notification_running">yuzu запущен</string> | 7 | <string name="emulation_notification_running">yuzu запущен</string> |
| 8 | <string name="notice_notification_channel_name">Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¸ ошибки</string> | 8 | <string name="notice_notification_channel_name">Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¸ ошибки</string> |
| 9 | <string name="notice_notification_channel_description">Показывать уведомлениÑ, когда что-то пошло не так</string> | 9 | <string name="notice_notification_channel_description">Показывать уведомлениÑ, когда что-то пошло не так</string> |
| 10 | <string name="notification_permission_not_granted">Ð’Ñ‹ не предоÑтавили разрешение уведомлений!</string> | 10 | <string name="notification_permission_not_granted">Ð’Ñ‹ не предоÑтавили разрешение на уведомлениÑ!</string> |
| 11 | 11 | ||
| 12 | <!-- Setup strings --> | 12 | <!-- Setup strings --> |
| 13 | <string name="welcome">Добро пожаловать!</string> | 13 | <string name="welcome">Добро пожаловать!</string> |
| @@ -25,6 +25,7 @@ | |||
| 25 | <string name="back">Ðазад</string> | 25 | <string name="back">Ðазад</string> |
| 26 | <string name="add_games">Добавить игры</string> | 26 | <string name="add_games">Добавить игры</string> |
| 27 | <string name="add_games_description">Выберите папку Ñ Ð¸Ð³Ñ€Ð°Ð¼Ð¸</string> | 27 | <string name="add_games_description">Выберите папку Ñ Ð¸Ð³Ñ€Ð°Ð¼Ð¸</string> |
| 28 | <string name="step_complete">Выполнено!</string> | ||
| 28 | 29 | ||
| 29 | <!-- Home strings --> | 30 | <!-- Home strings --> |
| 30 | <string name="home_games">Игры</string> | 31 | <string name="home_games">Игры</string> |
| @@ -38,6 +39,7 @@ | |||
| 38 | <string name="add_games_warning_description">Игры не будут отображатьÑÑ Ð² ÑпиÑке Игры, еÑли папка не выбрана.</string> | 39 | <string name="add_games_warning_description">Игры не будут отображатьÑÑ Ð² ÑпиÑке Игры, еÑли папка не выбрана.</string> |
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 40 | <string name="home_search_games">Ðайти игры</string> | 41 | <string name="home_search_games">Ðайти игры</string> |
| 42 | <string name="search_settings">ÐаÑтройки поиÑка</string> | ||
| 41 | <string name="games_dir_selected">Выбрана папка Ñ Ð¸Ð³Ñ€Ð°Ð¼Ð¸</string> | 43 | <string name="games_dir_selected">Выбрана папка Ñ Ð¸Ð³Ñ€Ð°Ð¼Ð¸</string> |
| 42 | <string name="install_prod_keys">УÑтановить prod.keys</string> | 44 | <string name="install_prod_keys">УÑтановить prod.keys</string> |
| 43 | <string name="install_prod_keys_description">ТребуетÑÑ Ð´Ð»Ñ Ñ€Ð°Ñшифровки розничных игр</string> | 45 | <string name="install_prod_keys_description">ТребуетÑÑ Ð´Ð»Ñ Ñ€Ð°Ñшифровки розничных игр</string> |
| @@ -61,14 +63,17 @@ | |||
| 61 | <string name="invalid_keys_file">Выбран неверный файл ключей</string> | 63 | <string name="invalid_keys_file">Выбран неверный файл ключей</string> |
| 62 | <string name="install_keys_success">Ключи уÑпешно уÑтановлены</string> | 64 | <string name="install_keys_success">Ключи уÑпешно уÑтановлены</string> |
| 63 | <string name="reading_keys_failure">Ошибка при чтении ключей шифрованиÑ</string> | 65 | <string name="reading_keys_failure">Ошибка при чтении ключей шифрованиÑ</string> |
| 66 | <string name="install_prod_keys_failure_extension_description">УбедитеÑÑŒ, что файл ключей имеет раÑширение .keys, и повторите попытку.</string> | ||
| 67 | <string name="install_amiibo_keys_failure_extension_description">УбедитеÑÑŒ, что файл ключей имеет раÑширение .bin, и повторите попытку.</string> | ||
| 64 | <string name="invalid_keys_error">Ðеверные ключи шифрованиÑ</string> | 68 | <string name="invalid_keys_error">Ðеверные ключи шифрованиÑ</string> |
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 69 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">Выбранный файл неверен или поврежден. ПожалуйÑта, пере-дампите ваши ключи.</string> | 70 | <string name="install_keys_failure_description">Выбранный файл неверен или поврежден. ПожалуйÑта, пере-дампите ваши ключи.</string> |
| 67 | <string name="install_gpu_driver">УÑтановить драйвер ГП</string> | 71 | <string name="install_gpu_driver">УÑтановить драйвер ГП</string> |
| 68 | <string name="install_gpu_driver_description">УÑтановите альтернативные драйверы Ð´Ð»Ñ Ð¿Ð¾Ñ‚ÐµÐ½Ñ†Ð¸Ð°Ð»ÑŒÐ½Ð¾ лучшей производительноÑти и/или точноÑти</string> | 72 | <string name="install_gpu_driver_description">УÑтановите альтернативные драйверы Ð´Ð»Ñ Ð¿Ð¾Ñ‚ÐµÐ½Ñ†Ð¸Ð°Ð»ÑŒÐ½Ð¾ лучшей производительноÑти и/или точноÑти</string> |
| 69 | <string name="advanced_settings">РаÑширенные наÑтройки</string> | 73 | <string name="advanced_settings">РаÑширенные наÑтройки</string> |
| 74 | <string name="advanced_settings_game">РаÑширенные наÑтройки: %1$s</string> | ||
| 70 | <string name="settings_description">ÐаÑтройка параметров ÑмулÑтора</string> | 75 | <string name="settings_description">ÐаÑтройка параметров ÑмулÑтора</string> |
| 71 | <string name="search_recently_played">Ðедавно Ñыграно</string> | 76 | <string name="search_recently_played">Ðедавно Ñыгранные</string> |
| 72 | <string name="search_recently_added">Ðедавно добавлено</string> | 77 | <string name="search_recently_added">Ðедавно добавлено</string> |
| 73 | <string name="search_retail">Розничные</string> | 78 | <string name="search_retail">Розничные</string> |
| 74 | <string name="search_homebrew">Homebrew</string> | 79 | <string name="search_homebrew">Homebrew</string> |
| @@ -86,6 +91,34 @@ | |||
| 86 | <string name="save_file_invalid_zip_structure_description">Ðазвание первой вложенной папки должно быть идентификатором игры.</string> | 91 | <string name="save_file_invalid_zip_structure_description">Ðазвание первой вложенной папки должно быть идентификатором игры.</string> |
| 87 | <string name="import_saves">Импорт</string> | 92 | <string name="import_saves">Импорт</string> |
| 88 | <string name="export_saves">ÐкÑпорт</string> | 93 | <string name="export_saves">ÐкÑпорт</string> |
| 94 | <string name="install_firmware">УÑтановить прошивку</string> | ||
| 95 | <string name="install_firmware_description">Прошивка должна находитьÑÑ Ð² ZIP-архиве и необходима Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ некоторых игр</string> | ||
| 96 | <string name="firmware_installing">УÑтановка прошивки</string> | ||
| 97 | <string name="firmware_installed_success">Прошивка уÑпешно уÑтановлена</string> | ||
| 98 | <string name="firmware_installed_failure">Ðе удалоÑÑŒ уÑтановить прошивку</string> | ||
| 99 | <string name="firmware_installed_failure_description">УбедитеÑÑŒ что файлы прошивки nca находÑÑ‚ÑÑ Ð² корне zip-архива и повторите попытку.</string> | ||
| 100 | <string name="share_log">ПоделитьÑÑ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð¾Ð¼ отладки</string> | ||
| 101 | <string name="share_log_description">ПоделитьÑÑ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð¾Ð¼ отладки yuzu Ð´Ð»Ñ ÑƒÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼</string> | ||
| 102 | <string name="share_log_missing">Файл журнала не найден</string> | ||
| 103 | <string name="install_game_content">УÑтановить игровой контент</string> | ||
| 104 | <string name="install_game_content_description">УÑтановить Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð³Ñ€Ñ‹ или дополнений</string> | ||
| 105 | <string name="installing_game_content">УÑтановка контента...</string> | ||
| 106 | <string name="install_game_content_failure">Ошибка уÑтановки файл(ов) в NAND.</string> | ||
| 107 | <string name="install_game_content_failure_description">УбедитеÑÑŒ что Ñодержимое допуÑтимо и что файл prod.keys уÑтановлен.</string> | ||
| 108 | <string name="install_game_content_failure_base">УÑтановка базовых игр запрещена во избежание возможных конфликтов.</string> | ||
| 109 | <string name="install_game_content_failure_file_extension">ПоддерживаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ контент NSP и XCI. ПожалуйÑта убедитеÑÑŒ что игровой контент дейÑтвителен.</string> | ||
| 110 | <string name="install_game_content_failed_count">%1$d ошибка уÑтановки</string> | ||
| 111 | <string name="install_game_content_success">Игровой контент уÑпешно уÑтановлен</string> | ||
| 112 | <string name="install_game_content_success_install">%1$d УÑпешно уÑтановлено</string> | ||
| 113 | <string name="install_game_content_success_overwrite">%1$d УÑпешно перезапиÑано</string> | ||
| 114 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 115 | <string name="custom_driver_not_supported">ПользовательÑкие драйверы не поддерживаютÑÑ</string> | ||
| 116 | <string name="custom_driver_not_supported_description">Загрузка пользовательÑкого драйвера в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½Ðµ поддерживаетÑÑ Ð´Ð»Ñ Ñтого уÑтройÑтва.\nПроверьте Ñтот параметр еще раз в будущем чтобы узнать была ли добавлена ​​поддержка! | ||
| 117 | Â </string> | ||
| 118 | <string name="manage_yuzu_data">Управление данными yuzu</string> | ||
| 119 | <string name="manage_yuzu_data_description">Импортируйте/ÑкÑпортируйте прошивку, ключи, пользовательÑкие данные и многое другое!</string> | ||
| 120 | <string name="share_save_file">ПоделитьÑÑ Ñ„Ð°Ð¹Ð»Ð¾Ð¼ ÑохранениÑ</string> | ||
| 121 | <string name="export_save_failed">Ðе удалоÑÑŒ ÑкÑпортировать Ñохранение</string> | ||
| 89 | 122 | ||
| 90 | <!-- About screen strings --> | 123 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">Gaia не ÑущеÑтвует</string> | 124 | <string name="gaia_is_not_real">Gaia не ÑущеÑтвует</string> |
| @@ -94,7 +127,18 @@ | |||
| 94 | <string name="contributors">Контрибьюторы</string> | 127 | <string name="contributors">Контрибьюторы</string> |
| 95 | <string name="contributors_description">Сделано Ñ \u2764 от команды yuzu</string> | 128 | <string name="contributors_description">Сделано Ñ \u2764 от команды yuzu</string> |
| 96 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 129 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 130 | <string name="licenses_description">Проекты, которые Ñделали yuzu Ð´Ð»Ñ Android возможным</string> | ||
| 97 | <string name="build">Сборка</string> | 131 | <string name="build">Сборка</string> |
| 132 | <string name="user_data">Данные пользователÑ</string> | ||
| 133 | <string name="user_data_description">Импортируйте/ÑкÑпортируйте вÑе данные приложениÑ.\n\nПри импорте пользовательÑких данных вÑе ÑущеÑтвующие пользовательÑкие данные будут удалены!</string> | ||
| 134 | <string name="exporting_user_data">ÐкÑпорт пользовательÑких данных…</string> | ||
| 135 | <string name="importing_user_data">Импорт пользовательÑких данных…</string> | ||
| 136 | <string name="import_user_data">Импортировать пользовательÑкие данные</string> | ||
| 137 | <string name="invalid_yuzu_backup">ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ Ñ€ÐµÐ·ÐµÑ€Ð²Ð½Ð°Ñ ÐºÐ¾Ð¿Ð¸Ñ yuzu</string> | ||
| 138 | <string name="user_data_export_success">ПользовательÑкие данные уÑпешно ÑкÑпортированы</string> | ||
| 139 | <string name="user_data_import_success">ПользовательÑкие данные уÑпешно импортированы</string> | ||
| 140 | <string name="user_data_export_cancelled">ÐкÑпорт отменен</string> | ||
| 141 | <string name="user_data_import_failed_description">УбедитеÑÑŒ что папки пользовательÑких данных находÑÑ‚ÑÑ Ð² корне zip-папки и Ñодержат файл конфигурации config/config.ini и повторите попытку.</string> | ||
| 98 | <string name="support_link">https://discord.gg/u77vRWY</string> | 142 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 99 | <string name="website_link">https://yuzu-emu.org/</string> | 143 | <string name="website_link">https://yuzu-emu.org/</string> |
| 100 | <string name="github_link">https://github.com/yuzu-emu</string> | 144 | <string name="github_link">https://github.com/yuzu-emu</string> |
| @@ -114,41 +158,51 @@ | |||
| 114 | <string name="are_you_interested">Ð’Ñ‹ заинтереÑованы?</string> | 158 | <string name="are_you_interested">Ð’Ñ‹ заинтереÑованы?</string> |
| 115 | 159 | ||
| 116 | <!-- General settings strings --> | 160 | <!-- General settings strings --> |
| 117 | <string name="frame_limit_enable">Включить ограничение ÑкороÑти</string> | 161 | <string name="frame_limit_enable">Ограничить ÑкороÑть</string> |
| 118 | <string name="frame_limit_enable_description">ЕÑли Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°, ÑкороÑть ÑмулÑции будет ограничена указанным процентом от нормальной ÑкороÑти.</string> | 162 | <string name="frame_limit_enable_description">Ограничивает ÑкороÑть ÑмулÑции указанным процентом от нормальной ÑкороÑти.</string> |
| 119 | <string name="frame_limit_slider">Ограничение процента cкороÑти</string> | 163 | <string name="frame_limit_slider">Ограничение процента cкороÑти</string> |
| 120 | <string name="frame_limit_slider_description">Указывает процент Ð´Ð»Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ ÑкороÑти ÑмулÑции. При значении по умолчанию 100% ÑмулÑÑ†Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ ограничена нормальной ÑкороÑтью. Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð²Ñ‹ÑˆÐµ или ниже будут увеличивать или уменьшать ограничение ÑкороÑти.</string> | 164 | <string name="frame_limit_slider_description">Указывает процент Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ ÑкороÑти ÑмулÑции. 100% - Ñто Ð½Ð¾Ñ€Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ ÑкороÑть. Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð±Ð¾Ð»ÑŒÑˆÐµ или меньше увеличивают или уменьшают ограничение ÑкороÑти.</string> |
| 121 | <string name="cpu_accuracy">ТочноÑть ЦП</string> | 165 | <string name="cpu_accuracy">ТочноÑть ЦП</string> |
| 166 | <string name="value_with_units">%1$s%2$s</string> | ||
| 122 | 167 | ||
| 123 | <!-- System settings strings --> | 168 | <!-- System settings strings --> |
| 124 | <string name="use_docked_mode">Режим док-Ñтанции</string> | 169 | <string name="use_docked_mode">Режим док-Ñтанции</string> |
| 125 | <string name="use_docked_mode_description">ÐмулÑÑ†Ð¸Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ð° док-Ñтанции, что увеличивает разрешение за Ñчет ÑÐ½Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñти.</string> | 170 | <string name="use_docked_mode_description">Увеличивает разрешение, ÑÐ½Ð¸Ð¶Ð°Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñть. Портативный режим иÑпользуетÑÑ Ð¿Ñ€Ð¸ отключении, ÑÐ½Ð¸Ð¶Ð°Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ðµ и Ð¿Ð¾Ð²Ñ‹ÑˆÐ°Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñть.</string> |
| 126 | <string name="emulated_region">Ðмулируемый регион</string> | 171 | <string name="emulated_region">Регион конÑоли</string> |
| 127 | <string name="emulated_language">Ðмулируемый Ñзык</string> | 172 | <string name="emulated_language">Язык конÑоли</string> |
| 128 | <string name="select_rtc_date">Выберите дату RTC</string> | 173 | <string name="select_rtc_date">Выберите дату RTC</string> |
| 129 | <string name="select_rtc_time">Выберите Ð²Ñ€ÐµÐ¼Ñ RTC</string> | 174 | <string name="select_rtc_time">Выберите Ð²Ñ€ÐµÐ¼Ñ RTC</string> |
| 130 | <string name="use_custom_rtc">Включить пользовательÑкий RTC</string> | 175 | <string name="use_custom_rtc">ПользовательÑкий RTC</string> |
| 131 | <string name="use_custom_rtc_description">Ðтот параметр позволÑет уÑтановить пользовательÑкие чаÑÑ‹ реального времени отдельно от текущего ÑиÑтемного времени</string> | 176 | <string name="use_custom_rtc_description">ПозволÑет уÑтановить пользовательÑкие чаÑÑ‹ реального времени отдельно от текущего ÑиÑтемного времени.</string> |
| 132 | <string name="set_custom_rtc">УÑтановить пользовательÑкий RTC</string> | 177 | <string name="set_custom_rtc">УÑтановить пользовательÑкий RTC</string> |
| 133 | 178 | ||
| 134 | <!-- Graphics settings strings --> | 179 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">API</string> | ||
| 136 | <string name="renderer_accuracy">Уровень точноÑти</string> | 180 | <string name="renderer_accuracy">Уровень точноÑти</string> |
| 137 | <string name="renderer_resolution">Разрешение</string> | 181 | <string name="renderer_resolution">Разрешение (портативное/в док-Ñтанции)</string> |
| 138 | <string name="renderer_vsync">Режим верт. Ñинхронизации</string> | 182 | <string name="renderer_vsync">Режим верт. Ñинхронизации</string> |
| 183 | <string name="renderer_screen_layout">ОриентациÑ</string> | ||
| 139 | <string name="renderer_aspect_ratio">Соотношение Ñторон</string> | 184 | <string name="renderer_aspect_ratio">Соотношение Ñторон</string> |
| 140 | <string name="renderer_scaling_filter">Фильтр адаптации окна</string> | 185 | <string name="renderer_scaling_filter">Фильтр адаптации окна</string> |
| 141 | <string name="renderer_anti_aliasing">Метод ÑглаживаниÑ</string> | 186 | <string name="renderer_anti_aliasing">Метод ÑглаживаниÑ</string> |
| 142 | <string name="renderer_force_max_clock">Принудительно заÑтавить макÑимальную тактовую чаÑтоту (только Ð´Ð»Ñ Adreno)</string> | 187 | <string name="renderer_force_max_clock">Принудительно заÑтавить макÑимальную тактовую чаÑтоту (только Ð´Ð»Ñ Adreno)</string> |
| 143 | <string name="renderer_force_max_clock_description">ЗаÑтавлÑет ГП работать на макÑимально возможных тактовых чаÑтотах (тепловые Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð²Ñе равно будут применÑтьÑÑ).</string> | 188 | <string name="renderer_force_max_clock_description">ЗаÑтавлÑет ГП работать на макÑимально возможных тактовых чаÑтотах (тепловые Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð²Ñе равно будут применÑтьÑÑ).</string> |
| 144 | <string name="renderer_asynchronous_shaders">ИÑпользовать аÑинхронные шейдеры</string> | 189 | <string name="renderer_asynchronous_shaders">ИÑпользовать аÑинхронные шейдеры</string> |
| 145 | <string name="renderer_asynchronous_shaders_description">Компилирует шейдеры аÑинхронно, что уменьшает завиÑаниÑ, но может взамен предоÑтавить визуальные баги.</string> | 190 | <string name="renderer_asynchronous_shaders_description">КомпилÑÑ†Ð¸Ñ ÑˆÐµÐ¹Ð´ÐµÑ€Ð¾Ð² проиÑходит аÑинхронно, что уменьшает завиÑаниÑ, но может привеÑти к поÑвлению багов.</string> |
| 146 | <string name="renderer_debug">Включить отладку графики</string> | 191 | <string name="renderer_reactive_flushing">Ð ÐµÐ°ÐºÑ‚Ð¸Ð²Ð½Ð°Ñ Ð¾Ñ‡Ð¸Ñтка</string> |
| 147 | <string name="renderer_debug_description">ЕÑли включено, графичеÑкий API переходит в более медленный режим отладки</string> | 192 | <string name="renderer_reactive_flushing_description">Повышение точноÑти рендеринга в некоторых играх за Ñчет ÑÐ½Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñти.</string> |
| 148 | <string name="use_disk_shader_cache">ИÑпользовать кÑш шейдеров на диÑке</string> | 193 | <string name="use_disk_shader_cache">КÑш шейдеров на диÑке</string> |
| 149 | <string name="use_disk_shader_cache_description">Уменьшение завиÑаний за Ñчет Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¸ загрузки Ñгенерированных шейдеров на хранилище.</string> | 194 | <string name="use_disk_shader_cache_description">Уменьшение завиÑаний за Ñчет Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¸ загрузки Ñгенерированных шейдеров.</string> |
| 195 | |||
| 196 | <!-- Debug settings strings --> | ||
| 197 | <string name="cpu">ЦП</string> | ||
| 198 | <string name="cpu_debug_mode">Отладка ЦП</string> | ||
| 199 | <string name="cpu_debug_mode_description">Переводит ЦП в режим медленной отладки.</string> | ||
| 200 | <string name="gpu">графичеÑкий процеÑÑор</string> | ||
| 201 | <string name="renderer_api">API</string> | ||
| 202 | <string name="renderer_debug">Отладка графики</string> | ||
| 203 | <string name="renderer_debug_description">Переводит графичеÑкий API в режим медленной отладки.</string> | ||
| 204 | <string name="fastmem">Fastmem</string> | ||
| 150 | 205 | ||
| 151 | <!-- Audio settings strings --> | ||
| 152 | <string name="audio_volume">ГромкоÑть</string> | 206 | <string name="audio_volume">ГромкоÑть</string> |
| 153 | <string name="audio_volume_description">Задает громкоÑть аудиовыхода.</string> | 207 | <string name="audio_volume_description">Задает громкоÑть аудиовыхода.</string> |
| 154 | 208 | ||
| @@ -157,7 +211,9 @@ | |||
| 157 | <string name="ini_saved">Сохраненные наÑтройки</string> | 211 | <string name="ini_saved">Сохраненные наÑтройки</string> |
| 158 | <string name="gameid_saved">ÐаÑтройки Ñохранены Ð´Ð»Ñ %1$s</string> | 212 | <string name="gameid_saved">ÐаÑтройки Ñохранены Ð´Ð»Ñ %1$s</string> |
| 159 | <string name="error_saving">Ошибка ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ %1$s.ini: %2$s</string> | 213 | <string name="error_saving">Ошибка ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ %1$s.ini: %2$s</string> |
| 214 | <string name="unimplemented_menu">Ðереализованное меню</string> | ||
| 160 | <string name="loading">Загрузка...</string> | 215 | <string name="loading">Загрузка...</string> |
| 216 | <string name="shutting_down">Выключение…</string> | ||
| 161 | <string name="reset_setting_confirmation">Хотите ли вы вернуть Ñтот параметр к значению по умолчанию?</string> | 217 | <string name="reset_setting_confirmation">Хотите ли вы вернуть Ñтот параметр к значению по умолчанию?</string> |
| 162 | <string name="reset_to_default">Ð¡Ð±Ñ€Ð¾Ñ Ðº наÑтройкам по умолчанию</string> | 218 | <string name="reset_to_default">Ð¡Ð±Ñ€Ð¾Ñ Ðº наÑтройкам по умолчанию</string> |
| 163 | <string name="reset_all_settings">СброÑить вÑе наÑтройки?</string> | 219 | <string name="reset_all_settings">СброÑить вÑе наÑтройки?</string> |
| @@ -165,6 +221,14 @@ | |||
| 165 | <string name="settings_reset">ÐаÑтройки Ñброшены</string> | 221 | <string name="settings_reset">ÐаÑтройки Ñброшены</string> |
| 166 | <string name="close">Закрыть</string> | 222 | <string name="close">Закрыть</string> |
| 167 | <string name="learn_more">Узнать больше</string> | 223 | <string name="learn_more">Узнать больше</string> |
| 224 | <string name="auto">Ðвто</string> | ||
| 225 | <string name="submit">Отправить</string> | ||
| 226 | <string name="string_null">Null</string> | ||
| 227 | <string name="string_import">Импорт</string> | ||
| 228 | <string name="export">ÐкÑпорт</string> | ||
| 229 | <string name="export_failed">Ошибка ÑкÑпорта</string> | ||
| 230 | <string name="import_failed">Ошибка импортированиÑ</string> | ||
| 231 | <string name="cancelling">ОтменÑÑŽ</string> | ||
| 168 | 232 | ||
| 169 | <!-- GPU driver installation --> | 233 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">Выбрать драйвер ГП</string> | 234 | <string name="select_gpu_driver">Выбрать драйвер ГП</string> |
| @@ -172,6 +236,7 @@ | |||
| 172 | <string name="select_gpu_driver_install">УÑтановить</string> | 236 | <string name="select_gpu_driver_install">УÑтановить</string> |
| 173 | <string name="select_gpu_driver_default">По умолчанию</string> | 237 | <string name="select_gpu_driver_default">По умолчанию</string> |
| 174 | <string name="select_gpu_driver_use_default">ИÑпользуетÑÑ Ñтандартный драйвер ГП </string> | 238 | <string name="select_gpu_driver_use_default">ИÑпользуетÑÑ Ñтандартный драйвер ГП </string> |
| 239 | <string name="select_gpu_driver_error">Выбран неверный драйвер, иÑпользуетÑÑ Ñтандартный ÑиÑтемный!</string> | ||
| 175 | <string name="system_gpu_driver">СиÑтемный драйвер ГП</string> | 240 | <string name="system_gpu_driver">СиÑтемный драйвер ГП</string> |
| 176 | <string name="installing_driver">УÑтановка драйвера...</string> | 241 | <string name="installing_driver">УÑтановка драйвера...</string> |
| 177 | 242 | ||
| @@ -182,10 +247,11 @@ | |||
| 182 | <string name="preferences_graphics">Графика</string> | 247 | <string name="preferences_graphics">Графика</string> |
| 183 | <string name="preferences_audio">Ðудио</string> | 248 | <string name="preferences_audio">Ðудио</string> |
| 184 | <string name="preferences_theme">Тема и цвет</string> | 249 | <string name="preferences_theme">Тема и цвет</string> |
| 250 | <string name="preferences_debug">Отладка</string> | ||
| 185 | 251 | ||
| 186 | <!-- ROM loading errors --> | 252 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">Ваш ROM зашифрованный</string> | 253 | <string name="loader_error_encrypted">Ваш ROM зашифрованный</string> |
| 188 | <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> | 254 | <string name="loader_error_encrypted_roms_description"><![CDATA[Следуйте инÑтрукциÑм, чтобы пере-дампить игровые картриджи <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\"> или <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\"> уÑтановленные игры</a>.]]></string> |
| 189 | <string name="loader_error_encrypted_keys_description"><![CDATA[ПожалуйÑта, убедитеÑÑŒ, что ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> уÑтановлен, чтобы игры можно было раÑшифровать.]]></string> | 255 | <string name="loader_error_encrypted_keys_description"><![CDATA[ПожалуйÑта, убедитеÑÑŒ, что ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> уÑтановлен, чтобы игры можно было раÑшифровать.]]></string> |
| 190 | <string name="loader_error_video_core">Произошла ошибка при инициализации видеоÑдра.</string> | 256 | <string name="loader_error_video_core">Произошла ошибка при инициализации видеоÑдра.</string> |
| 191 | <string name="loader_error_video_core_description">Обычно Ñто вызвано неÑовмеÑтимым драйвером ГП. УÑтановка пользовательÑкого драйвера ГП может решить Ñту проблему.</string> | 257 | <string name="loader_error_video_core_description">Обычно Ñто вызвано неÑовмеÑтимым драйвером ГП. УÑтановка пользовательÑкого драйвера ГП может решить Ñту проблему.</string> |
| @@ -199,17 +265,17 @@ | |||
| 199 | <string name="emulation_toggle_controls">Переключение управлениÑ</string> | 265 | <string name="emulation_toggle_controls">Переключение управлениÑ</string> |
| 200 | <string name="emulation_rel_stick_center">ОтноÑительный центр Ñтика</string> | 266 | <string name="emulation_rel_stick_center">ОтноÑительный центр Ñтика</string> |
| 201 | <string name="emulation_dpad_slide">Слайд креÑтовиной</string> | 267 | <string name="emulation_dpad_slide">Слайд креÑтовиной</string> |
| 202 | <string name="emulation_haptics">Ð¢Ð°ÐºÑ‚Ð¸Ð»ÑŒÐ½Ð°Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð°Ñ ÑвÑзь</string> | 268 | <string name="emulation_haptics">ÐžÐ±Ñ€Ð°Ñ‚Ð½Ð°Ñ ÑвÑзь от нажатий</string> |
| 203 | <string name="emulation_show_overlay">Показать оверлей</string> | 269 | <string name="emulation_show_overlay">Показать оверлей</string> |
| 204 | <string name="emulation_toggle_all">Переключить вÑÑ‘</string> | 270 | <string name="emulation_toggle_all">Переключить вÑÑ‘</string> |
| 205 | <string name="emulation_control_adjust">ÐаÑтроить оверлей</string> | 271 | <string name="emulation_control_adjust">Регулировка оверлеÑ</string> |
| 206 | <string name="emulation_control_scale">МаÑштаб</string> | 272 | <string name="emulation_control_scale">МаÑштаб</string> |
| 207 | <string name="emulation_control_opacity">ÐепрозрачноÑть</string> | 273 | <string name="emulation_control_opacity">ÐепрозрачноÑть</string> |
| 208 | <string name="emulation_touch_overlay_reset">СброÑить оверлей</string> | 274 | <string name="emulation_touch_overlay_reset">СброÑить оверлей</string> |
| 209 | <string name="emulation_touch_overlay_edit">Изменить оверлей</string> | 275 | <string name="emulation_touch_overlay_edit">Редактировать оверлей</string> |
| 210 | <string name="emulation_pause">Пауза ÑмулÑции</string> | 276 | <string name="emulation_pause">Пауза ÑмулÑции</string> |
| 211 | <string name="emulation_unpause">Возобновление ÑмулÑции</string> | 277 | <string name="emulation_unpause">Возобновить ÑмулÑцию</string> |
| 212 | <string name="emulation_input_overlay">ÐаÑтройки оверлеÑ</string> | 278 | <string name="emulation_input_overlay">ÐаÑтройка оверлеÑ</string> |
| 213 | 279 | ||
| 214 | <string name="load_settings">Загрузка наÑтроек...</string> | 280 | <string name="load_settings">Загрузка наÑтроек...</string> |
| 215 | 281 | ||
| @@ -226,6 +292,9 @@ | |||
| 226 | <string name="fatal_error">Ð¤Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°</string> | 292 | <string name="fatal_error">Ð¤Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°</string> |
| 227 | <string name="fatal_error_message">Произошла Ñ„Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. Проверьте журнал Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ð¹ информации.\nПродолжение ÑмулÑции может привеÑти к ÑбоÑм и ошибкам.</string> | 293 | <string name="fatal_error_message">Произошла Ñ„Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. Проверьте журнал Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ð¹ информации.\nПродолжение ÑмулÑции может привеÑти к ÑбоÑм и ошибкам.</string> |
| 228 | <string name="performance_warning">Отключение Ñтой наÑтройки значительно Ñнизит производительноÑть ÑмулÑции! Ð”Ð»Ñ Ð´Ð¾ÑÑ‚Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð½Ð°Ð¸Ð»ÑƒÑ‡ÑˆÐ¸Ñ… результатов рекомендуетÑÑ Ð¾Ñтавить Ñту наÑтройку включенной.</string> | 294 | <string name="performance_warning">Отключение Ñтой наÑтройки значительно Ñнизит производительноÑть ÑмулÑции! Ð”Ð»Ñ Ð´Ð¾ÑÑ‚Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð½Ð°Ð¸Ð»ÑƒÑ‡ÑˆÐ¸Ñ… результатов рекомендуетÑÑ Ð¾Ñтавить Ñту наÑтройку включенной.</string> |
| 295 | <string name="device_memory_inadequate">ÐžÐ¿ÐµÑ€Ð°Ñ‚Ð¸Ð²Ð½Ð°Ñ Ð¿Ð°Ð¼Ñть уÑтройÑтва: %1$s\nРекомендовано: %2$s</string> | ||
| 296 | <string name="memory_formatted">%1$s%2$s</string> | ||
| 297 | <string name="no_game_present">Загрузочной игры нету!</string> | ||
| 229 | 298 | ||
| 230 | <!-- Region Names --> | 299 | <!-- Region Names --> |
| 231 | <string name="region_japan">ЯпониÑ</string> | 300 | <string name="region_japan">ЯпониÑ</string> |
| @@ -236,7 +305,14 @@ | |||
| 236 | <string name="region_korea">КореÑ</string> | 305 | <string name="region_korea">КореÑ</string> |
| 237 | <string name="region_taiwan">Тайвань</string> | 306 | <string name="region_taiwan">Тайвань</string> |
| 238 | 307 | ||
| 239 | <!-- Language Names --> | 308 | <!-- Memory Sizes --> |
| 309 | <string name="memory_byte">Байт</string> | ||
| 310 | <string name="memory_kilobyte">КБ</string> | ||
| 311 | <string name="memory_megabyte">МБ</string> | ||
| 312 | <string name="memory_gigabyte">GB</string> | ||
| 313 | <string name="memory_terabyte">ТБ</string> | ||
| 314 | <string name="memory_petabyte">ПБ</string> | ||
| 315 | <string name="memory_exabyte">ЕВ</string> | ||
| 240 | 316 | ||
| 241 | <!-- Renderer APIs --> | 317 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">Vulkan</string> | 318 | <string name="renderer_vulkan">Vulkan</string> |
| @@ -274,6 +350,11 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">FXAA</string> | 350 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 275 | <string name="anti_aliasing_smaa">SMAA</string> | 351 | <string name="anti_aliasing_smaa">SMAA</string> |
| 276 | 352 | ||
| 353 | <!-- Screen Layouts --> | ||
| 354 | <string name="screen_layout_landscape">Пейзаж</string> | ||
| 355 | <string name="screen_layout_portrait">Портрет</string> | ||
| 356 | <string name="screen_layout_auto">Ðвто</string> | ||
| 357 | |||
| 277 | <!-- Aspect Ratios --> | 358 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">Стандартное (16:9)</string> | 359 | <string name="ratio_default">Стандартное (16:9)</string> |
| 279 | <string name="ratio_force_four_three">ЗаÑтавить 4:3</string> | 360 | <string name="ratio_force_four_three">ЗаÑтавить 4:3</string> |
| @@ -288,8 +369,8 @@ | |||
| 288 | 369 | ||
| 289 | <!-- Gamepad Buttons --> | 370 | <!-- Gamepad Buttons --> |
| 290 | <string name="gamepad_d_pad">КреÑтовина</string> | 371 | <string name="gamepad_d_pad">КреÑтовина</string> |
| 291 | <string name="gamepad_left_stick">Левый мини-джойÑтик</string> | 372 | <string name="gamepad_left_stick">Левый Ñтик</string> |
| 292 | <string name="gamepad_right_stick">Правый мини-джойÑтик</string> | 373 | <string name="gamepad_right_stick">Правый Ñтик</string> |
| 293 | <string name="gamepad_home">Home</string> | 374 | <string name="gamepad_home">Home</string> |
| 294 | <string name="gamepad_screenshot">Скриншот</string> | 375 | <string name="gamepad_screenshot">Скриншот</string> |
| 295 | 376 | ||
| @@ -298,18 +379,32 @@ | |||
| 298 | <string name="building_shaders">ПоÑтройка шейдеров</string> | 379 | <string name="building_shaders">ПоÑтройка шейдеров</string> |
| 299 | 380 | ||
| 300 | <!-- Theme options --> | 381 | <!-- Theme options --> |
| 301 | <string name="change_app_theme">Изменить тему приложениÑ</string> | 382 | <string name="change_app_theme">Сменить тему</string> |
| 302 | <string name="theme_default">По умолчанию</string> | 383 | <string name="theme_default">По умолчанию</string> |
| 303 | <string name="theme_material_you">Material You</string> | 384 | <string name="theme_material_you">Material You</string> |
| 304 | 385 | ||
| 305 | <!-- Theme Modes --> | 386 | <!-- Theme Modes --> |
| 306 | <string name="change_theme_mode">Изменить режим темы</string> | 387 | <string name="change_theme_mode">Сменить режим темы</string> |
| 307 | <string name="theme_mode_follow_system">СиÑтемнаÑ</string> | 388 | <string name="theme_mode_follow_system">СиÑтемнаÑ</string> |
| 308 | <string name="theme_mode_light">СветлаÑ</string> | 389 | <string name="theme_mode_light">СветлаÑ</string> |
| 309 | <string name="theme_mode_dark">ТемнаÑ</string> | 390 | <string name="theme_mode_dark">ТемнаÑ</string> |
| 310 | 391 | ||
| 392 | <!-- Audio output engines --> | ||
| 393 | <string name="cubeb">cubeb</string> | ||
| 394 | |||
| 311 | <!-- Black backgrounds theme --> | 395 | <!-- Black backgrounds theme --> |
| 312 | <string name="use_black_backgrounds">ИÑпользовать черный фон</string> | 396 | <string name="use_black_backgrounds">Чёрный фон</string> |
| 313 | <string name="use_black_backgrounds_description">При иÑпользовании темной темы применÑйте черный фон.</string> | 397 | <string name="use_black_backgrounds_description">При иÑпользовании темной темы применÑйте черный фон.</string> |
| 314 | 398 | ||
| 315 | </resources> | 399 | <!-- Picture-In-Picture --> |
| 400 | <string name="picture_in_picture">Картинка в картинке</string> | ||
| 401 | <string name="picture_in_picture_description">Свернуть окно при размещении в фоновом режиме</string> | ||
| 402 | <string name="pause">Пауза</string> | ||
| 403 | <string name="play">Играть</string> | ||
| 404 | <string name="mute">Выключить звук</string> | ||
| 405 | <string name="unmute">Включить звук</string> | ||
| 406 | |||
| 407 | <!-- Licenses screen strings --> | ||
| 408 | <string name="licenses">Лицензии</string> | ||
| 409 | <string name="license_fidelityfx_fsr_description">Ð’Ñ‹ÑококачеÑтвенное маÑштабирование от AMD</string> | ||
| 410 | </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 index 5b789ee98..34809dbb8 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">Це програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»ÑÑ” запуÑкати ігри Ð´Ð»Ñ Ñ–Ð³Ñ€Ð¾Ð²Ð¾Ñ— конÑолі Nintendo Switch. Ми не надаємо Ñамі ігри або ключі.<br /><br />Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у Ñховищі приÑтрою.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ</a>]]></string> | 4 | <string name="app_disclaimer">Це програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»ÑÑ” запуÑкати ігри Ð´Ð»Ñ Ñ–Ð³Ñ€Ð¾Ð²Ð¾Ñ— конÑолі Nintendo Switch. Ми не надаємо Ñамі ігри або ключі.<br /><br />Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у Ñховищі приÑтрою.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">ЕмулÑÑ†Ñ–Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð°</string> | 5 | <string name="emulation_notification_channel_name">ЕмулÑÑ†Ñ–Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð°</string> |
| @@ -25,7 +25,6 @@ | |||
| 25 | <string name="back">Ðазад</string> | 25 | <string name="back">Ðазад</string> |
| 26 | <string name="add_games">Додати ігри</string> | 26 | <string name="add_games">Додати ігри</string> |
| 27 | <string name="add_games_description">Виберіть папку з іграми</string> | 27 | <string name="add_games_description">Виберіть папку з іграми</string> |
| 28 | |||
| 29 | <!-- Home strings --> | 28 | <!-- Home strings --> |
| 30 | <string name="home_games">Ігри</string> | 29 | <string name="home_games">Ігри</string> |
| 31 | <string name="home_search">Пошук</string> | 30 | <string name="home_search">Пошук</string> |
| @@ -61,6 +60,7 @@ | |||
| 61 | <string name="invalid_keys_file">Вибрано неправильний файл ключів</string> | 60 | <string name="invalid_keys_file">Вибрано неправильний файл ключів</string> |
| 62 | <string name="install_keys_success">Ключі уÑпішно вÑтановлено</string> | 61 | <string name="install_keys_success">Ключі уÑпішно вÑтановлено</string> |
| 63 | <string name="reading_keys_failure">Помилка під Ñ‡Ð°Ñ Ð·Ñ‡Ð¸Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ»ÑŽÑ‡Ñ–Ð² шифруваннÑ</string> | 62 | <string name="reading_keys_failure">Помилка під Ñ‡Ð°Ñ Ð·Ñ‡Ð¸Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ»ÑŽÑ‡Ñ–Ð² шифруваннÑ</string> |
| 63 | <string name="install_prod_keys_failure_extension_description">ПереконайтеÑÑ, що файл ключів має Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ .keys, Ñ– повторіть Ñпробу.</string> | ||
| 64 | <string name="invalid_keys_error">Ðевірні ключі шифруваннÑ</string> | 64 | <string name="invalid_keys_error">Ðевірні ключі шифруваннÑ</string> |
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">Обраний файл невірний або пошкоджений. Будь лаÑка, пере-дампіть ваші ключі.</string> | 66 | <string name="install_keys_failure_description">Обраний файл невірний або пошкоджений. Будь лаÑка, пере-дампіть ваші ключі.</string> |
| @@ -68,8 +68,6 @@ | |||
| 68 | <string name="install_gpu_driver_description">Ð’Ñтановіть альтернативні драйвери Ð´Ð»Ñ Ð¿Ð¾Ñ‚ÐµÐ½Ñ†Ñ–Ð¹Ð½Ð¾ кращої продуктивноÑті та/або точноÑті</string> | 68 | <string name="install_gpu_driver_description">Ð’Ñтановіть альтернативні драйвери Ð´Ð»Ñ Ð¿Ð¾Ñ‚ÐµÐ½Ñ†Ñ–Ð¹Ð½Ð¾ кращої продуктивноÑті та/або точноÑті</string> |
| 69 | <string name="advanced_settings">Розширені налаштуваннÑ</string> | 69 | <string name="advanced_settings">Розширені налаштуваннÑ</string> |
| 70 | <string name="settings_description">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ñ–Ð² емулÑтора</string> | 70 | <string name="settings_description">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ñ–Ð² емулÑтора</string> |
| 71 | <string name="search_recently_played">Ðещодавно зіграно</string> | ||
| 72 | <string name="search_recently_added">Ðещодавно додано</string> | ||
| 73 | <string name="search_retail">Роздрібні</string> | 71 | <string name="search_retail">Роздрібні</string> |
| 74 | <string name="search_homebrew">Homebrew</string> | 72 | <string name="search_homebrew">Homebrew</string> |
| 75 | <string name="open_user_folder">Відкрити папку yuzu</string> | 73 | <string name="open_user_folder">Відкрити папку yuzu</string> |
| @@ -86,7 +84,6 @@ | |||
| 86 | <string name="save_file_invalid_zip_structure_description">Ðазва першої вкладеної папки має бути ідентифікатором гри.</string> | 84 | <string name="save_file_invalid_zip_structure_description">Ðазва першої вкладеної папки має бути ідентифікатором гри.</string> |
| 87 | <string name="import_saves">Імпорт</string> | 85 | <string name="import_saves">Імпорт</string> |
| 88 | <string name="export_saves">ЕкÑпорт</string> | 86 | <string name="export_saves">ЕкÑпорт</string> |
| 89 | |||
| 90 | <!-- About screen strings --> | 87 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">Gaia не Ñ–Ñнує</string> | 88 | <string name="gaia_is_not_real">Gaia не Ñ–Ñнує</string> |
| 92 | <string name="copied_to_clipboard">Скопійовано в буфер обміну</string> | 89 | <string name="copied_to_clipboard">Скопійовано в буфер обміну</string> |
| @@ -113,42 +110,20 @@ | |||
| 113 | <string name="our_eternal_gratitude">Ðаша неÑкінченна вдÑчніÑть</string> | 110 | <string name="our_eternal_gratitude">Ðаша неÑкінченна вдÑчніÑть</string> |
| 114 | <string name="are_you_interested">Ви зацікавлені?</string> | 111 | <string name="are_you_interested">Ви зацікавлені?</string> |
| 115 | 112 | ||
| 116 | <!-- General settings strings --> | ||
| 117 | <string name="frame_limit_enable">Увімкнути Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÐ¾Ñті</string> | ||
| 118 | <string name="frame_limit_enable_description">Якщо цю функцію ввімкнено, швидкіÑть емулÑції буде обмежена зазначеним відÑотком від нормальної швидкоÑті.</string> | ||
| 119 | <string name="frame_limit_slider">ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ñотка швидкоÑті</string> | 113 | <string name="frame_limit_slider">ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ñотка швидкоÑті</string> |
| 120 | <string name="frame_limit_slider_description">Вказує відÑоток Ð´Ð»Ñ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÐ¾Ñті емулÑції. При значенні за замовчуваннÑм 100% емулÑÑ†Ñ–Ñ Ð±ÑƒÐ´Ðµ обмежена нормальною швидкіÑтю. Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð¸Ñ‰Ðµ або нижче збільшуватимуть або зменшуватимуть Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÐ¾Ñті.</string> | ||
| 121 | <string name="cpu_accuracy">ТочніÑть ЦП</string> | 114 | <string name="cpu_accuracy">ТочніÑть ЦП</string> |
| 122 | |||
| 123 | <!-- System settings strings --> | ||
| 124 | <string name="use_docked_mode">Режим док-Ñтанції</string> | ||
| 125 | <string name="use_docked_mode_description">ЕмулÑÑ†Ñ–Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ñƒ док-Ñтанції, що збільшує роздільну здатніÑть за рахунок Ð·Ð½Ð¸Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾Ñті.</string> | ||
| 126 | <string name="emulated_region">Емульований регіон</string> | 115 | <string name="emulated_region">Емульований регіон</string> |
| 127 | <string name="emulated_language">Емульована мова</string> | 116 | <string name="emulated_language">Емульована мова</string> |
| 128 | <string name="select_rtc_date">Оберіть дату RTC</string> | 117 | <string name="use_custom_rtc">КориÑтувацький RTC</string> |
| 129 | <string name="select_rtc_time">Оберіть Ñ‡Ð°Ñ RTC</string> | ||
| 130 | <string name="use_custom_rtc">Увімкнути кориÑтувацький RTC</string> | ||
| 131 | <string name="use_custom_rtc_description">Цей параметр дає змогу вÑтановити кориÑтувацький годинник реального чаÑу окремо від поточного ÑиÑтемного чаÑу</string> | ||
| 132 | <string name="set_custom_rtc">Ð’Ñтановити кориÑтувацький RTC</string> | ||
| 133 | |||
| 134 | <!-- Graphics settings strings --> | 118 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">API</string> | ||
| 136 | <string name="renderer_accuracy">Рівень точноÑті</string> | 119 | <string name="renderer_accuracy">Рівень точноÑті</string> |
| 137 | <string name="renderer_resolution">Роздільна здатніÑть</string> | ||
| 138 | <string name="renderer_vsync">Режим верт. Ñинхронізації</string> | 120 | <string name="renderer_vsync">Режим верт. Ñинхронізації</string> |
| 139 | <string name="renderer_aspect_ratio">Ð¡Ð¿Ñ–Ð²Ð²Ñ–Ð´Ð½Ð¾ÑˆÐµÐ½Ð½Ñ Ñторін</string> | ||
| 140 | <string name="renderer_scaling_filter">Фільтр адаптації вікна</string> | ||
| 141 | <string name="renderer_anti_aliasing">Метод згладжуваннÑ</string> | ||
| 142 | <string name="renderer_force_max_clock">ПримуÑово змуÑити макÑимальну тактову чаÑтоту (тільки Ð´Ð»Ñ Adreno)</string> | 121 | <string name="renderer_force_max_clock">ПримуÑово змуÑити макÑимальну тактову чаÑтоту (тільки Ð´Ð»Ñ Adreno)</string> |
| 143 | <string name="renderer_force_max_clock_description">Змушує ГП працювати на макÑимально можливих тактових чаÑтотах (теплові Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð²Ñе одно будуть заÑтоÑовуватиÑÑ).</string> | 122 | <string name="renderer_force_max_clock_description">Змушує ГП працювати на макÑимально можливих тактових чаÑтотах (теплові Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð²Ñе одно будуть заÑтоÑовуватиÑÑ).</string> |
| 144 | <string name="renderer_asynchronous_shaders">ВикориÑтовувати аÑинхронні шейдери</string> | 123 | <string name="renderer_asynchronous_shaders">ВикориÑтовувати аÑинхронні шейдери</string> |
| 145 | <string name="renderer_asynchronous_shaders_description">Компілює шейдери аÑинхронно, що зменшує завиÑаннÑ, але може натоміÑть надати візуальні баги.</string> | 124 | <!-- Debug settings strings --> |
| 146 | <string name="renderer_debug">Увімкнути Ð½Ð°Ð»Ð°Ð³Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ð³Ñ€Ð°Ñ„Ñ–ÐºÐ¸</string> | 125 | <string name="cpu">ЦП</string> |
| 147 | <string name="renderer_debug_description">Якщо увімкнено, графічний API переходить у повільніший режим налагодженнÑ</string> | 126 | <string name="renderer_api">API</string> |
| 148 | <string name="use_disk_shader_cache">ВикориÑтовувати кеш шейдерів на диÑку</string> | ||
| 149 | <string name="use_disk_shader_cache_description">Ð—Ð¼ÐµÐ½ÑˆÐµÐ½Ð½Ñ Ð·Ð°Ð²Ð¸Ñань завдÑки зберіганню та завантаженню згенерованих шейдерів на Ñховище.</string> | ||
| 150 | |||
| 151 | <!-- Audio settings strings --> | ||
| 152 | <string name="audio_volume">ГучніÑть</string> | 127 | <string name="audio_volume">ГучніÑть</string> |
| 153 | <string name="audio_volume_description">Вказує гучніÑть аудіовиходу.</string> | 128 | <string name="audio_volume_description">Вказує гучніÑть аудіовиходу.</string> |
| 154 | 129 | ||
| @@ -161,17 +136,20 @@ | |||
| 161 | <string name="reset_setting_confirmation">Чи хочете ви повернути цей параметр до Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð° замовчуваннÑм?</string> | 136 | <string name="reset_setting_confirmation">Чи хочете ви повернути цей параметр до Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð° замовчуваннÑм?</string> |
| 162 | <string name="reset_to_default">Ð¡ÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð´Ð¾ налаштувань за замовчуваннÑм</string> | 137 | <string name="reset_to_default">Ð¡ÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð´Ð¾ налаштувань за замовчуваннÑм</string> |
| 163 | <string name="reset_all_settings">Скинути вÑÑ– налаштуваннÑ</string> | 138 | <string name="reset_all_settings">Скинути вÑÑ– налаштуваннÑ</string> |
| 164 | <string name="reset_all_settings_description">УÑÑ– додаткові Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ Ñкинуто до Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð° замовчуваннÑм. Це неможливо ÑкаÑувати.</string> | ||
| 165 | <string name="settings_reset">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñкинуто</string> | 139 | <string name="settings_reset">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñкинуто</string> |
| 166 | <string name="close">Закрити</string> | 140 | <string name="close">Закрити</string> |
| 167 | <string name="learn_more">ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ</string> | 141 | <string name="learn_more">ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ</string> |
| 168 | 142 | <string name="auto">Ðвто</string> | |
| 143 | <string name="string_null">Null</string> | ||
| 144 | <string name="string_import">Імпорт</string> | ||
| 145 | <string name="export">ЕкÑпорт</string> | ||
| 169 | <!-- GPU driver installation --> | 146 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">Вибрати драйвер ГП</string> | 147 | <string name="select_gpu_driver">Вибрати драйвер ГП</string> |
| 171 | <string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string> | 148 | <string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string> |
| 172 | <string name="select_gpu_driver_install">Ð’Ñтановити</string> | 149 | <string name="select_gpu_driver_install">Ð’Ñтановити</string> |
| 173 | <string name="select_gpu_driver_default">За замовчуваннÑм</string> | 150 | <string name="select_gpu_driver_default">За замовчуваннÑм</string> |
| 174 | <string name="select_gpu_driver_use_default">ВикориÑтовуєтьÑÑ Ñтандартний драйвер ГП</string> | 151 | <string name="select_gpu_driver_use_default">ВикориÑтовуєтьÑÑ Ñтандартний драйвер ГП</string> |
| 152 | <string name="select_gpu_driver_error">Обрано неправильний драйвер, викориÑтовуєтьÑÑ Ñтандартний ÑиÑтемний!</string> | ||
| 175 | <string name="system_gpu_driver">СиÑтемний драйвер ГП</string> | 153 | <string name="system_gpu_driver">СиÑтемний драйвер ГП</string> |
| 176 | <string name="installing_driver">Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ñ€Ð°Ð¹Ð²ÐµÑ€Ð°...</string> | 154 | <string name="installing_driver">Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ñ€Ð°Ð¹Ð²ÐµÑ€Ð°...</string> |
| 177 | 155 | ||
| @@ -182,40 +160,19 @@ | |||
| 182 | <string name="preferences_graphics">Графіка</string> | 160 | <string name="preferences_graphics">Графіка</string> |
| 183 | <string name="preferences_audio">Ðудіо</string> | 161 | <string name="preferences_audio">Ðудіо</string> |
| 184 | <string name="preferences_theme">Тема і колір</string> | 162 | <string name="preferences_theme">Тема і колір</string> |
| 163 | <string name="preferences_debug">ÐалагодженнÑ</string> | ||
| 185 | 164 | ||
| 186 | <!-- ROM loading errors --> | 165 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">Ваш ROM зашифрований</string> | 166 | <string name="loader_error_encrypted">Ваш ROM зашифрований</string> |
| 188 | <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> | ||
| 189 | <string name="loader_error_encrypted_keys_description"><![CDATA[Будь лаÑка, переконайтеÑÑ, що ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> вÑтановлено, щоб ігри можна було розшифрувати.]]></string> | 167 | <string name="loader_error_encrypted_keys_description"><![CDATA[Будь лаÑка, переконайтеÑÑ, що ваш файл <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> вÑтановлено, щоб ігри можна було розшифрувати.]]></string> |
| 190 | <string name="loader_error_video_core">СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ñ–Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ— відеоÑдра.</string> | 168 | <string name="loader_error_video_core">СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ñ–Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ— відеоÑдра.</string> |
| 191 | <string name="loader_error_video_core_description">Зазвичай це Ñпричинено неÑуміÑним драйвером ГП. Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувацького драйвера ГП може вирішити цю проблему.</string> | 169 | <string name="loader_error_video_core_description">Зазвичай це Ñпричинено неÑуміÑним драйвером ГП. Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувацького драйвера ГП може вирішити цю проблему.</string> |
| 192 | <string name="loader_error_invalid_format">Ðе вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити ROM</string> | 170 | <string name="loader_error_invalid_format">Ðе вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити ROM</string> |
| 193 | <string name="loader_error_file_not_found">Файл ROM не Ñ–Ñнує</string> | 171 | <string name="loader_error_file_not_found">Файл ROM не Ñ–Ñнує</string> |
| 194 | 172 | ||
| 195 | <!-- Emulation Menu --> | ||
| 196 | <string name="emulation_exit">Вихід з емулÑції</string> | ||
| 197 | <string name="emulation_done">Готово</string> | 173 | <string name="emulation_done">Готово</string> |
| 198 | <string name="emulation_fps_counter">Лічильник FPS</string> | ||
| 199 | <string name="emulation_toggle_controls">ÐŸÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ð½Ð½Ñ ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ</string> | ||
| 200 | <string name="emulation_rel_stick_center">ВідноÑний центр Ñтіка</string> | ||
| 201 | <string name="emulation_dpad_slide">Слайд хреÑтовиною</string> | ||
| 202 | <string name="emulation_haptics">Тактильний зворотний зв\'Ñзок</string> | ||
| 203 | <string name="emulation_show_overlay">Показати оверлей</string> | ||
| 204 | <string name="emulation_toggle_all">Перемкнути вÑе</string> | ||
| 205 | <string name="emulation_control_adjust">Ðалаштувати оверлей</string> | ||
| 206 | <string name="emulation_control_scale">МаÑштаб</string> | 174 | <string name="emulation_control_scale">МаÑштаб</string> |
| 207 | <string name="emulation_control_opacity">ÐепрозоріÑть</string> | 175 | <string name="emulation_control_opacity">ÐепрозоріÑть</string> |
| 208 | <string name="emulation_touch_overlay_reset">Скинути оверлей</string> | ||
| 209 | <string name="emulation_touch_overlay_edit">Змінити оверлей</string> | ||
| 210 | <string name="emulation_pause">Пауза емулÑції</string> | ||
| 211 | <string name="emulation_unpause">Ð’Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÐµÐ¼ÑƒÐ»Ñції</string> | ||
| 212 | <string name="emulation_input_overlay">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð²ÐµÑ€Ð»ÐµÑ</string> | ||
| 213 | |||
| 214 | <string name="load_settings">Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½ÑŒ...</string> | ||
| 215 | |||
| 216 | <!-- Software keyboard --> | ||
| 217 | <string name="software_keyboard">Віртуальна клавіатура</string> | ||
| 218 | |||
| 219 | <!-- Errors and warnings --> | 176 | <!-- Errors and warnings --> |
| 220 | <string name="abort_button">Перервати</string> | 177 | <string name="abort_button">Перервати</string> |
| 221 | <string name="continue_button">Продовжити</string> | 178 | <string name="continue_button">Продовжити</string> |
| @@ -226,7 +183,6 @@ | |||
| 226 | <string name="fatal_error">Фатальна помилка</string> | 183 | <string name="fatal_error">Фатальна помилка</string> |
| 227 | <string name="fatal_error_message">СталаÑÑ Ñ„Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð° помилка. Перевірте журнал Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾ÐºÐ»Ð°Ð´Ð½Ð¾Ñ— інформації.\nÐŸÑ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ ÐµÐ¼ÑƒÐ»Ñції може призвеÑти до збоїв Ñ– помилок.</string> | 184 | <string name="fatal_error_message">СталаÑÑ Ñ„Ð°Ñ‚Ð°Ð»ÑŒÐ½Ð° помилка. Перевірте журнал Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾ÐºÐ»Ð°Ð´Ð½Ð¾Ñ— інформації.\nÐŸÑ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ ÐµÐ¼ÑƒÐ»Ñції може призвеÑти до збоїв Ñ– помилок.</string> |
| 228 | <string name="performance_warning">Ð’Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð½Ð°Ñ‡Ð½Ð¾ знизить продуктивніÑть емулÑції! Ð”Ð»Ñ Ð´Ð¾ÑÑÐ³Ð½ÐµÐ½Ð½Ñ Ð½Ð°Ð¹ÐºÑ€Ð°Ñ‰Ð¸Ñ… результатів рекомендуєтьÑÑ Ð·Ð°Ð»Ð¸ÑˆÐ¸Ñ‚Ð¸ це Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¸Ð¼.</string> | 185 | <string name="performance_warning">Ð’Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð½Ð°Ñ‡Ð½Ð¾ знизить продуктивніÑть емулÑції! Ð”Ð»Ñ Ð´Ð¾ÑÑÐ³Ð½ÐµÐ½Ð½Ñ Ð½Ð°Ð¹ÐºÑ€Ð°Ñ‰Ð¸Ñ… результатів рекомендуєтьÑÑ Ð·Ð°Ð»Ð¸ÑˆÐ¸Ñ‚Ð¸ це Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¸Ð¼.</string> |
| 229 | |||
| 230 | <!-- Region Names --> | 186 | <!-- Region Names --> |
| 231 | <string name="region_japan">ЯпоніÑ</string> | 187 | <string name="region_japan">ЯпоніÑ</string> |
| 232 | <string name="region_usa">СШÐ</string> | 188 | <string name="region_usa">СШÐ</string> |
| @@ -236,8 +192,7 @@ | |||
| 236 | <string name="region_korea">КореÑ</string> | 192 | <string name="region_korea">КореÑ</string> |
| 237 | <string name="region_taiwan">Тайвань</string> | 193 | <string name="region_taiwan">Тайвань</string> |
| 238 | 194 | ||
| 239 | <!-- Language Names --> | 195 | <string name="memory_gigabyte">GB</string> |
| 240 | |||
| 241 | <!-- Renderer APIs --> | 196 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">Vulkan</string> | 197 | <string name="renderer_vulkan">Vulkan</string> |
| 243 | <string name="renderer_none">Вимкнено</string> | 198 | <string name="renderer_none">Вимкнено</string> |
| @@ -274,22 +229,18 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">FXAA</string> | 229 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 275 | <string name="anti_aliasing_smaa">SMAA</string> | 230 | <string name="anti_aliasing_smaa">SMAA</string> |
| 276 | 231 | ||
| 232 | <string name="screen_layout_auto">Ðвто</string> | ||
| 233 | |||
| 277 | <!-- Aspect Ratios --> | 234 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">За замовчуваннÑм (16:9)</string> | 235 | <string name="ratio_default">За замовчуваннÑм (16:9)</string> |
| 279 | <string name="ratio_force_four_three">ЗмуÑити 4:3</string> | 236 | <string name="ratio_force_four_three">ЗмуÑити 4:3</string> |
| 280 | <string name="ratio_force_twenty_one_nine">ЗмуÑити 21:9</string> | 237 | <string name="ratio_force_twenty_one_nine">ЗмуÑити 21:9</string> |
| 281 | <string name="ratio_force_sixteen_ten">ЗмуÑити 16:10</string> | 238 | <string name="ratio_force_sixteen_ten">ЗмуÑити 16:10</string> |
| 282 | <string name="ratio_stretch">РозтÑгнути до вікна</string> | ||
| 283 | |||
| 284 | <!-- CPU Accuracy --> | 239 | <!-- CPU Accuracy --> |
| 285 | <string name="cpu_accuracy_accurate">Точно</string> | 240 | <string name="cpu_accuracy_accurate">Точно</string> |
| 286 | <string name="cpu_accuracy_unsafe">Ðебезпечно</string> | 241 | <string name="cpu_accuracy_unsafe">Ðебезпечно</string> |
| 287 | <string name="cpu_accuracy_paranoid">Параноїк (повільно)</string> | 242 | <string name="cpu_accuracy_paranoid">Параноїк (повільно)</string> |
| 288 | 243 | ||
| 289 | <!-- Gamepad Buttons --> | ||
| 290 | <string name="gamepad_d_pad">Кнопки напрÑмків</string> | ||
| 291 | <string name="gamepad_left_stick">Лівий міні-джойÑтик</string> | ||
| 292 | <string name="gamepad_right_stick">Правий міні-джойÑтик</string> | ||
| 293 | <string name="gamepad_home">Home</string> | 244 | <string name="gamepad_home">Home</string> |
| 294 | <string name="gamepad_screenshot">Знімок екрану</string> | 245 | <string name="gamepad_screenshot">Знімок екрану</string> |
| 295 | 246 | ||
| @@ -297,19 +248,16 @@ | |||
| 297 | <string name="preparing_shaders">Підготовка шейдерів</string> | 248 | <string name="preparing_shaders">Підготовка шейдерів</string> |
| 298 | <string name="building_shaders">Побудова шейдерів</string> | 249 | <string name="building_shaders">Побудова шейдерів</string> |
| 299 | 250 | ||
| 300 | <!-- Theme options --> | ||
| 301 | <string name="change_app_theme">Змінити тему заÑтоÑунку</string> | ||
| 302 | <string name="theme_default">За замовчуваннÑм</string> | 251 | <string name="theme_default">За замовчуваннÑм</string> |
| 303 | <string name="theme_material_you">Material You</string> | 252 | <string name="theme_material_you">Material You</string> |
| 304 | 253 | ||
| 305 | <!-- Theme Modes --> | ||
| 306 | <string name="change_theme_mode">Змінити режим теми</string> | ||
| 307 | <string name="theme_mode_follow_system">СиÑтемна</string> | 254 | <string name="theme_mode_follow_system">СиÑтемна</string> |
| 308 | <string name="theme_mode_light">Світла</string> | 255 | <string name="theme_mode_light">Світла</string> |
| 309 | <string name="theme_mode_dark">Темна</string> | 256 | <string name="theme_mode_dark">Темна</string> |
| 310 | 257 | ||
| 311 | <!-- Black backgrounds theme --> | ||
| 312 | <string name="use_black_backgrounds">ВикориÑтовувати чорне тло</string> | ||
| 313 | <string name="use_black_backgrounds_description">У разі викориÑÑ‚Ð°Ð½Ð½Ñ Ñ‚ÐµÐ¼Ð½Ð¾Ñ— теми заÑтоÑовуйте чорне тло.</string> | 258 | <string name="use_black_backgrounds_description">У разі викориÑÑ‚Ð°Ð½Ð½Ñ Ñ‚ÐµÐ¼Ð½Ð¾Ñ— теми заÑтоÑовуйте чорне тло.</string> |
| 314 | 259 | ||
| 315 | </resources> | 260 | <string name="mute">Вимкнути звук</string> |
| 261 | <string name="unmute">Увімкнути звук</string> | ||
| 262 | |||
| 263 | </resources> | ||
diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml new file mode 100644 index 000000000..f977db3a2 --- /dev/null +++ b/src/android/app/src/main/res/values-vi/strings.xml | |||
| @@ -0,0 +1,340 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> | ||
| 3 | |||
| 4 | <string name="app_disclaimer">Phần má»m nà y sẽ chạy các game cho máy chÆ¡i game Nintendo Switch. Không có title games hoặc keys được bao gồm.<br /><br />Trước khi bạn bắt đầu, hãy tìm táºp tin <![CDATA[<b> prod.keys </b>]]> trên bá»™ nhá»› thiết bị cá»§a bạn.<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tìm hiểu thêm</a>]]></string> | ||
| 5 | <string name="emulation_notification_channel_name">Giả láºp Ä‘ang chạy</string> | ||
| 6 | <string name="emulation_notification_channel_description">Hiển thị thông báo liên tục khi giả láºp Ä‘ang chạy.</string> | ||
| 7 | <string name="emulation_notification_running">yuzu đang chạy</string> | ||
| 8 | <string name="notice_notification_channel_name">Thông báo và lỗi</string> | ||
| 9 | <string name="notice_notification_channel_description">Hiển thị thông báo khi có sự cố xảy ra.</string> | ||
| 10 | <string name="notification_permission_not_granted">Ứng dụng không được cấp quyá»n thông báo!</string> | ||
| 11 | |||
| 12 | <!-- Setup strings --> | ||
| 13 | <string name="welcome">Chà o mừng!</string> | ||
| 14 | <string name="welcome_description">Tìm hiểu cách cà i đặt <b>yuzu</b> và bắt đầu giả láºp.</string> | ||
| 15 | <string name="get_started">Bắt đầu</string> | ||
| 16 | <string name="keys">Keys</string> | ||
| 17 | <string name="keys_description">Chá»n tệp <b>prod.keys</b> cá»§a bạn bằng nút bên dưới.</string> | ||
| 18 | <string name="select_keys">Chá»n Keys</string> | ||
| 19 | <string name="games">Game</string> | ||
| 20 | <string name="games_description">Chá»n thư mục <b>Game</b> cá»§a bạn bằng nút bên dưới.</string> | ||
| 21 | <string name="done">Hoà n thà nh</string> | ||
| 22 | <string name="done_description">Tất cả đã hoà n tất.\nHãy táºn hưởng các game cá»§a bạn!</string> | ||
| 23 | <string name="text_continue">Tiếp tục</string> | ||
| 24 | <string name="next">Tiếp theo</string> | ||
| 25 | <string name="back">Trở lại</string> | ||
| 26 | <string name="add_games">Thêm Game</string> | ||
| 27 | <string name="add_games_description">Chá»n thư mục game cá»§a bạn</string> | ||
| 28 | <!-- Home strings --> | ||
| 29 | <string name="home_games">Game</string> | ||
| 30 | <string name="home_search">Tìm kiếm</string> | ||
| 31 | <string name="home_settings">Cà i đặt</string> | ||
| 32 | <string name="empty_gamelist">Không tìm thấy táºp tin hoặc chưa có thư mục game nà o được chá»n.</string> | ||
| 33 | <string name="search_and_filter_games">Tìm và lá»c game</string> | ||
| 34 | <string name="select_games_folder">Chá»n thư mục game</string> | ||
| 35 | <string name="select_games_folder_description">Cho phép yuzu thêm và o danh sách game</string> | ||
| 36 | <string name="add_games_warning">Bá» qua việc lá»±a chá»n thư mục game?</string> | ||
| 37 | <string name="add_games_warning_description">Game sẽ không hiển thị trong danh sách nếu má»™t thư mục không được chá»n.</string> | ||
| 38 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | ||
| 39 | <string name="home_search_games">Tìm kiếm game</string> | ||
| 40 | <string name="games_dir_selected">Thư mục game đã được chá»n</string> | ||
| 41 | <string name="install_prod_keys">Cà i đặt prod.keys</string> | ||
| 42 | <string name="install_prod_keys_description">Yêu cầu để giải mã các game bán lẻ</string> | ||
| 43 | <string name="install_prod_keys_warning">BỠqua việc thêm keys?</string> | ||
| 44 | <string name="install_prod_keys_warning_description">Cần có keys hợp lệ để giả láºp các game bán lẻ. Chỉ có các ứng dụng homebrew có thể váºn hà nh nếu bạn tiếp tục.</string> | ||
| 45 | <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string> | ||
| 46 | <string name="notifications">Thông báo</string> | ||
| 47 | <string name="notifications_description">Cấp quyá»n thông báo bằng nút bên dưới.</string> | ||
| 48 | <string name="give_permission">Cấp quyá»n</string> | ||
| 49 | <string name="notification_warning">Bá» qua việc cấp quyá»n thông báo?</string> | ||
| 50 | <string name="notification_warning_description">yuzu sẽ không thể gá»i những thông báo quan trá»ng đến bạn.</string> | ||
| 51 | <string name="permission_denied">Äã từ chối cấp quyá»n</string> | ||
| 52 | <string name="permission_denied_description">Bạn từ chối cấp quyá»n nà y quá nhiá»u lần và giá» bạn phải cấp quyá»n thá»§ công trong cà i đặt máy.</string> | ||
| 53 | <string name="about">Thông tin</string> | ||
| 54 | <string name="about_description">Phiên bản, đóng góp và những thứ khác</string> | ||
| 55 | <string name="warning_help">Trợ giúp</string> | ||
| 56 | <string name="warning_skip">Bá» qua</string> | ||
| 57 | <string name="warning_cancel">Há»§y bá»</string> | ||
| 58 | <string name="install_amiibo_keys">Cà i đặt keys Amiibo</string> | ||
| 59 | <string name="install_amiibo_keys_description">Cần thiết để dùng Amiibo trong game</string> | ||
| 60 | <string name="invalid_keys_file">Tệp keys không hợp lệ đã được chá»n</string> | ||
| 61 | <string name="install_keys_success">Cà i đặt keys thà nh công</string> | ||
| 62 | <string name="reading_keys_failure">Lá»—i Ä‘á»c keys mã hóa</string> | ||
| 63 | <string name="install_prod_keys_failure_extension_description">Xác minh rằng tệp keys của bạn có đuôi .keys và thỠlại.</string> | ||
| 64 | <string name="install_amiibo_keys_failure_extension_description">Xác minh rằng tệp keys của bạn có đuôi .bin và thỠlại.</string> | ||
| 65 | <string name="invalid_keys_error">Keys mã hoá không hợp lệ</string> | ||
| 66 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | ||
| 67 | <string name="install_keys_failure_description">Tệp đã chá»n sai hoặc há»ng. Vui lòng trÃch xuất lại keys cá»§a bạn.</string> | ||
| 68 | <string name="install_gpu_driver">Cà i đặt driver GPU</string> | ||
| 69 | <string name="install_gpu_driver_description">Cà i đặt driver thay thế để có thể có hiệu suất tốt và chÃnh xác hÆ¡n</string> | ||
| 70 | <string name="advanced_settings">Cà i đặt nâng cao</string> | ||
| 71 | <string name="settings_description">Cấu hình cà i đặt giả láºp</string> | ||
| 72 | <string name="search_recently_played">Äã chÆ¡i gần đây</string> | ||
| 73 | <string name="search_recently_added">Äã thêm gần đây</string> | ||
| 74 | <string name="search_retail">Bán lẻ</string> | ||
| 75 | <string name="search_homebrew">Homebrew</string> | ||
| 76 | <string name="open_user_folder">Mở thư mục yuzu</string> | ||
| 77 | <string name="open_user_folder_description">Quản lý tệp nội bộ của yuzu</string> | ||
| 78 | <string name="theme_and_color_description">Thay đổi giao diện ứng dụng</string> | ||
| 79 | <string name="no_file_manager">Không tìm thấy trình quản lý táºp tin</string> | ||
| 80 | <string name="notification_no_directory_link">Không thể mở thư mục yuzu</string> | ||
| 81 | <string name="notification_no_directory_link_description">Vui lòng xác định thư mục ngưá»i dùng vá»›i bảng Ä‘iá»u khiển bên cá»§a trình quản lý tệp thá»§ công.</string> | ||
| 82 | <string name="manage_save_data">Quản lý dữ liệu save</string> | ||
| 83 | <string name="manage_save_data_description">Äã tìm thấy dữ liệu save. Vui lòng chá»n má»™t tuỳ chá»n bên dưới.</string> | ||
| 84 | <string name="import_export_saves_description">Nháºp hoặc xuất tệp save</string> | ||
| 85 | <string name="save_file_imported_success">Nháºp thà nh công</string> | ||
| 86 | <string name="save_file_invalid_zip_structure">Cấu trúc thư mục save không hợp lệ</string> | ||
| 87 | <string name="save_file_invalid_zip_structure_description">Tên thư mục con đầu tiên phải là ID title của game.</string> | ||
| 88 | <string name="import_saves">Nháºp</string> | ||
| 89 | <string name="export_saves">Xuất</string> | ||
| 90 | <string name="install_firmware">Cà i đặt firmware</string> | ||
| 91 | <string name="install_firmware_description">Firmware phải được đặt trong má»™t táºp tin nén ZIP và cần thiết để khởi chạy má»™t số game</string> | ||
| 92 | <string name="firmware_installing">Äang cà i đặt firmware</string> | ||
| 93 | <string name="firmware_installed_success">Cà i đặt firmware thà nh công</string> | ||
| 94 | <string name="firmware_installed_failure">Cà i đặt firmware thất bại</string> | ||
| 95 | <string name="share_log">Chia sẻ nháºt ký gỡ lá»—i</string> | ||
| 96 | <string name="share_log_description">Chia sẻ táºp tin nháºt ký cá»§a yuzu để gỡ lá»—i vấn Ä‘á»</string> | ||
| 97 | <string name="share_log_missing">Không tìm thấy táºp tin nháºt ký</string> | ||
| 98 | <string name="install_game_content">Cà i đặt nội dung game</string> | ||
| 99 | <string name="install_game_content_description">Cà i đặt cáºp nháºt game hoặc DLC</string> | ||
| 100 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 101 | <!-- About screen strings --> | ||
| 102 | <string name="gaia_is_not_real">Gaia không có tháºt</string> | ||
| 103 | <string name="copied_to_clipboard">Äã sao chép và o bá»™ nhá»› tạm</string> | ||
| 104 | <string name="about_app_description">Má»™t giả láºp Switch mã nguồn mở</string> | ||
| 105 | <string name="contributors">Ngưá»i đóng góp</string> | ||
| 106 | <string name="contributors_description">ÄÆ°á»£c là m vá»›i \u2764 từ nhóm yuzu</string> | ||
| 107 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | ||
| 108 | <string name="licenses_description">Các dá»± án là m cho yuzu trên Android trở thà nh Ä‘iá»u có thể</string> | ||
| 109 | <string name="build">Dá»±ng</string> | ||
| 110 | <string name="support_link">https://discord.gg/u77vRWY</string> | ||
| 111 | <string name="website_link">https://yuzu-emu.org/</string> | ||
| 112 | <string name="github_link">https://github.com/yuzu-emu</string> | ||
| 113 | |||
| 114 | <!-- Early access upgrade strings --> | ||
| 115 | <string name="early_access">Early Access</string> | ||
| 116 | <string name="get_early_access">Tải Early Access</string> | ||
| 117 | <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> | ||
| 118 | <string name="get_early_access_description">Các tÃnh năng tiên tiến, truy cáºp sá»›m các bản cáºp nháºt và nhiá»u hÆ¡n nữa</string> | ||
| 119 | <string name="early_access_benefits">Lợi Ãch cá»§a Early Access</string> | ||
| 120 | <string name="cutting_edge_features">TÃnh năng tiên tiến</string> | ||
| 121 | <string name="early_access_updates">Truy cáºp sá»›m các bản cáºp nháºt</string> | ||
| 122 | <string name="no_manual_installation">Không có cà i đặt thủ công</string> | ||
| 123 | <string name="prioritized_support">Ưu tiên hỗ trợ</string> | ||
| 124 | <string name="helping_game_preservation">Hỗ trợ bảo tồn game</string> | ||
| 125 | <string name="our_eternal_gratitude">Sự biết ơn vô hạn của chúng tôi</string> | ||
| 126 | <string name="are_you_interested">Bạn có thấy hứng thú không?</string> | ||
| 127 | |||
| 128 | <!-- General settings strings --> | ||
| 129 | <string name="frame_limit_enable">Giới hạn tốc độ</string> | ||
| 130 | <string name="frame_limit_enable_description">Giá»›i hạn tốc độ giả láºp ở má»™t phần trăm cụ thể cá»§a tốc độ bình thưá»ng.</string> | ||
| 131 | <string name="frame_limit_slider">Giới hạn phần trăm tốc độ</string> | ||
| 132 | <string name="frame_limit_slider_description">Xác định phần trăm để giá»›i hạn tốc độ giả láºp. 100% là tốc độ bình thưá»ng. Giá trị cao hÆ¡n hoặc thấp hÆ¡n sẽ tăng hoặc giảm giá»›i hạn tốc độ.</string> | ||
| 133 | <string name="cpu_accuracy">Äá»™ chÃnh xác CPU</string> | ||
| 134 | <!-- System settings strings --> | ||
| 135 | <string name="use_docked_mode">Chế độ docked</string> | ||
| 136 | <string name="use_docked_mode_description">Tăng độ phân giải, giảm hiệu suất. Chế độ handheld được sỠdụng khi tắt, giảm độ phân giải và tăng hiệu suất.</string> | ||
| 137 | <string name="emulated_region">Khu vá»±c giả láºp</string> | ||
| 138 | <string name="emulated_language">Ngôn ngữ giả láºp</string> | ||
| 139 | <string name="select_rtc_date">Chá»n ngà y RTC</string> | ||
| 140 | <string name="select_rtc_time">Chá»n giá» RTC</string> | ||
| 141 | <string name="use_custom_rtc">RTC tuỳ chỉnh</string> | ||
| 142 | <string name="use_custom_rtc_description">Cho phép bạn thiết láºp má»™t đồng hồ thá»i gian thá»±c tùy chỉnh riêng biệt so vá»›i thá»i gian hệ thống hiện tại.</string> | ||
| 143 | <string name="set_custom_rtc">Thiết láºp RTC tùy chỉnh</string> | ||
| 144 | |||
| 145 | <!-- Graphics settings strings --> | ||
| 146 | <string name="renderer_accuracy">Mức độ chÃnh xác</string> | ||
| 147 | <string name="renderer_resolution">Äá»™ phân giải (Handheld/Docked)</string> | ||
| 148 | <string name="renderer_vsync">Chế độ VSync</string> | ||
| 149 | <string name="renderer_aspect_ratio">Tỉ lệ khung hình</string> | ||
| 150 | <string name="renderer_scaling_filter">Bá»™ lá»c Ä‘iá»u chỉnh cá»a sổ</string> | ||
| 151 | <string name="renderer_anti_aliasing">Phương pháp khỠrăng cưa</string> | ||
| 152 | <string name="renderer_force_max_clock">Buộc chạy ở xung nhịp tối đa (chỉ cho Adreno)</string> | ||
| 153 | <string name="renderer_force_max_clock_description">Buộc GPU hoạt động ở xung nhịp tối đa có thể (rà ng buộc nhiệt độ vẫn sẽ được áp dụng).</string> | ||
| 154 | <string name="renderer_asynchronous_shaders">Dùng các shader bất đồng bộ</string> | ||
| 155 | <string name="renderer_asynchronous_shaders_description">Biên dịch các shader bất đồng bá»™, giảm tình trạng giáºt lag nhưng có thể gây ra các lá»—i.</string> | ||
| 156 | <string name="renderer_reactive_flushing">Dùng xả tương ứng</string> | ||
| 157 | <string name="renderer_reactive_flushing_description">Cải thiện độ chÃnh xác kết xuất trong má»™t số game nhưng đồng thá»i giảm hiệu suất.</string> | ||
| 158 | <string name="use_disk_shader_cache">Lưu bộ nhớ đệm shader trên ổ cứng</string> | ||
| 159 | <string name="use_disk_shader_cache_description">Giảm tình trạng giáºt lag bằng cách lưu trữ và tải các shader được tạo ra ná»™i bá»™.</string> | ||
| 160 | |||
| 161 | <!-- Debug settings strings --> | ||
| 162 | <string name="cpu">CPU</string> | ||
| 163 | <string name="renderer_api">API</string> | ||
| 164 | <string name="renderer_debug">Gỡ lỗi đồ hoạ</string> | ||
| 165 | <string name="renderer_debug_description">Äặt API đồ há»a và o chế độ gỡ lá»—i cháºm.</string> | ||
| 166 | <string name="audio_volume">Âm lượng</string> | ||
| 167 | <string name="audio_volume_description">Xác định âm lượng của đầu ra âm thanh.</string> | ||
| 168 | |||
| 169 | <!-- Miscellaneous --> | ||
| 170 | <string name="slider_default">Mặc định</string> | ||
| 171 | <string name="ini_saved">Cà i đặt đã lưu</string> | ||
| 172 | <string name="gameid_saved">Cà i đặt đã lưu cho %1$s</string> | ||
| 173 | <string name="error_saving">Lỗi khi lưu %1$s.ini: %2$s</string> | ||
| 174 | <string name="loading">Äang tải...</string> | ||
| 175 | <string name="reset_setting_confirmation">Bạn có muốn đặt lại cà i đặt nà y vỠgiá trị mặc định không?</string> | ||
| 176 | <string name="reset_to_default">Äặt lại vá» mặc định</string> | ||
| 177 | <string name="reset_all_settings">Bạn có muốn đặt lại tất cả các cà i đặt vỠgiá trị mặc định không?</string> | ||
| 178 | <string name="reset_all_settings_description">Tất cả các cà i đặt nâng cao sẽ được đặt lại vá» cấu hình mặc định. Äiá»u nà y không thể hoà n tác.</string> | ||
| 179 | <string name="settings_reset">Cà i đặt đã được đặt lại</string> | ||
| 180 | <string name="close">Äóng</string> | ||
| 181 | <string name="learn_more">Tìm hiểu thêm</string> | ||
| 182 | <string name="auto">Tự động</string> | ||
| 183 | <string name="submit">Gá»i</string> | ||
| 184 | <string name="string_null">Null</string> | ||
| 185 | <string name="string_import">Nháºp</string> | ||
| 186 | <string name="export">Xuất</string> | ||
| 187 | <!-- GPU driver installation --> | ||
| 188 | <string name="select_gpu_driver">Chá»n driver GPU</string> | ||
| 189 | <string name="select_gpu_driver_title">Bạn có muốn thay thế driver GPU hiện tại không?</string> | ||
| 190 | <string name="select_gpu_driver_install">Cà i đặt</string> | ||
| 191 | <string name="select_gpu_driver_default">Mặc định</string> | ||
| 192 | <string name="select_gpu_driver_use_default">Dùng driver GPU mặc định</string> | ||
| 193 | <string name="select_gpu_driver_error">Driver không hợp lệ đã được chá»n, dùng mặc định hệ thống!</string> | ||
| 194 | <string name="system_gpu_driver">Driver GPU hệ thống</string> | ||
| 195 | <string name="installing_driver">Äang cà i đặt driver...</string> | ||
| 196 | |||
| 197 | <!-- Preferences Screen --> | ||
| 198 | <string name="preferences_settings">Cà i đặt</string> | ||
| 199 | <string name="preferences_general">Chung</string> | ||
| 200 | <string name="preferences_system">Hệ thống</string> | ||
| 201 | <string name="preferences_graphics">Äồ hoạ</string> | ||
| 202 | <string name="preferences_audio">Âm thanh</string> | ||
| 203 | <string name="preferences_theme">Chủ đỠvà mà u sắc</string> | ||
| 204 | <string name="preferences_debug">Gỡ lỗi</string> | ||
| 205 | |||
| 206 | <!-- ROM loading errors --> | ||
| 207 | <string name="loader_error_encrypted">ROM của bạn đã bị mã hoá</string> | ||
| 208 | <string name="loader_error_encrypted_keys_description"><![CDATA[Vui lòng đảm bảo tệp <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> đã được cà i đặt để các game có thể được giải mã.]]></string> | ||
| 209 | <string name="loader_error_video_core">Äã xảy ra lá»—i khi khởi tạo lõi video</string> | ||
| 210 | <string name="loader_error_video_core_description">Việc nà y thưá»ng do driver GPU không tương thÃch. Cà i đặt má»™t driver GPU tùy chỉnh có thể giải quyết vấn đỠnà y.</string> | ||
| 211 | <string name="loader_error_invalid_format">Không thể nạp ROM</string> | ||
| 212 | <string name="loader_error_file_not_found">Tệp ROM không tồn tại</string> | ||
| 213 | |||
| 214 | <!-- Emulation Menu --> | ||
| 215 | <string name="emulation_exit">Thoát giả láºp</string> | ||
| 216 | <string name="emulation_done">Hoà n thà nh</string> | ||
| 217 | <string name="emulation_fps_counter">Bộ đếm FPS</string> | ||
| 218 | <string name="emulation_toggle_controls">Chuyển đổi Ä‘iá»u khiển</string> | ||
| 219 | <string name="emulation_rel_stick_center">Trung tâm nút cần xoay tương đối</string> | ||
| 220 | <string name="emulation_dpad_slide">Trượt D-pad</string> | ||
| 221 | <string name="emulation_haptics">Chạm haptics</string> | ||
| 222 | <string name="emulation_show_overlay">Hiện lớp phủ</string> | ||
| 223 | <string name="emulation_toggle_all">Chuyển đổi tất cả</string> | ||
| 224 | <string name="emulation_control_adjust">Äiá»u chỉnh lá»›p phá»§</string> | ||
| 225 | <string name="emulation_control_scale">Tỉ lệ thu phóng</string> | ||
| 226 | <string name="emulation_control_opacity">Äá»™ má»</string> | ||
| 227 | <string name="emulation_touch_overlay_reset">Äặt lại lá»›p phá»§</string> | ||
| 228 | <string name="emulation_touch_overlay_edit">Chỉnh sá»a lá»›p phá»§</string> | ||
| 229 | <string name="emulation_pause">Tạm đừng giả láºp</string> | ||
| 230 | <string name="emulation_unpause">Tiếp tục giả láºp</string> | ||
| 231 | <string name="emulation_input_overlay">Tuỳ chá»n lá»›p phá»§</string> | ||
| 232 | |||
| 233 | <string name="load_settings">Äang tải cà i đặt...</string> | ||
| 234 | |||
| 235 | <!-- Software keyboard --> | ||
| 236 | <string name="software_keyboard">Bà n phÃm má»m</string> | ||
| 237 | |||
| 238 | <!-- Errors and warnings --> | ||
| 239 | <string name="abort_button">Há»§y bá»</string> | ||
| 240 | <string name="continue_button">Tiếp tục</string> | ||
| 241 | <string name="system_archive_not_found">Không tìm thấy bản lưu trữ của hệ thống</string> | ||
| 242 | <string name="system_archive_not_found_message">%s bị thiếu. Vui lòng trÃch xuất các bản lưu trữ hệ thống cá»§a bạn.\nNếu chạy tiếp giả láºp có thể bị crash và lá»—i.</string> | ||
| 243 | <string name="system_archive_general">Một bản lưu trữ của hệ thống</string> | ||
| 244 | <string name="save_load_error">Lỗi Lưu/Tải</string> | ||
| 245 | <string name="fatal_error">Lá»—i nghiêm trá»ng</string> | ||
| 246 | <string name="fatal_error_message">Äã xảy ra lá»—i nghiêm trá»ng. Kiểm tra nháºt ký để biết thêm chi tiết.\nNếu chạy tiếp giả láºp có thể bị crash và lá»—i.</string> | ||
| 247 | <string name="performance_warning">Tắt cà i đặt nà y sẽ là m giảm đáng kể hiệu suất giả láºp! Äể có trải nghiệm tốt nhất, bạn nên báºt cà i đặt nà y.</string> | ||
| 248 | <!-- Region Names --> | ||
| 249 | <string name="region_japan">Nháºt Bản</string> | ||
| 250 | <string name="region_usa">Hoa Kỳ</string> | ||
| 251 | <string name="region_europe">Châu Âu</string> | ||
| 252 | <string name="region_australia">Úc</string> | ||
| 253 | <string name="region_china">Trung Quốc</string> | ||
| 254 | <string name="region_korea">Hà n Quốc</string> | ||
| 255 | <string name="region_taiwan">ÄÃ i Loan</string> | ||
| 256 | |||
| 257 | <string name="memory_gigabyte">GB</string> | ||
| 258 | <!-- Renderer APIs --> | ||
| 259 | <string name="renderer_vulkan">Vulkan</string> | ||
| 260 | <string name="renderer_none">Không có</string> | ||
| 261 | |||
| 262 | <!-- Renderer Accuracy --> | ||
| 263 | <string name="renderer_accuracy_normal">Bình thưá»ng</string> | ||
| 264 | <string name="renderer_accuracy_high">Cao</string> | ||
| 265 | <string name="renderer_accuracy_extreme">Cá»±c đại (Cháºm)</string> | ||
| 266 | |||
| 267 | <!-- Resolutions --> | ||
| 268 | <string name="resolution_half">0.5X (360p/540p)</string> | ||
| 269 | <string name="resolution_three_quarter">0.75X (540p/810p)</string> | ||
| 270 | <string name="resolution_one">1X (720p/1080p)</string> | ||
| 271 | <string name="resolution_two">2X (1440p/2160p) (Cháºm)</string> | ||
| 272 | <string name="resolution_three">3X (2160p/3240p) (Cháºm)</string> | ||
| 273 | <string name="resolution_four">4X (2880p/4320p) (Cháºm)</string> | ||
| 274 | |||
| 275 | <!-- Renderer VSync --> | ||
| 276 | <string name="renderer_vsync_immediate">Immediate (Tắt)</string> | ||
| 277 | <string name="renderer_vsync_mailbox">Mailbox</string> | ||
| 278 | <string name="renderer_vsync_fifo">FIFO (Báºt)</string> | ||
| 279 | <string name="renderer_vsync_fifo_relaxed">FIFO Relaxed</string> | ||
| 280 | |||
| 281 | <!-- Scaling Filters --> | ||
| 282 | <string name="scaling_filter_nearest_neighbor">Nearest Neighbor</string> | ||
| 283 | <string name="scaling_filter_bilinear">Bilinear</string> | ||
| 284 | <string name="scaling_filter_bicubic">Bicubic</string> | ||
| 285 | <string name="scaling_filter_gaussian">Gaussian</string> | ||
| 286 | <string name="scaling_filter_scale_force">ScaleForce</string> | ||
| 287 | <string name="scaling_filter_fsr">AMD FidelityFXâ„¢ Super Resolution</string> | ||
| 288 | |||
| 289 | <!-- Anti-Aliasing --> | ||
| 290 | <string name="anti_aliasing_none">Không có</string> | ||
| 291 | <string name="anti_aliasing_fxaa">FXAA</string> | ||
| 292 | <string name="anti_aliasing_smaa">SMAA</string> | ||
| 293 | |||
| 294 | <string name="screen_layout_auto">Tự động</string> | ||
| 295 | |||
| 296 | <!-- Aspect Ratios --> | ||
| 297 | <string name="ratio_default">Mặc định (16:9)</string> | ||
| 298 | <string name="ratio_force_four_three">Dùng 4:3</string> | ||
| 299 | <string name="ratio_force_twenty_one_nine">Dùng 21:9</string> | ||
| 300 | <string name="ratio_force_sixteen_ten">Dùng 16:10</string> | ||
| 301 | <string name="ratio_stretch">Mở rá»™ng đến cá»a sổ</string> | ||
| 302 | |||
| 303 | <!-- CPU Accuracy --> | ||
| 304 | <string name="cpu_accuracy_accurate">ChÃnh xác</string> | ||
| 305 | <string name="cpu_accuracy_unsafe">Không an toà n</string> | ||
| 306 | <string name="cpu_accuracy_paranoid">Paranoid (Cháºm)</string> | ||
| 307 | |||
| 308 | <!-- Gamepad Buttons --> | ||
| 309 | <string name="gamepad_d_pad">D-pad</string> | ||
| 310 | <string name="gamepad_left_stick">Cần trái</string> | ||
| 311 | <string name="gamepad_right_stick">Cần phải</string> | ||
| 312 | <string name="gamepad_home">Home</string> | ||
| 313 | <string name="gamepad_screenshot">Ảnh chụp mà n hình</string> | ||
| 314 | |||
| 315 | <!-- Disk shader cache --> | ||
| 316 | <string name="preparing_shaders">Äang chuẩn bị shader</string> | ||
| 317 | <string name="building_shaders">Äang đựng shader</string> | ||
| 318 | |||
| 319 | <!-- Theme options --> | ||
| 320 | <string name="change_app_theme">Thay đổi chủ đỠứng dụng</string> | ||
| 321 | <string name="theme_default">Mặc định</string> | ||
| 322 | <string name="theme_material_you">Material You</string> | ||
| 323 | |||
| 324 | <!-- Theme Modes --> | ||
| 325 | <string name="change_theme_mode">Thay đổi chá»§ Ä‘á»</string> | ||
| 326 | <string name="theme_mode_follow_system">Theo hệ thống</string> | ||
| 327 | <string name="theme_mode_light">Sáng</string> | ||
| 328 | <string name="theme_mode_dark">Tối</string> | ||
| 329 | |||
| 330 | <!-- Black backgrounds theme --> | ||
| 331 | <string name="use_black_backgrounds">Ná»n Ä‘en</string> | ||
| 332 | <string name="use_black_backgrounds_description">Khi sá» dụng chá»§ đỠtối, hãy áp dụng ná»n Ä‘en.</string> | ||
| 333 | |||
| 334 | <string name="mute">Tắt tiếng</string> | ||
| 335 | <string name="unmute">Báºt tiếng</string> | ||
| 336 | |||
| 337 | <!-- Licenses screen strings --> | ||
| 338 | <string name="licenses">Giấy phép</string> | ||
| 339 | <string name="license_fidelityfx_fsr_description">Upscaling chất lượng cao từ AMD</string> | ||
| 340 | </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 index c0e885751..13455564f 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">æ¤è½¯ä»¶å¯ä»¥è¿è¡Œ Nintendo Switch 游æˆï¼Œä½†ä¸åŒ…å«ä»»ä½•游æˆå’Œå¯†é’¥æ–‡ä»¶ã€‚<br /><br />在开始å‰ï¼Œè¯·æ‰¾åˆ°æ”¾ç½®äºŽè®¾å¤‡å˜å‚¨ä¸çš„ <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string> | 4 | <string name="app_disclaimer">æ¤è½¯ä»¶å¯ä»¥è¿è¡Œ Nintendo Switch 游æˆï¼Œä½†ä¸åŒ…å«ä»»ä½•游æˆå’Œå¯†é’¥æ–‡ä»¶ã€‚<br /><br />在开始å‰ï¼Œè¯·æ‰¾åˆ°æ”¾ç½®äºŽè®¾å¤‡å˜å‚¨ä¸çš„ <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">æ£åœ¨è¿›è¡Œæ¨¡æ‹Ÿ</string> | 5 | <string name="emulation_notification_channel_name">æ£åœ¨è¿›è¡Œæ¨¡æ‹Ÿ</string> |
| @@ -17,7 +17,7 @@ | |||
| 17 | <string name="keys_description">使用下方的按钮æ¥é€‰æ‹©ä½ çš„ <b>prod.keys</b> 文件。</string> | 17 | <string name="keys_description">使用下方的按钮æ¥é€‰æ‹©ä½ çš„ <b>prod.keys</b> 文件。</string> |
| 18 | <string name="select_keys">选择密钥文件</string> | 18 | <string name="select_keys">选择密钥文件</string> |
| 19 | <string name="games">游æˆ</string> | 19 | <string name="games">游æˆ</string> |
| 20 | <string name="games_description">ä½¿ç”¨ä¸‹æ–¹çš„æŒ‰é’®é€‰æ‹©ä½ çš„ <b>游æˆ</b> 文件夹。</string> | 20 | <string name="games_description">ä½¿ç”¨ä¸‹æ–¹çš„æŒ‰é’®é€‰æ‹©ä½ çš„<b>游æˆ</b>文件夹。</string> |
| 21 | <string name="done">完æˆ</string> | 21 | <string name="done">完æˆ</string> |
| 22 | <string name="done_description">ä½ å®Œæˆäº†å…¨éƒ¨è®¾ç½®ã€‚\n玩的开心ï¼</string> | 22 | <string name="done_description">ä½ å®Œæˆäº†å…¨éƒ¨è®¾ç½®ã€‚\n玩的开心ï¼</string> |
| 23 | <string name="text_continue">ç»§ç»</string> | 23 | <string name="text_continue">ç»§ç»</string> |
| @@ -25,6 +25,7 @@ | |||
| 25 | <string name="back">上一æ¥</string> | 25 | <string name="back">上一æ¥</string> |
| 26 | <string name="add_games">æ·»åŠ æ¸¸æˆ</string> | 26 | <string name="add_games">æ·»åŠ æ¸¸æˆ</string> |
| 27 | <string name="add_games_description">é€‰æ‹©ä½ çš„æ¸¸æˆæ–‡ä»¶å¤¹</string> | 27 | <string name="add_games_description">é€‰æ‹©ä½ çš„æ¸¸æˆæ–‡ä»¶å¤¹</string> |
| 28 | <string name="step_complete">完æˆï¼</string> | ||
| 28 | 29 | ||
| 29 | <!-- Home strings --> | 30 | <!-- Home strings --> |
| 30 | <string name="home_games">游æˆ</string> | 31 | <string name="home_games">游æˆ</string> |
| @@ -38,6 +39,7 @@ | |||
| 38 | <string name="add_games_warning_description">å¦‚æžœæœªé€‰æ‹©æ¸¸æˆæ–‡ä»¶å¤¹ï¼Œæ¸¸æˆå°†ä¸ä¼šæ˜¾ç¤ºåœ¨æ¸¸æˆåˆ—表ä¸ã€‚</string> | 39 | <string name="add_games_warning_description">å¦‚æžœæœªé€‰æ‹©æ¸¸æˆæ–‡ä»¶å¤¹ï¼Œæ¸¸æˆå°†ä¸ä¼šæ˜¾ç¤ºåœ¨æ¸¸æˆåˆ—表ä¸ã€‚</string> |
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 40 | <string name="home_search_games">æœç´¢æ¸¸æˆ</string> | 41 | <string name="home_search_games">æœç´¢æ¸¸æˆ</string> |
| 42 | <string name="search_settings">æœç´¢è®¾ç½®</string> | ||
| 41 | <string name="games_dir_selected">å·²é€‰æ‹©æ¸¸æˆæ–‡ä»¶å¤¹</string> | 43 | <string name="games_dir_selected">å·²é€‰æ‹©æ¸¸æˆæ–‡ä»¶å¤¹</string> |
| 42 | <string name="install_prod_keys">安装 prod.keys 文件</string> | 44 | <string name="install_prod_keys">安装 prod.keys 文件</string> |
| 43 | <string name="install_prod_keys_description">需è¦å¯†é’¥æ–‡ä»¶æ¥è§£å¯†æ¸¸æˆ</string> | 45 | <string name="install_prod_keys_description">需è¦å¯†é’¥æ–‡ä»¶æ¥è§£å¯†æ¸¸æˆ</string> |
| @@ -61,12 +63,15 @@ | |||
| 61 | <string name="invalid_keys_file">é€‰æ‹©çš„å¯†é’¥æ–‡ä»¶æ— æ•ˆ</string> | 63 | <string name="invalid_keys_file">é€‰æ‹©çš„å¯†é’¥æ–‡ä»¶æ— æ•ˆ</string> |
| 62 | <string name="install_keys_success">密钥文件已æˆåŠŸå®‰è£…</string> | 64 | <string name="install_keys_success">密钥文件已æˆåŠŸå®‰è£…</string> |
| 63 | <string name="reading_keys_failure">读å–åŠ å¯†å¯†é’¥æ—¶å‡ºé”™</string> | 65 | <string name="reading_keys_failure">读å–åŠ å¯†å¯†é’¥æ—¶å‡ºé”™</string> |
| 66 | <string name="install_prod_keys_failure_extension_description">è¯·ç¡®ä¿æ‚¨çš„密钥文件扩展å为 .keys å¹¶é‡è¯•。</string> | ||
| 67 | <string name="install_amiibo_keys_failure_extension_description">è¯·ç¡®ä¿æ‚¨çš„密钥文件扩展å为 .bin å¹¶é‡è¯•。</string> | ||
| 64 | <string name="invalid_keys_error">æ— æ•ˆçš„åŠ å¯†å¯†é’¥</string> | 68 | <string name="invalid_keys_error">æ— æ•ˆçš„åŠ å¯†å¯†é’¥</string> |
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 69 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">é€‰æ‹©çš„å¯†é’¥æ–‡ä»¶ä¸æ£ç¡®æˆ–å·²æŸåã€‚è¯·é‡æ–°è½¬å‚¨å¯†é’¥æ–‡ä»¶ã€‚</string> | 70 | <string name="install_keys_failure_description">é€‰æ‹©çš„å¯†é’¥æ–‡ä»¶ä¸æ£ç¡®æˆ–å·²æŸåã€‚è¯·é‡æ–°è½¬å‚¨å¯†é’¥æ–‡ä»¶ã€‚</string> |
| 67 | <string name="install_gpu_driver">安装 GPU 驱动</string> | 71 | <string name="install_gpu_driver">安装 GPU 驱动</string> |
| 68 | <string name="install_gpu_driver_description">安装替代的驱动程åºä»¥èŽ·å¾—æ›´å¥½çš„æ€§èƒ½å’Œç²¾åº¦</string> | 72 | <string name="install_gpu_driver_description">安装替代的驱动程åºä»¥èŽ·å¾—æ›´å¥½çš„æ€§èƒ½å’Œç²¾åº¦</string> |
| 69 | <string name="advanced_settings">高级选项</string> | 73 | <string name="advanced_settings">高级选项</string> |
| 74 | <string name="advanced_settings_game">高级选项: %1$s</string> | ||
| 70 | <string name="settings_description">更改模拟器设置</string> | 75 | <string name="settings_description">更改模拟器设置</string> |
| 71 | <string name="search_recently_played">最近游玩</string> | 76 | <string name="search_recently_played">最近游玩</string> |
| 72 | <string name="search_recently_added">æœ€è¿‘æ·»åŠ </string> | 77 | <string name="search_recently_added">æœ€è¿‘æ·»åŠ </string> |
| @@ -86,6 +91,33 @@ | |||
| 86 | <string name="save_file_invalid_zip_structure_description">ç¬¬ä¸€ä¸ªåæ–‡ä»¶å¤¹åç§°å¿…é¡»ä¸ºå½“å‰æ¸¸æˆçš„ ID。</string> | 91 | <string name="save_file_invalid_zip_structure_description">ç¬¬ä¸€ä¸ªåæ–‡ä»¶å¤¹åç§°å¿…é¡»ä¸ºå½“å‰æ¸¸æˆçš„ ID。</string> |
| 87 | <string name="import_saves">导入</string> | 92 | <string name="import_saves">导入</string> |
| 88 | <string name="export_saves">导出</string> | 93 | <string name="export_saves">导出</string> |
| 94 | <string name="install_firmware">安装固件</string> | ||
| 95 | <string name="install_firmware_description">固件文件必须为 zip æ ¼å¼ï¼Œå¯åЍæŸäº›æ¸¸æˆæ—¶å¿…需</string> | ||
| 96 | <string name="firmware_installing">æ£åœ¨å®‰è£…固件</string> | ||
| 97 | <string name="firmware_installed_success">固件已æˆåŠŸå®‰è£…</string> | ||
| 98 | <string name="firmware_installed_failure">固件安装失败</string> | ||
| 99 | <string name="firmware_installed_failure_description">请确ä¿å›ºä»¶ nca 文件ä½äºŽ zip åŽ‹ç¼©åŒ…çš„æ ¹ç›®å½•ï¼Œç„¶åŽé‡è¯•。</string> | ||
| 100 | <string name="share_log">分享调试日志</string> | ||
| 101 | <string name="share_log_description">分享 yuzu 日志文件以便调试</string> | ||
| 102 | <string name="share_log_missing">未找到日志文件</string> | ||
| 103 | <string name="install_game_content">安装游æˆé™„åŠ å†…å®¹</string> | ||
| 104 | <string name="install_game_content_description">å®‰è£…æ¸¸æˆæ›´æ–°åŠ DLC</string> | ||
| 105 | <string name="installing_game_content">安装ä¸...</string> | ||
| 106 | <string name="install_game_content_failure">å‘ NAND 安装文件时失败</string> | ||
| 107 | <string name="install_game_content_failure_description">请确ä¿é™„åŠ å†…å®¹çš„æœ‰æ•ˆæ€§ï¼Œå¹¶ä¸” prod.keys 密钥文件已安装。</string> | ||
| 108 | <string name="install_game_content_failure_base">为é¿å…产生冲çªï¼Œæ¤åŠŸèƒ½ä¸èƒ½ç”¨äºŽå®‰è£…æ¸¸æˆæœ¬ä½“。</string> | ||
| 109 | <string name="install_game_content_failure_file_extension">åªæœ‰ NSP 或 XCI æ ¼å¼çš„é™„åŠ å†…å®¹å¯ä»¥å®‰è£…ã€‚è¯·ç¡®ä¿æ‚¨çš„æ¸¸æˆé™„åŠ å†…å®¹æ˜¯æœ‰æ•ˆçš„ã€‚</string> | ||
| 110 | <string name="install_game_content_failed_count">%1$d 安装出错</string> | ||
| 111 | <string name="install_game_content_success">游æˆé™„åŠ å†…å®¹å·²æˆåŠŸå®‰è£…</string> | ||
| 112 | <string name="install_game_content_success_install">%1$d 安装æˆåŠŸ</string> | ||
| 113 | <string name="install_game_content_success_overwrite">%1$d 覆盖安装æˆåŠŸ</string> | ||
| 114 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 115 | <string name="custom_driver_not_supported">䏿”¯æŒè‡ªå®šä¹‰é©±åЍ</string> | ||
| 116 | <string name="custom_driver_not_supported_description">æ¤è®¾å¤‡ä¸æ”¯æŒè‡ªå®šä¹‰é©±åŠ¨ã€‚\n请之åŽå†è®¿é—®æ¤é¡¹ï¼ŒæŸ¥çœ‹æ˜¯å¦å·²ä¸ºæ¤è®¾å¤‡æ·»åŠ æ”¯æŒã€‚</string> | ||
| 117 | <string name="manage_yuzu_data">ç®¡ç† yuzu æ•°æ®</string> | ||
| 118 | <string name="manage_yuzu_data_description">导入/导出固件ã€å¯†é’¥ã€ç”¨æˆ·æ•°æ®åŠå…¶ä»–。</string> | ||
| 119 | <string name="share_save_file">åˆ†äº«å˜æ¡£æ–‡ä»¶</string> | ||
| 120 | <string name="export_save_failed">å¯¼å‡ºå˜æ¡£æ–‡ä»¶å¤±è´¥</string> | ||
| 89 | 121 | ||
| 90 | <!-- About screen strings --> | 122 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">Gaia ä¸çœŸå®ž</string> | 123 | <string name="gaia_is_not_real">Gaia ä¸çœŸå®ž</string> |
| @@ -94,14 +126,25 @@ | |||
| 94 | <string name="contributors">贡献者</string> | 126 | <string name="contributors">贡献者</string> |
| 95 | <string name="contributors_description">使用æ¥è‡ª yuzu 团队的 \u2764 制作</string> | 127 | <string name="contributors_description">使用æ¥è‡ª yuzu 团队的 \u2764 制作</string> |
| 96 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 128 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 129 | <string name="licenses_description">Android 版 yuzu 离ä¸å¼€è¿™äº›é¡¹ç›®çš„æ”¯æŒ</string> | ||
| 97 | <string name="build">构建版本</string> | 130 | <string name="build">构建版本</string> |
| 131 | <string name="user_data">用户数æ®</string> | ||
| 132 | <string name="user_data_description">导入/å¯¼å‡ºåº”ç”¨ç¨‹åºæ‰€æœ‰æ•°æ®ã€‚\n\nå¯¼å…¥ç”¨æˆ·æ•°æ®æ—¶ï¼Œå°†åˆ 除当剿‰€æœ‰çš„用户数æ®ï¼</string> | ||
| 133 | <string name="exporting_user_data">æ£åœ¨å¯¼å‡ºç”¨æˆ·æ•°æ®...</string> | ||
| 134 | <string name="importing_user_data">æ£åœ¨å¯¼å…¥ç”¨æˆ·æ•°æ®...</string> | ||
| 135 | <string name="import_user_data">导入用户数æ®</string> | ||
| 136 | <string name="invalid_yuzu_backup">æ— æ•ˆçš„ yuzu 备份</string> | ||
| 137 | <string name="user_data_export_success">å¯¼å‡ºç”¨æˆ·æ•°æ®æˆåŠŸ</string> | ||
| 138 | <string name="user_data_import_success">å¯¼å…¥ç”¨æˆ·æ•°æ®æˆåŠŸ</string> | ||
| 139 | <string name="user_data_export_cancelled">已喿¶ˆå¯¼å‡ºæ•°æ®</string> | ||
| 140 | <string name="user_data_import_failed_description">请确ä¿ç”¨æˆ·æ•°æ®æ–‡ä»¶å¤¹ä½äºŽ zip åŽ‹ç¼©åŒ…çš„æ ¹ç›®å½•ï¼Œå¹¶åœ¨ config/config.ini 路径ä¸åŒ…å«é…置文件,然åŽé‡è¯•。</string> | ||
| 98 | <string name="support_link">https://discord.gg/u77vRWY</string> | 141 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 99 | <string name="website_link">https://yuzu-emu.org/</string> | 142 | <string name="website_link">https://yuzu-emu.org/</string> |
| 100 | <string name="github_link">https://github.com/yuzu-emu</string> | 143 | <string name="github_link">https://github.com/yuzu-emu</string> |
| 101 | 144 | ||
| 102 | <!-- Early access upgrade strings --> | 145 | <!-- Early access upgrade strings --> |
| 103 | <string name="early_access">抢先体验</string> | 146 | <string name="early_access">抢先体验</string> |
| 104 | <string name="get_early_access">å–得抢先体验</string> | 147 | <string name="get_early_access">èŽ·å–æŠ¢å…ˆä½“éªŒï¼</string> |
| 105 | <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> | 148 | <string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string> |
| 106 | <string name="get_early_access_description">æœ€æ–°çš„åŠŸèƒ½ã€æŠ¢å…ˆæ›´æ–°ã€ä»¥åŠæ›´å¤š</string> | 149 | <string name="get_early_access_description">æœ€æ–°çš„åŠŸèƒ½ã€æŠ¢å…ˆæ›´æ–°ã€ä»¥åŠæ›´å¤š</string> |
| 107 | <string name="early_access_benefits">抢先体验的æƒç›Š</string> | 150 | <string name="early_access_benefits">抢先体验的æƒç›Š</string> |
| @@ -109,33 +152,34 @@ | |||
| 109 | <string name="early_access_updates">抢先更新</string> | 152 | <string name="early_access_updates">抢先更新</string> |
| 110 | <string name="no_manual_installation">æ— éœ€æ‰‹åŠ¨å®‰è£…</string> | 153 | <string name="no_manual_installation">æ— éœ€æ‰‹åŠ¨å®‰è£…</string> |
| 111 | <string name="prioritized_support">优先支æŒ</string> | 154 | <string name="prioritized_support">优先支æŒ</string> |
| 112 | <string name="helping_game_preservation">帮助ä¿ç•™æ¸¸æˆ</string> | 155 | <string name="helping_game_preservation">帮助ä¿ç•™æ¸¸çŽ©åŽ†å²</string> |
| 113 | <string name="our_eternal_gratitude">我们真诚的感激</string> | 156 | <string name="our_eternal_gratitude">我们真诚的感激</string> |
| 114 | <string name="are_you_interested">æ‚¨å¯¹æ¤æ„Ÿå…´è¶£å—?</string> | 157 | <string name="are_you_interested">æ‚¨å¯¹æ¤æ„Ÿå…´è¶£å—?</string> |
| 115 | 158 | ||
| 116 | <!-- General settings strings --> | 159 | <!-- General settings strings --> |
| 117 | <string name="frame_limit_enable">å¯ç”¨è¿è¡Œé€Ÿåº¦é™åˆ¶</string> | 160 | <string name="frame_limit_enable">è¿è¡Œé€Ÿåº¦é™åˆ¶</string> |
| 118 | <string name="frame_limit_enable_description">å¯ç”¨åŽï¼Œæ¨¡æ‹Ÿé€Ÿåº¦å°†é™åˆ¶åœ¨æ£å¸¸è¿è¡Œé€Ÿåº¦çš„æŒ‡å®šç™¾åˆ†æ¯”。</string> | 161 | <string name="frame_limit_enable_description">å°†è¿è¡Œé€Ÿåº¦é™åˆ¶ä¸ºæ£å¸¸é€Ÿåº¦çš„æŒ‡å®šç™¾åˆ†æ¯”。</string> |
| 119 | <string name="frame_limit_slider">é™åˆ¶é€Ÿåº¦ç™¾åˆ†æ¯”</string> | 162 | <string name="frame_limit_slider">é™åˆ¶é€Ÿåº¦ç™¾åˆ†æ¯”</string> |
| 120 | <string name="frame_limit_slider_description">指定é™åˆ¶æ¨¡æ‹Ÿé€Ÿåº¦çš„百分比。预设为 100%ï¼Œæ¤æ—¶æ¨¡æ‹Ÿé€Ÿåº¦å°†è¢«é™åˆ¶ä¸ºæ ‡å‡†é€Ÿåº¦ã€‚æ›´é«˜æˆ–æ›´ä½Žçš„å€¼å°†å¢žåŠ æˆ–é™ä½Žé€Ÿåº¦é™åˆ¶ä¸Šé™ã€‚</string> | 163 | <string name="frame_limit_slider_description">指定é™åˆ¶è¿è¡Œé€Ÿåº¦çš„百分比。100% 为æ£å¸¸é€Ÿåº¦ã€‚æ›´é«˜æˆ–æ›´ä½Žçš„å€¼å°†å¢žåŠ æˆ–é™ä½Žé€Ÿåº¦é™åˆ¶ä¸Šé™ã€‚</string> |
| 121 | <string name="cpu_accuracy">CPU 精度</string> | 164 | <string name="cpu_accuracy">CPU 精度</string> |
| 165 | <string name="value_with_units">%1$s%2$s</string> | ||
| 122 | 166 | ||
| 123 | <!-- System settings strings --> | 167 | <!-- System settings strings --> |
| 124 | <string name="use_docked_mode">主机模å¼</string> | 168 | <string name="use_docked_mode">主机模å¼</string> |
| 125 | <string name="use_docked_mode_description">以主机模å¼è¿›è¡Œæ¨¡æ‹Ÿï¼Œç‰ºç‰²æ€§èƒ½å¹¶æé«˜ç”»é¢åˆ†è¾¨çŽ‡ã€‚</string> | 169 | <string name="use_docked_mode_description">æé«˜åˆ†è¾¨çŽ‡ï¼Œä½†é™ä½Žæ€§èƒ½ã€‚ç¦ç”¨æ¤é¡¹æ—¶ä½¿ç”¨æŽŒæœºæ¨¡å¼ï¼Œé™ä½Žåˆ†è¾¨çއ并æé«˜æ€§èƒ½ã€‚</string> |
| 126 | <string name="emulated_region">模拟区域</string> | 170 | <string name="emulated_region">模拟区域</string> |
| 127 | <string name="emulated_language">模拟è¯è¨€</string> | 171 | <string name="emulated_language">模拟è¯è¨€</string> |
| 128 | <string name="select_rtc_date">选择日期</string> | 172 | <string name="select_rtc_date">选择日期</string> |
| 129 | <string name="select_rtc_time">选择时间</string> | 173 | <string name="select_rtc_time">选择时间</string> |
| 130 | <string name="use_custom_rtc">å¯ç”¨è‡ªå®šä¹‰ç³»ç»Ÿæ—¶é’Ÿ</string> | 174 | <string name="use_custom_rtc">自定义系统时间</string> |
| 131 | <string name="use_custom_rtc_description">æ¤é€‰é¡¹å…许您设置与目å‰ç³»ç»Ÿæ—¶é—´ç›¸ç‹¬ç«‹çš„自定义系统时钟</string> | 175 | <string name="use_custom_rtc_description">æ¤é€‰é¡¹å…许您设置与目å‰ç³»ç»Ÿæ—¶é—´ç›¸ç‹¬ç«‹çš„自定义系统时钟。</string> |
| 132 | <string name="set_custom_rtc">设置自定义系统时钟</string> | 176 | <string name="set_custom_rtc">设置自定义系统时间</string> |
| 133 | 177 | ||
| 134 | <!-- Graphics settings strings --> | 178 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">API</string> | ||
| 136 | <string name="renderer_accuracy">精度ç‰çº§</string> | 179 | <string name="renderer_accuracy">精度ç‰çº§</string> |
| 137 | <string name="renderer_resolution">分辨率</string> | 180 | <string name="renderer_resolution">分辨率 (掌机模å¼/主机模å¼)</string> |
| 138 | <string name="renderer_vsync">åž‚ç›´åŒæ¥æ¨¡å¼</string> | 181 | <string name="renderer_vsync">åž‚ç›´åŒæ¥æ¨¡å¼</string> |
| 182 | <string name="renderer_screen_layout">å±å¹•æ–¹å‘</string> | ||
| 139 | <string name="renderer_aspect_ratio">å±å¹•纵横比</string> | 183 | <string name="renderer_aspect_ratio">å±å¹•纵横比</string> |
| 140 | <string name="renderer_scaling_filter">çª—å£æ»¤é•œ</string> | 184 | <string name="renderer_scaling_filter">çª—å£æ»¤é•œ</string> |
| 141 | <string name="renderer_anti_aliasing">抗锯齿方å¼</string> | 185 | <string name="renderer_anti_aliasing">抗锯齿方å¼</string> |
| @@ -143,12 +187,23 @@ | |||
| 143 | <string name="renderer_force_max_clock_description">强制 GPU 以最大时钟è¿è¡Œ (ä»è¢«æ¸©æŽ§é™åˆ¶)。</string> | 187 | <string name="renderer_force_max_clock_description">强制 GPU 以最大时钟è¿è¡Œ (ä»è¢«æ¸©æŽ§é™åˆ¶)。</string> |
| 144 | <string name="renderer_asynchronous_shaders">使用异æ¥ç€è‰²å™¨</string> | 188 | <string name="renderer_asynchronous_shaders">使用异æ¥ç€è‰²å™¨</string> |
| 145 | <string name="renderer_asynchronous_shaders_description">异æ¥ç¼–译ç€è‰²å™¨ï¼Œå‡å°‘å¡é¡¿ï¼Œä½†å¯èƒ½å¼•入故障。</string> | 189 | <string name="renderer_asynchronous_shaders_description">异æ¥ç¼–译ç€è‰²å™¨ï¼Œå‡å°‘å¡é¡¿ï¼Œä½†å¯èƒ½å¼•入故障。</string> |
| 146 | <string name="renderer_debug">å¯ç”¨å›¾å½¢è°ƒè¯•</string> | 190 | <string name="renderer_reactive_flushing">å¯ç”¨å应性刷新</string> |
| 147 | <string name="renderer_debug_description">å¯ç”¨æ—¶ï¼Œå›¾å½¢ API 将进入较慢的调试模å¼ã€‚</string> | 191 | <string name="renderer_reactive_flushing_description">牺牲性能,æé«˜æŸäº›æ¸¸æˆçš„æ¸²æŸ“精度。</string> |
| 148 | <string name="use_disk_shader_cache">使用ç£ç›˜ç€è‰²å™¨ç¼“å˜</string> | 192 | <string name="use_disk_shader_cache">ç£ç›˜ç€è‰²å™¨ç¼“å˜</string> |
| 149 | <string name="use_disk_shader_cache_description">将生æˆçš„ç€è‰²å™¨ç¼“å˜äºŽç£ç›˜ä¸å¹¶è¿›è¡Œè¯»å–以å‡å°‘å¡é¡¿ã€‚</string> | 193 | <string name="use_disk_shader_cache_description">将生æˆçš„ç€è‰²å™¨ç¼“å˜äºŽç£ç›˜ä¸å¹¶è¿›è¡Œè¯»å–,以å‡å°‘å¡é¡¿ã€‚</string> |
| 194 | |||
| 195 | <!-- Debug settings strings --> | ||
| 196 | <string name="cpu">CPU</string> | ||
| 197 | <string name="cpu_debug_mode">CPU 调试</string> | ||
| 198 | <string name="cpu_debug_mode_description">å°† CPU 设置为较慢的调试模å¼ã€‚</string> | ||
| 199 | <string name="gpu">GPU</string> | ||
| 200 | <string name="renderer_api">API</string> | ||
| 201 | <string name="renderer_debug">图形调试</string> | ||
| 202 | <string name="renderer_debug_description">将图形 API 设置为较慢的调试模å¼ã€‚</string> | ||
| 203 | <string name="fastmem">Fastmem</string> | ||
| 150 | 204 | ||
| 151 | <!-- Audio settings strings --> | 205 | <!-- Audio settings strings --> |
| 206 | <string name="audio_output_engine">输出引擎</string> | ||
| 152 | <string name="audio_volume">音é‡</string> | 207 | <string name="audio_volume">音é‡</string> |
| 153 | <string name="audio_volume_description">指定输出的音é‡ã€‚</string> | 208 | <string name="audio_volume_description">指定输出的音é‡ã€‚</string> |
| 154 | 209 | ||
| @@ -157,7 +212,9 @@ | |||
| 157 | <string name="ini_saved">å·²ä¿å˜è®¾ç½®</string> | 212 | <string name="ini_saved">å·²ä¿å˜è®¾ç½®</string> |
| 158 | <string name="gameid_saved">å·²ä¿å˜ %1$s 的设置</string> | 213 | <string name="gameid_saved">å·²ä¿å˜ %1$s 的设置</string> |
| 159 | <string name="error_saving">ä¿å˜ %1$s.ini 时出错: %2$s</string> | 214 | <string name="error_saving">ä¿å˜ %1$s.ini 时出错: %2$s</string> |
| 215 | <string name="unimplemented_menu">未生效èœå•</string> | ||
| 160 | <string name="loading">åŠ è½½ä¸â€¦</string> | 216 | <string name="loading">åŠ è½½ä¸â€¦</string> |
| 217 | <string name="shutting_down">æ£åœ¨å…³é—…</string> | ||
| 161 | <string name="reset_setting_confirmation">您è¦å°†æ¤è®¾å®šé‡è®¾ä¸ºé»˜è®¤å€¼å—?</string> | 218 | <string name="reset_setting_confirmation">您è¦å°†æ¤è®¾å®šé‡è®¾ä¸ºé»˜è®¤å€¼å—?</string> |
| 162 | <string name="reset_to_default">æ¢å¤é»˜è®¤</string> | 219 | <string name="reset_to_default">æ¢å¤é»˜è®¤</string> |
| 163 | <string name="reset_all_settings">é‡ç½®æ‰€æœ‰è®¾ç½®é¡¹ï¼Ÿ</string> | 220 | <string name="reset_all_settings">é‡ç½®æ‰€æœ‰è®¾ç½®é¡¹ï¼Ÿ</string> |
| @@ -165,6 +222,14 @@ | |||
| 165 | <string name="settings_reset">é‡è®¾è®¾ç½®é¡¹</string> | 222 | <string name="settings_reset">é‡è®¾è®¾ç½®é¡¹</string> |
| 166 | <string name="close">å…³é—</string> | 223 | <string name="close">å…³é—</string> |
| 167 | <string name="learn_more">了解更多</string> | 224 | <string name="learn_more">了解更多</string> |
| 225 | <string name="auto">自动</string> | ||
| 226 | <string name="submit">æäº¤</string> | ||
| 227 | <string name="string_null">æ— </string> | ||
| 228 | <string name="string_import">导入</string> | ||
| 229 | <string name="export">导出</string> | ||
| 230 | <string name="export_failed">导出失败</string> | ||
| 231 | <string name="import_failed">导入失败</string> | ||
| 232 | <string name="cancelling">å–æ¶ˆä¸</string> | ||
| 168 | 233 | ||
| 169 | <!-- GPU driver installation --> | 234 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">选择 GPU 驱动程åº</string> | 235 | <string name="select_gpu_driver">选择 GPU 驱动程åº</string> |
| @@ -172,6 +237,7 @@ | |||
| 172 | <string name="select_gpu_driver_install">安装</string> | 237 | <string name="select_gpu_driver_install">安装</string> |
| 173 | <string name="select_gpu_driver_default">系统默认</string> | 238 | <string name="select_gpu_driver_default">系统默认</string> |
| 174 | <string name="select_gpu_driver_use_default">使用默认 GPU 驱动程åº</string> | 239 | <string name="select_gpu_driver_use_default">使用默认 GPU 驱动程åº</string> |
| 240 | <string name="select_gpu_driver_error">é€‰æ‹©çš„é©±åŠ¨ç¨‹åºæ— 效,将使用系统默认的驱动程åºï¼</string> | ||
| 175 | <string name="system_gpu_driver">系统 GPU 驱动程åº</string> | 241 | <string name="system_gpu_driver">系统 GPU 驱动程åº</string> |
| 176 | <string name="installing_driver">æ£åœ¨å®‰è£…驱动程åºâ€¦</string> | 242 | <string name="installing_driver">æ£åœ¨å®‰è£…驱动程åºâ€¦</string> |
| 177 | 243 | ||
| @@ -182,10 +248,11 @@ | |||
| 182 | <string name="preferences_graphics">图形</string> | 248 | <string name="preferences_graphics">图形</string> |
| 183 | <string name="preferences_audio">声音</string> | 249 | <string name="preferences_audio">声音</string> |
| 184 | <string name="preferences_theme">主题和色彩</string> | 250 | <string name="preferences_theme">主题和色彩</string> |
| 251 | <string name="preferences_debug">调试</string> | ||
| 185 | 252 | ||
| 186 | <!-- ROM loading errors --> | 253 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">您的 ROM å·²åŠ å¯†</string> | 254 | <string name="loader_error_encrypted">您的 ROM å·²åŠ å¯†</string> |
| 188 | <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> | 255 | <string name="loader_error_encrypted_roms_description"><![CDATA[请按照指å—釿–°è½¬å‚¨æ‚¨çš„<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">游æˆå¡å¸¦</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">已安装的游æˆ</a>。]]></string> |
| 189 | <string name="loader_error_encrypted_keys_description"><![CDATA[è¯·ç¡®ä¿ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 文件已安装,使得游æˆå¯ä»¥è¢«è§£å¯†ã€‚]]></string> | 256 | <string name="loader_error_encrypted_keys_description"><![CDATA[è¯·ç¡®ä¿ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 文件已安装,使得游æˆå¯ä»¥è¢«è§£å¯†ã€‚]]></string> |
| 190 | <string name="loader_error_video_core">åˆå§‹åŒ–è§†é¢‘æ ¸å¿ƒæ—¶å‘生错误</string> | 257 | <string name="loader_error_video_core">åˆå§‹åŒ–è§†é¢‘æ ¸å¿ƒæ—¶å‘生错误</string> |
| 191 | <string name="loader_error_video_core_description">这通常由ä¸å…¼å®¹çš„ GPU 驱动程åºé€ æˆï¼Œå®‰è£…自定义 GPU 驱动程åºå¯èƒ½è§£å†³æ¤é—®é¢˜ã€‚</string> | 258 | <string name="loader_error_video_core_description">这通常由ä¸å…¼å®¹çš„ GPU 驱动程åºé€ æˆï¼Œå®‰è£…自定义 GPU 驱动程åºå¯èƒ½è§£å†³æ¤é—®é¢˜ã€‚</string> |
| @@ -226,6 +293,9 @@ | |||
| 226 | <string name="fatal_error">致命错误</string> | 293 | <string name="fatal_error">致命错误</string> |
| 227 | <string name="fatal_error_message">å‘生致命错误,请查阅日志获å–详细信æ¯ã€‚\nç»§ç»æ¨¡æ‹Ÿå¯èƒ½ä¼šé€ æˆå´©æºƒå’Œé”™è¯¯ã€‚</string> | 294 | <string name="fatal_error_message">å‘生致命错误,请查阅日志获å–详细信æ¯ã€‚\nç»§ç»æ¨¡æ‹Ÿå¯èƒ½ä¼šé€ æˆå´©æºƒå’Œé”™è¯¯ã€‚</string> |
| 228 | <string name="performance_warning">关闿¤é¡¹ä¼šæ˜¾è‘—é™ä½Žæ¨¡æ‹Ÿæ€§èƒ½ï¼å»ºè®®æ‚¨å°†æ¤é¡¹ä¿æŒä¸ºå¯ç”¨çжæ€ã€‚</string> | 295 | <string name="performance_warning">关闿¤é¡¹ä¼šæ˜¾è‘—é™ä½Žæ¨¡æ‹Ÿæ€§èƒ½ï¼å»ºè®®æ‚¨å°†æ¤é¡¹ä¿æŒä¸ºå¯ç”¨çжæ€ã€‚</string> |
| 296 | <string name="device_memory_inadequate">设备 RAM: %1$s\n推è RAM: %2$s</string> | ||
| 297 | <string name="memory_formatted">%1$s%2$s</string> | ||
| 298 | <string name="no_game_present">当剿²¡æœ‰å¯å¯åŠ¨çš„æ¸¸æˆï¼</string> | ||
| 229 | 299 | ||
| 230 | <!-- Region Names --> | 300 | <!-- Region Names --> |
| 231 | <string name="region_japan">日本</string> | 301 | <string name="region_japan">日本</string> |
| @@ -236,7 +306,14 @@ | |||
| 236 | <string name="region_korea">韩国</string> | 306 | <string name="region_korea">韩国</string> |
| 237 | <string name="region_taiwan">ä¸å›½å°æ¹¾</string> | 307 | <string name="region_taiwan">ä¸å›½å°æ¹¾</string> |
| 238 | 308 | ||
| 239 | <!-- Language Names --> | 309 | <!-- Memory Sizes --> |
| 310 | <string name="memory_byte">Byte</string> | ||
| 311 | <string name="memory_kilobyte">KB</string> | ||
| 312 | <string name="memory_megabyte">MB</string> | ||
| 313 | <string name="memory_gigabyte">GB</string> | ||
| 314 | <string name="memory_terabyte">TB</string> | ||
| 315 | <string name="memory_petabyte">PB</string> | ||
| 316 | <string name="memory_exabyte">EB</string> | ||
| 240 | 317 | ||
| 241 | <!-- Renderer APIs --> | 318 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">Vulkan</string> | 319 | <string name="renderer_vulkan">Vulkan</string> |
| @@ -274,6 +351,11 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">快速近似抗锯齿</string> | 351 | <string name="anti_aliasing_fxaa">快速近似抗锯齿</string> |
| 275 | <string name="anti_aliasing_smaa">ååƒç´ å½¢æ€å¦æŠ—锯齿</string> | 352 | <string name="anti_aliasing_smaa">ååƒç´ å½¢æ€å¦æŠ—锯齿</string> |
| 276 | 353 | ||
| 354 | <!-- Screen Layouts --> | ||
| 355 | <string name="screen_layout_landscape">横å‘大å±</string> | ||
| 356 | <string name="screen_layout_portrait">纵å‘å±å¹•</string> | ||
| 357 | <string name="screen_layout_auto">自动</string> | ||
| 358 | |||
| 277 | <!-- Aspect Ratios --> | 359 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">默认 (16:9)</string> | 360 | <string name="ratio_default">默认 (16:9)</string> |
| 279 | <string name="ratio_force_four_three">强制 4:3</string> | 361 | <string name="ratio_force_four_three">强制 4:3</string> |
| @@ -303,13 +385,27 @@ | |||
| 303 | <string name="theme_material_you">Material You</string> | 385 | <string name="theme_material_you">Material You</string> |
| 304 | 386 | ||
| 305 | <!-- Theme Modes --> | 387 | <!-- Theme Modes --> |
| 306 | <string name="change_theme_mode">主题模å¼</string> | 388 | <string name="change_theme_mode">更改主题模å¼</string> |
| 307 | <string name="theme_mode_follow_system">è·Ÿéšç³»ç»Ÿ</string> | 389 | <string name="theme_mode_follow_system">è·Ÿéšç³»ç»Ÿ</string> |
| 308 | <string name="theme_mode_light">浅色</string> | 390 | <string name="theme_mode_light">浅色</string> |
| 309 | <string name="theme_mode_dark">深色</string> | 391 | <string name="theme_mode_dark">深色</string> |
| 310 | 392 | ||
| 393 | <!-- Audio output engines --> | ||
| 394 | <string name="cubeb">cubeb</string> | ||
| 395 | |||
| 311 | <!-- Black backgrounds theme --> | 396 | <!-- Black backgrounds theme --> |
| 312 | <string name="use_black_backgrounds">使用黑色背景</string> | 397 | <string name="use_black_backgrounds">使用黑色背景</string> |
| 313 | <string name="use_black_backgrounds_description">使用深色主题时,套用黑色背景。</string> | 398 | <string name="use_black_backgrounds_description">使用深色主题时,套用黑色背景。</string> |
| 314 | 399 | ||
| 315 | </resources> | 400 | <!-- Picture-In-Picture --> |
| 401 | <string name="picture_in_picture">ç”»ä¸ç”»</string> | ||
| 402 | <string name="picture_in_picture_description">模拟器ä½äºŽåŽå°æ—¶æœ€å°åŒ–窗å£</string> | ||
| 403 | <string name="pause">æš‚åœ</string> | ||
| 404 | <string name="play">开始</string> | ||
| 405 | <string name="mute">é™éŸ³</string> | ||
| 406 | <string name="unmute">å–æ¶ˆé™éŸ³</string> | ||
| 407 | |||
| 408 | <!-- Licenses screen strings --> | ||
| 409 | <string name="licenses">许å¯è¯</string> | ||
| 410 | <string name="license_fidelityfx_fsr_description">æ¥è‡ª AMD 的高å“质画质å‡çº§</string> | ||
| 411 | </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 index 4a21bf893..b8f468c68 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <resources> | 2 | <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> |
| 3 | 3 | ||
| 4 | <string name="app_disclaimer">æ¤è»Ÿé«”å¯ä»¥åŸ·è¡Œ Nintendo Switch ä¸»æ©ŸéŠæˆ²ï¼Œä½†ä¸åŒ…å«ä»»ä½•éŠæˆ²å’Œé‡‘鑰。<br /><br />在您開始å‰ï¼Œè«‹æ‰¾åˆ°æ”¾ç½®æ–¼æ‚¨çš„è£ç½®å„²å˜ç©ºé–“çš„ <![CDATA[<b> prod.keys </b>]]> 檔案。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入çžè§£</a>]]></string> | 4 | <string name="app_disclaimer">æ¤è»Ÿé«”å¯ä»¥åŸ·è¡Œ Nintendo Switch ä¸»æ©ŸéŠæˆ²ï¼Œä½†ä¸åŒ…å«ä»»ä½•éŠæˆ²å’Œé‡‘鑰。<br /><br />在您開始å‰ï¼Œè«‹æ‰¾åˆ°æ”¾ç½®æ–¼æ‚¨çš„è£ç½®å„²å˜ç©ºé–“çš„ <![CDATA[<b> prod.keys </b>]]> 檔案。<br /><br /><![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入çžè§£</a>]]></string> |
| 5 | <string name="emulation_notification_channel_name">模擬進行ä¸</string> | 5 | <string name="emulation_notification_channel_name">模擬進行ä¸</string> |
| @@ -25,6 +25,7 @@ | |||
| 25 | <string name="back">上一æ¥</string> | 25 | <string name="back">上一æ¥</string> |
| 26 | <string name="add_games">æ–°å¢žéŠæˆ²</string> | 26 | <string name="add_games">æ–°å¢žéŠæˆ²</string> |
| 27 | <string name="add_games_description">é¸å–æ‚¨çš„éŠæˆ²è³‡æ–™å¤¾</string> | 27 | <string name="add_games_description">é¸å–æ‚¨çš„éŠæˆ²è³‡æ–™å¤¾</string> |
| 28 | <string name="step_complete">完æˆï¼</string> | ||
| 28 | 29 | ||
| 29 | <!-- Home strings --> | 30 | <!-- Home strings --> |
| 30 | <string name="home_games">éŠæˆ²</string> | 31 | <string name="home_games">éŠæˆ²</string> |
| @@ -33,11 +34,12 @@ | |||
| 33 | <string name="empty_gamelist">找ä¸åˆ°æª”案,或者尚未é¸å–éŠæˆ²ç›®éŒ„。</string> | 34 | <string name="empty_gamelist">找ä¸åˆ°æª”案,或者尚未é¸å–éŠæˆ²ç›®éŒ„。</string> |
| 34 | <string name="search_and_filter_games">æœå°‹ä¸¦ç¯©é¸éŠæˆ²</string> | 35 | <string name="search_and_filter_games">æœå°‹ä¸¦ç¯©é¸éŠæˆ²</string> |
| 35 | <string name="select_games_folder">é¸å–éŠæˆ²è³‡æ–™å¤¾</string> | 36 | <string name="select_games_folder">é¸å–éŠæˆ²è³‡æ–™å¤¾</string> |
| 36 | <string name="select_games_folder_description">一律å…許 yuzu å¡«å…¥éŠæˆ²æ¸…å–®</string> | 37 | <string name="select_games_folder_description">å…許 yuzu å¡«å…¥éŠæˆ²æ¸…å–®</string> |
| 37 | <string name="add_games_warning">è·³éŽé¸å–éŠæˆ²è³‡æ–™å¤¾ï¼Ÿ</string> | 38 | <string name="add_games_warning">è·³éŽé¸å–éŠæˆ²è³‡æ–™å¤¾ï¼Ÿ</string> |
| 38 | <string name="add_games_warning_description">如果資料夾未é¸å–ï¼ŒéŠæˆ²å°‡ä¸æœƒé¡¯ç¤ºåœ¨éŠæˆ²æ¸…單。</string> | 39 | <string name="add_games_warning_description">如果資料夾未é¸å–ï¼ŒéŠæˆ²å°‡ä¸æœƒé¡¯ç¤ºåœ¨éŠæˆ²æ¸…單。</string> |
| 39 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 40 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 40 | <string name="home_search_games">æœå°‹éŠæˆ²</string> | 41 | <string name="home_search_games">æœå°‹éŠæˆ²</string> |
| 42 | <string name="search_settings">æœç´¢è®¾ç½®</string> | ||
| 41 | <string name="games_dir_selected">éŠæˆ²ç›®éŒ„å·²é¸å–</string> | 43 | <string name="games_dir_selected">éŠæˆ²ç›®éŒ„å·²é¸å–</string> |
| 42 | <string name="install_prod_keys">å®‰è£ prod.keys</string> | 44 | <string name="install_prod_keys">å®‰è£ prod.keys</string> |
| 43 | <string name="install_prod_keys_description">需è¦è§£å¯†é›¶å”®éŠæˆ²</string> | 45 | <string name="install_prod_keys_description">需è¦è§£å¯†é›¶å”®éŠæˆ²</string> |
| @@ -60,13 +62,16 @@ | |||
| 60 | <string name="install_amiibo_keys_description">需è¦åœ¨éŠæˆ²ä¸ä½¿ç”¨ Amiibo</string> | 62 | <string name="install_amiibo_keys_description">需è¦åœ¨éŠæˆ²ä¸ä½¿ç”¨ Amiibo</string> |
| 61 | <string name="invalid_keys_file">無效的金鑰檔案已é¸å–</string> | 63 | <string name="invalid_keys_file">無效的金鑰檔案已é¸å–</string> |
| 62 | <string name="install_keys_success">金鑰已æˆåŠŸå®‰è£</string> | 64 | <string name="install_keys_success">金鑰已æˆåŠŸå®‰è£</string> |
| 63 | <string name="reading_keys_failure">讀å–åŠ å¯†é‡‘é‘°æ™‚å‡ºç¾éŒ¯èª¤</string> | 65 | <string name="reading_keys_failure">讀å–åŠ å¯†é‡‘é‘°æ™‚ç™¼ç”ŸéŒ¯èª¤</string> |
| 66 | <string name="install_prod_keys_failure_extension_description">é©—è‰æ‚¨çš„金鑰檔案是å¦å…·æœ‰ .keys 副檔å並å†è©¦ä¸€æ¬¡ã€‚</string> | ||
| 67 | <string name="install_amiibo_keys_failure_extension_description">é©—è‰æ‚¨çš„金鑰檔案是å¦å…·æœ‰ .bin 副檔å並å†è©¦ä¸€æ¬¡ã€‚</string> | ||
| 64 | <string name="invalid_keys_error">ç„¡æ•ˆçš„åŠ å¯†é‡‘é‘°</string> | 68 | <string name="invalid_keys_error">ç„¡æ•ˆçš„åŠ å¯†é‡‘é‘°</string> |
| 65 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 69 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 66 | <string name="install_keys_failure_description">é¸å–çš„æª”æ¡ˆä¸æ£ç¢ºæˆ–å·²ææ¯€ï¼Œè«‹é‡æ–°å‚¾å°æ‚¨çš„金鑰。</string> | 70 | <string name="install_keys_failure_description">é¸å–çš„æª”æ¡ˆä¸æ£ç¢ºæˆ–å·²ææ¯€ï¼Œè«‹é‡æ–°å‚¾å°æ‚¨çš„金鑰。</string> |
| 67 | <string name="install_gpu_driver">å®‰è£ GPU 驅動程å¼</string> | 71 | <string name="install_gpu_driver">å®‰è£ GPU 驅動程å¼</string> |
| 68 | <string name="install_gpu_driver_description">å®‰è£æ›¿ä»£é©…動程å¼ä»¥å–得潛在的更佳效能或準確度</string> | 72 | <string name="install_gpu_driver_description">å®‰è£æ›¿ä»£é©…動程å¼ä»¥å–得潛在的更佳效能或準確度</string> |
| 69 | <string name="advanced_settings">進階è¨å®š</string> | 73 | <string name="advanced_settings">進階è¨å®š</string> |
| 74 | <string name="advanced_settings_game">高级选项: %1$s</string> | ||
| 70 | <string name="settings_description">進行模擬器è¨å®š</string> | 75 | <string name="settings_description">進行模擬器è¨å®š</string> |
| 71 | <string name="search_recently_played">最近éŠçŽ©</string> | 76 | <string name="search_recently_played">最近éŠçŽ©</string> |
| 72 | <string name="search_recently_added">最近新增</string> | 77 | <string name="search_recently_added">最近新增</string> |
| @@ -86,6 +91,33 @@ | |||
| 86 | <string name="save_file_invalid_zip_structure_description">首個å資料夾åç¨±å¿…é ˆç‚ºéŠæˆ²æ¨™é¡Œ ID。</string> | 91 | <string name="save_file_invalid_zip_structure_description">首個å資料夾åç¨±å¿…é ˆç‚ºéŠæˆ²æ¨™é¡Œ ID。</string> |
| 87 | <string name="import_saves">匯入</string> | 92 | <string name="import_saves">匯入</string> |
| 88 | <string name="export_saves">匯出</string> | 93 | <string name="export_saves">匯出</string> |
| 94 | <string name="install_firmware">安è£éŸŒé«”</string> | ||
| 95 | <string name="install_firmware_description">éŸŒé«”å¿…é ˆç‚º ZIP å°å˜æª”ï¼Œå°‡æœƒç”¨æ–¼éƒ¨åˆ†éŠæˆ²çš„啟動</string> | ||
| 96 | <string name="firmware_installing">æ£åœ¨å®‰è£éŸŒé«”</string> | ||
| 97 | <string name="firmware_installed_success">韌體已æˆåŠŸå®‰è£</string> | ||
| 98 | <string name="firmware_installed_failure">韌體安è£å¤±æ•—</string> | ||
| 99 | <string name="firmware_installed_failure_description">请确ä¿å›ºä»¶ nca 文件ä½äºŽ zip åŽ‹ç¼©åŒ…çš„æ ¹ç›®å½•ï¼Œç„¶åŽé‡è¯•。</string> | ||
| 100 | <string name="share_log">分享åµéŒ¯è¨˜éŒ„</string> | ||
| 101 | <string name="share_log_description">分享 yuzu 的記錄檔以便å°ç›¸é—œå•題進行åµéŒ¯</string> | ||
| 102 | <string name="share_log_missing">找ä¸åˆ°è¨˜éŒ„檔</string> | ||
| 103 | <string name="install_game_content">安è£éŠæˆ²å…§å®¹</string> | ||
| 104 | <string name="install_game_content_description">安è£éŠæˆ²æ›´æ–°æˆ– DLC</string> | ||
| 105 | <string name="installing_game_content">安装ä¸...</string> | ||
| 106 | <string name="install_game_content_failure">å‘ NAND 安装文件时失败</string> | ||
| 107 | <string name="install_game_content_failure_description">请确ä¿é™„åŠ å†…å®¹çš„æœ‰æ•ˆæ€§ï¼Œå¹¶ä¸” prod.keys 密钥文件已安装。</string> | ||
| 108 | <string name="install_game_content_failure_base">为é¿å…产生冲çªï¼Œæ¤åŠŸèƒ½ä¸èƒ½ç”¨äºŽå®‰è£…æ¸¸æˆæœ¬ä½“。</string> | ||
| 109 | <string name="install_game_content_failure_file_extension">åªæœ‰ NSP 或 XCI æ ¼å¼çš„é™„åŠ å†…å®¹å¯ä»¥å®‰è£…ã€‚è¯·ç¡®ä¿æ‚¨çš„æ¸¸æˆé™„åŠ å†…å®¹æ˜¯æœ‰æ•ˆçš„ã€‚</string> | ||
| 110 | <string name="install_game_content_failed_count">%1$d 安装出错</string> | ||
| 111 | <string name="install_game_content_success">游æˆé™„åŠ å†…å®¹å·²æˆåŠŸå®‰è£…</string> | ||
| 112 | <string name="install_game_content_success_install">%1$d 安装æˆåŠŸ</string> | ||
| 113 | <string name="install_game_content_success_overwrite">%1$d 覆盖安装æˆåŠŸ</string> | ||
| 114 | <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> | ||
| 115 | <string name="custom_driver_not_supported">䏿”¯æŒè‡ªå®šä¹‰é©±åЍ</string> | ||
| 116 | <string name="custom_driver_not_supported_description">æ¤è®¾å¤‡ä¸æ”¯æŒè‡ªå®šä¹‰é©±åŠ¨ã€‚\n请之åŽå†è®¿é—®æ¤é¡¹ï¼ŒæŸ¥çœ‹æ˜¯å¦å·²ä¸ºæ¤è®¾å¤‡æ·»åŠ æ”¯æŒã€‚</string> | ||
| 117 | <string name="manage_yuzu_data">ç®¡ç† yuzu æ•°æ®</string> | ||
| 118 | <string name="manage_yuzu_data_description">导入/导出固件ã€å¯†é’¥ã€ç”¨æˆ·æ•°æ®åŠå…¶ä»–。</string> | ||
| 119 | <string name="share_save_file">åˆ†äº«å˜æ¡£æ–‡ä»¶</string> | ||
| 120 | <string name="export_save_failed">å¯¼å‡ºå˜æ¡£æ–‡ä»¶å¤±è´¥</string> | ||
| 89 | 121 | ||
| 90 | <!-- About screen strings --> | 122 | <!-- About screen strings --> |
| 91 | <string name="gaia_is_not_real">Gaia ä¸çœŸå¯¦</string> | 123 | <string name="gaia_is_not_real">Gaia ä¸çœŸå¯¦</string> |
| @@ -94,7 +126,18 @@ | |||
| 94 | <string name="contributors">åƒèˆ‡è€…</string> | 126 | <string name="contributors">åƒèˆ‡è€…</string> |
| 95 | <string name="contributors_description">使用來自 yuzu 團隊的 \u2764 製作</string> | 127 | <string name="contributors_description">使用來自 yuzu 團隊的 \u2764 製作</string> |
| 96 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> | 128 | <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> |
| 129 | <string name="licenses_description">這些專案使 yuzu Android 版æˆç‚ºå¯èƒ½</string> | ||
| 97 | <string name="build">組建</string> | 130 | <string name="build">組建</string> |
| 131 | <string name="user_data">用户数æ®</string> | ||
| 132 | <string name="user_data_description">导入/å¯¼å‡ºåº”ç”¨ç¨‹åºæ‰€æœ‰æ•°æ®ã€‚\n\nå¯¼å…¥ç”¨æˆ·æ•°æ®æ—¶ï¼Œå°†åˆ 除当剿‰€æœ‰çš„用户数æ®ï¼</string> | ||
| 133 | <string name="exporting_user_data">æ£åœ¨å¯¼å‡ºç”¨æˆ·æ•°æ®...</string> | ||
| 134 | <string name="importing_user_data">æ£åœ¨å¯¼å…¥ç”¨æˆ·æ•°æ®...</string> | ||
| 135 | <string name="import_user_data">导入用户数æ®</string> | ||
| 136 | <string name="invalid_yuzu_backup">æ— æ•ˆçš„ yuzu 备份</string> | ||
| 137 | <string name="user_data_export_success">å¯¼å‡ºç”¨æˆ·æ•°æ®æˆåŠŸ</string> | ||
| 138 | <string name="user_data_import_success">å¯¼å…¥ç”¨æˆ·æ•°æ®æˆåŠŸ</string> | ||
| 139 | <string name="user_data_export_cancelled">已喿¶ˆå¯¼å‡ºæ•°æ®</string> | ||
| 140 | <string name="user_data_import_failed_description">请确ä¿ç”¨æˆ·æ•°æ®æ–‡ä»¶å¤¹ä½äºŽ zip åŽ‹ç¼©åŒ…çš„æ ¹ç›®å½•ï¼Œå¹¶åœ¨ config/config.ini 路径ä¸åŒ…å«é…置文件,然åŽé‡è¯•。</string> | ||
| 98 | <string name="support_link">https://discord.gg/u77vRWY</string> | 141 | <string name="support_link">https://discord.gg/u77vRWY</string> |
| 99 | <string name="website_link">https://yuzu-emu.org/</string> | 142 | <string name="website_link">https://yuzu-emu.org/</string> |
| 100 | <string name="github_link">https://github.com/yuzu-emu</string> | 143 | <string name="github_link">https://github.com/yuzu-emu</string> |
| @@ -114,28 +157,29 @@ | |||
| 114 | <string name="are_you_interested">æ‚¨ä»æ„Ÿèˆˆè¶£å—Žï¼Ÿ</string> | 157 | <string name="are_you_interested">æ‚¨ä»æ„Ÿèˆˆè¶£å—Žï¼Ÿ</string> |
| 115 | 158 | ||
| 116 | <!-- General settings strings --> | 159 | <!-- General settings strings --> |
| 117 | <string name="frame_limit_enable">啟用é™åˆ¶é€Ÿåº¦</string> | 160 | <string name="frame_limit_enable">é™åˆ¶é€Ÿåº¦</string> |
| 118 | <string name="frame_limit_enable_description">若啟用,模擬速度將會é™åˆ¶åœ¨æ¨™æº–速度的指定百分比。</string> | 161 | <string name="frame_limit_enable_description">將模擬速度é™åˆ¶åœ¨æ¨™æº–速度的指定百分比。</string> |
| 119 | <string name="frame_limit_slider">é™åˆ¶é€Ÿåº¦ç™¾åˆ†æ¯”</string> | 162 | <string name="frame_limit_slider">é™åˆ¶é€Ÿåº¦ç™¾åˆ†æ¯”</string> |
| 120 | <string name="frame_limit_slider_description">指定é™åˆ¶æ¨¡æ“¬é€Ÿåº¦çš„百分比。é è¨ç‚º 100%,模擬速度將被é™åˆ¶ç‚ºæ¨™æº–é€Ÿåº¦ã€‚æ›´é«˜æˆ–æ›´ä½Žçš„å€¼å°‡æœƒå¢žåŠ æˆ–æ¸›å°‘é€Ÿåº¦é™åˆ¶ã€‚</string> | 163 | <string name="frame_limit_slider_description">指定é™åˆ¶æ¨¡æ“¬é€Ÿåº¦çš„百分比。100% ç‚ºæ¨™æº–é€Ÿåº¦ï¼Œæ›´é«˜æˆ–æ›´ä½Žçš„å€¼å°‡æœƒå¢žåŠ æˆ–æ¸›å°‘é€Ÿåº¦é™åˆ¶ã€‚</string> |
| 121 | <string name="cpu_accuracy">CPU 準確度</string> | 164 | <string name="cpu_accuracy">CPU 準確度</string> |
| 165 | <string name="value_with_units">%1$s%2$s</string> | ||
| 122 | 166 | ||
| 123 | <!-- System settings strings --> | 167 | <!-- System settings strings --> |
| 124 | <string name="use_docked_mode">底座模å¼</string> | 168 | <string name="use_docked_mode">底座模å¼</string> |
| 125 | <string name="use_docked_mode_description">ä»¥åº•åº§æ¨¡å¼æ¨¡æ“¬ï¼Œä»¥çŠ§ç‰²æ•ˆèƒ½çš„ä»£åƒ¹æé«˜è§£æžåº¦ã€‚</string> | 169 | <string name="use_docked_mode_description">æé«˜è§£æžåº¦ï¼Œé™ä½Žæ•ˆèƒ½ã€‚åœç”¨å¾Œå°‡æœƒä½¿ç”¨æ‰‹ææ¨¡å¼ï¼Œæœƒé™ä½Žè§£æžåº¦ä¸¦æé«˜æ•ˆèƒ½ã€‚</string> |
| 126 | <string name="emulated_region">模擬å€åŸŸ</string> | 170 | <string name="emulated_region">模擬å€åŸŸ</string> |
| 127 | <string name="emulated_language">模擬語言</string> | 171 | <string name="emulated_language">模擬語言</string> |
| 128 | <string name="select_rtc_date">é¸å– RTC 日期</string> | 172 | <string name="select_rtc_date">é¸å– RTC 日期</string> |
| 129 | <string name="select_rtc_time">é¸å– RTC 時間</string> | 173 | <string name="select_rtc_time">é¸å– RTC 時間</string> |
| 130 | <string name="use_custom_rtc">啟用自訂 RTC</string> | 174 | <string name="use_custom_rtc">自訂 RTC</string> |
| 131 | <string name="use_custom_rtc_description">æ¤è¨å®šå…許您è¨å®šèˆ‡æ‚¨çš„ç›®å‰ç³»çµ±æ™‚間相互ç¨ç«‹çš„è‡ªè¨‚å³æ™‚時é˜</string> | 175 | <string name="use_custom_rtc_description">å…許您è¨å®šèˆ‡æ‚¨çš„ç›®å‰ç³»çµ±æ™‚間相互ç¨ç«‹çš„è‡ªè¨‚å³æ™‚時é˜ã€‚</string> |
| 132 | <string name="set_custom_rtc">è¨å®šè‡ªè¨‚ RTC</string> | 176 | <string name="set_custom_rtc">è¨å®šè‡ªè¨‚ RTC</string> |
| 133 | 177 | ||
| 134 | <!-- Graphics settings strings --> | 178 | <!-- Graphics settings strings --> |
| 135 | <string name="renderer_api">API</string> | ||
| 136 | <string name="renderer_accuracy">準確度層級</string> | 179 | <string name="renderer_accuracy">準確度層級</string> |
| 137 | <string name="renderer_resolution">è§£æžåº¦</string> | 180 | <string name="renderer_resolution">è§£æžåº¦ (手æ/底座)</string> |
| 138 | <string name="renderer_vsync">VSync 模å¼</string> | 181 | <string name="renderer_vsync">VSync 模å¼</string> |
| 182 | <string name="renderer_screen_layout">å±å¹•æ–¹å‘</string> | ||
| 139 | <string name="renderer_aspect_ratio">長寬比</string> | 183 | <string name="renderer_aspect_ratio">長寬比</string> |
| 140 | <string name="renderer_scaling_filter">è¦–çª—é©æ‡‰éŽæ¿¾å™¨</string> | 184 | <string name="renderer_scaling_filter">è¦–çª—é©æ‡‰éŽæ¿¾å™¨</string> |
| 141 | <string name="renderer_anti_aliasing">消除鋸齒方法</string> | 185 | <string name="renderer_anti_aliasing">消除鋸齒方法</string> |
| @@ -143,12 +187,23 @@ | |||
| 143 | <string name="renderer_force_max_clock_description">強制 GPU 以最大å¯èƒ½æ™‚脈執行 (熱溫é™åˆ¶ä»è¢«å¥—用)。</string> | 187 | <string name="renderer_force_max_clock_description">強制 GPU 以最大å¯èƒ½æ™‚脈執行 (熱溫é™åˆ¶ä»è¢«å¥—用)。</string> |
| 144 | <string name="renderer_asynchronous_shaders">使用éžåŒæ¥è‘—色器</string> | 188 | <string name="renderer_asynchronous_shaders">使用éžåŒæ¥è‘—色器</string> |
| 145 | <string name="renderer_asynchronous_shaders_description">éžåŒæ¥ç·¨è¯è‘—色器,將會減少間斷,但å¯èƒ½æœƒå¼•入故障。</string> | 189 | <string name="renderer_asynchronous_shaders_description">éžåŒæ¥ç·¨è¯è‘—色器,將會減少間斷,但å¯èƒ½æœƒå¼•入故障。</string> |
| 146 | <string name="renderer_debug">啟用圖形åµéŒ¯</string> | 190 | <string name="renderer_reactive_flushing">ä½¿ç”¨é‡æ–°å•Ÿç”¨æŽ’清</string> |
| 147 | <string name="renderer_debug_description">æ ¸å–æ™‚,圖形 API 將會進入慢速åµéŒ¯æ¨¡å¼ã€‚</string> | 191 | <string name="renderer_reactive_flushing_description">çŠ§ç‰²æ•ˆèƒ½ï¼Œä»¥æ”¹å–„éƒ¨åˆ†éŠæˆ²çš„è½‰è¯æº–確度。</string> |
| 148 | <string name="use_disk_shader_cache">使用ç£ç¢Ÿè‘—色器快å–</string> | 192 | <string name="use_disk_shader_cache">ç£ç¢Ÿè‘—色器快å–</string> |
| 149 | <string name="use_disk_shader_cache_description">é€éŽå°‡ç”¢ç”Ÿçš„著色器儲å˜ä¸¦è¼‰å…¥è‡³ç£ç¢Ÿï¼Œæ¸›å°‘䏿–·ã€‚</string> | 193 | <string name="use_disk_shader_cache_description">é€éŽå°‡ç”¢ç”Ÿçš„著色器儲å˜ä¸¦è¼‰å…¥è‡³ç£ç¢Ÿï¼Œæ¸›å°‘䏿–·ã€‚</string> |
| 150 | 194 | ||
| 195 | <!-- Debug settings strings --> | ||
| 196 | <string name="cpu">CPU</string> | ||
| 197 | <string name="cpu_debug_mode">CPU 调试</string> | ||
| 198 | <string name="cpu_debug_mode_description">å°† CPU 设置为较慢的调试模å¼ã€‚</string> | ||
| 199 | <string name="gpu">GPU</string> | ||
| 200 | <string name="renderer_api">API</string> | ||
| 201 | <string name="renderer_debug">圖形åµéŒ¯</string> | ||
| 202 | <string name="renderer_debug_description">將圖形 API è¨ç‚ºæ…¢é€ŸåµéŒ¯æ¨¡å¼ã€‚</string> | ||
| 203 | <string name="fastmem">Fastmem</string> | ||
| 204 | |||
| 151 | <!-- Audio settings strings --> | 205 | <!-- Audio settings strings --> |
| 206 | <string name="audio_output_engine">输出引擎</string> | ||
| 152 | <string name="audio_volume">音é‡</string> | 207 | <string name="audio_volume">音é‡</string> |
| 153 | <string name="audio_volume_description">指定音訊輸出音é‡ã€‚</string> | 208 | <string name="audio_volume_description">指定音訊輸出音é‡ã€‚</string> |
| 154 | 209 | ||
| @@ -157,7 +212,9 @@ | |||
| 157 | <string name="ini_saved">已儲å˜è¨å®š</string> | 212 | <string name="ini_saved">已儲å˜è¨å®š</string> |
| 158 | <string name="gameid_saved">å·²å„²å˜ %1$s è¨å®š</string> | 213 | <string name="gameid_saved">å·²å„²å˜ %1$s è¨å®š</string> |
| 159 | <string name="error_saving">å„²å˜ %1$s 時發生錯誤 ini: %2$s</string> | 214 | <string name="error_saving">å„²å˜ %1$s 時發生錯誤 ini: %2$s</string> |
| 215 | <string name="unimplemented_menu">未生效èœå•</string> | ||
| 160 | <string name="loading">æ£åœ¨è¼‰å…¥â€¦</string> | 216 | <string name="loading">æ£åœ¨è¼‰å…¥â€¦</string> |
| 217 | <string name="shutting_down">æ£åœ¨å…³é—…</string> | ||
| 161 | <string name="reset_setting_confirmation">è¦å°‡æ¤è¨å®šé‡è¨å›žé è¨å€¼å—Žï¼Ÿ</string> | 218 | <string name="reset_setting_confirmation">è¦å°‡æ¤è¨å®šé‡è¨å›žé è¨å€¼å—Žï¼Ÿ</string> |
| 162 | <string name="reset_to_default">é‡è¨ç‚ºé è¨å€¼</string> | 219 | <string name="reset_to_default">é‡è¨ç‚ºé è¨å€¼</string> |
| 163 | <string name="reset_all_settings">é‡è¨æ‰€æœ‰è¨å®šï¼Ÿ</string> | 220 | <string name="reset_all_settings">é‡è¨æ‰€æœ‰è¨å®šï¼Ÿ</string> |
| @@ -165,6 +222,14 @@ | |||
| 165 | <string name="settings_reset">è¨å®šå·²é‡è¨</string> | 222 | <string name="settings_reset">è¨å®šå·²é‡è¨</string> |
| 166 | <string name="close">關閉</string> | 223 | <string name="close">關閉</string> |
| 167 | <string name="learn_more">深入çžè§£</string> | 224 | <string name="learn_more">深入çžè§£</string> |
| 225 | <string name="auto">自動</string> | ||
| 226 | <string name="submit">æäº¤</string> | ||
| 227 | <string name="string_null">ç„¡</string> | ||
| 228 | <string name="string_import">匯入</string> | ||
| 229 | <string name="export">匯出</string> | ||
| 230 | <string name="export_failed">导出失败</string> | ||
| 231 | <string name="import_failed">导入失败</string> | ||
| 232 | <string name="cancelling">å–æ¶ˆä¸</string> | ||
| 168 | 233 | ||
| 169 | <!-- GPU driver installation --> | 234 | <!-- GPU driver installation --> |
| 170 | <string name="select_gpu_driver">é¸å– GPU 驅動程å¼</string> | 235 | <string name="select_gpu_driver">é¸å– GPU 驅動程å¼</string> |
| @@ -172,6 +237,7 @@ | |||
| 172 | <string name="select_gpu_driver_install">安è£</string> | 237 | <string name="select_gpu_driver_install">安è£</string> |
| 173 | <string name="select_gpu_driver_default">é è¨</string> | 238 | <string name="select_gpu_driver_default">é è¨</string> |
| 174 | <string name="select_gpu_driver_use_default">使用é è¨ GPU 驅動程å¼</string> | 239 | <string name="select_gpu_driver_use_default">使用é è¨ GPU 驅動程å¼</string> |
| 240 | <string name="select_gpu_driver_error">é¸å–的驅動程å¼ç„¡æ•ˆï¼Œå°‡ä½¿ç”¨ç³»çµ±é è¨é©…動程å¼ï¼</string> | ||
| 175 | <string name="system_gpu_driver">系統 GPU 驅動程å¼</string> | 241 | <string name="system_gpu_driver">系統 GPU 驅動程å¼</string> |
| 176 | <string name="installing_driver">æ£åœ¨å®‰è£é©…動程å¼â€¦</string> | 242 | <string name="installing_driver">æ£åœ¨å®‰è£é©…動程å¼â€¦</string> |
| 177 | 243 | ||
| @@ -182,10 +248,11 @@ | |||
| 182 | <string name="preferences_graphics">圖形</string> | 248 | <string name="preferences_graphics">圖形</string> |
| 183 | <string name="preferences_audio">音訊</string> | 249 | <string name="preferences_audio">音訊</string> |
| 184 | <string name="preferences_theme">主題和色彩</string> | 250 | <string name="preferences_theme">主題和色彩</string> |
| 251 | <string name="preferences_debug">åµéŒ¯</string> | ||
| 185 | 252 | ||
| 186 | <!-- ROM loading errors --> | 253 | <!-- ROM loading errors --> |
| 187 | <string name="loader_error_encrypted">您的 ROM å·²åŠ å¯†</string> | 254 | <string name="loader_error_encrypted">您的 ROM å·²åŠ å¯†</string> |
| 188 | <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> | 255 | <string name="loader_error_encrypted_roms_description"><![CDATA[请按照指å—釿–°è½¬å‚¨æ‚¨çš„<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards\">游æˆå¡å¸¦</a>或<a href=\"https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop\">已安装的游æˆ</a>。]]></string> |
| 189 | <string name="loader_error_encrypted_keys_description"><![CDATA[è«‹ç¢ºä¿æ‚¨çš„ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 檔案已安è£ï¼Œè®“éŠæˆ²å¯ä»¥è§£å¯†ã€‚]]></string> | 256 | <string name="loader_error_encrypted_keys_description"><![CDATA[è«‹ç¢ºä¿æ‚¨çš„ <a href=\"https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a> 檔案已安è£ï¼Œè®“éŠæˆ²å¯ä»¥è§£å¯†ã€‚]]></string> |
| 190 | <string name="loader_error_video_core">åˆå§‹åŒ–è¦–è¨Šæ ¸å¿ƒæ™‚ç™¼ç”ŸéŒ¯èª¤</string> | 257 | <string name="loader_error_video_core">åˆå§‹åŒ–è¦–è¨Šæ ¸å¿ƒæ™‚ç™¼ç”ŸéŒ¯èª¤</string> |
| 191 | <string name="loader_error_video_core_description">這經常由ä¸ç›¸å®¹çš„ GPU 驅動程å¼é€ æˆï¼Œå®‰è£è‡ªè¨‚ GPU 驅動程å¼å¯èƒ½æœƒè§£æ±ºæ¤å•題。</string> | 258 | <string name="loader_error_video_core_description">這經常由ä¸ç›¸å®¹çš„ GPU 驅動程å¼é€ æˆï¼Œå®‰è£è‡ªè¨‚ GPU 驅動程å¼å¯èƒ½æœƒè§£æ±ºæ¤å•題。</string> |
| @@ -219,13 +286,16 @@ | |||
| 219 | <!-- Errors and warnings --> | 286 | <!-- Errors and warnings --> |
| 220 | <string name="abort_button">䏿¢</string> | 287 | <string name="abort_button">䏿¢</string> |
| 221 | <string name="continue_button">繼續</string> | 288 | <string name="continue_button">繼續</string> |
| 222 | <string name="system_archive_not_found">找ä¸åˆ°ç³»çµ±æª”案</string> | 289 | <string name="system_archive_not_found">找ä¸åˆ°ç³»çµ±å°å˜</string> |
| 223 | <string name="system_archive_not_found_message">%s éºå¤±ï¼Œè«‹å‚¾å°æ‚¨çš„系統å°å˜ã€‚\n繼續模擬å¯èƒ½æœƒé€ æˆç•¶æ©Ÿå’ŒéŒ¯èª¤ã€‚</string> | 290 | <string name="system_archive_not_found_message">%s éºå¤±ï¼Œè«‹å‚¾å°æ‚¨çš„系統å°å˜ã€‚\n繼續模擬å¯èƒ½æœƒé€ æˆç•¶æ©Ÿå’ŒéŒ¯èª¤ã€‚</string> |
| 224 | <string name="system_archive_general">系統å°å˜</string> | 291 | <string name="system_archive_general">系統å°å˜</string> |
| 225 | <string name="save_load_error">儲å˜/載入發生錯誤</string> | 292 | <string name="save_load_error">儲å˜/載入發生錯誤</string> |
| 226 | <string name="fatal_error">åš´é‡éŒ¯èª¤</string> | 293 | <string name="fatal_error">åš´é‡éŒ¯èª¤</string> |
| 227 | <string name="fatal_error_message">發生嚴é‡éŒ¯èª¤ï¼Œæª¢æŸ¥è¨˜éŒ„以å–得詳細資訊。\n繼續模擬å¯èƒ½æœƒé€ æˆç•¶æ©Ÿå’ŒéŒ¯èª¤ã€‚</string> | 294 | <string name="fatal_error_message">發生嚴é‡éŒ¯èª¤ï¼Œæª¢æŸ¥è¨˜éŒ„以å–得詳細資訊。\n繼續模擬å¯èƒ½æœƒé€ æˆç•¶æ©Ÿå’ŒéŒ¯èª¤ã€‚</string> |
| 228 | <string name="performance_warning">關閉æ¤è¨å®šæœƒé¡¯è‘—é™ä½Žæ¨¡æ“¬æ•ˆèƒ½ï¼å¦‚éœ€æœ€ä½³é«”é©—ï¼Œå»ºè°æ‚¨å°‡æ¤è¨å®šä¿æŒç‚ºå•Ÿç”¨ç‹€æ…‹ã€‚</string> | 295 | <string name="performance_warning">關閉æ¤è¨å®šæœƒé¡¯è‘—é™ä½Žæ¨¡æ“¬æ•ˆèƒ½ï¼å¦‚éœ€æœ€ä½³é«”é©—ï¼Œå»ºè°æ‚¨å°‡æ¤è¨å®šä¿æŒç‚ºå•Ÿç”¨ç‹€æ…‹ã€‚</string> |
| 296 | <string name="device_memory_inadequate">设备 RAM: %1$s\n推è RAM: %2$s</string> | ||
| 297 | <string name="memory_formatted">%1$s%2$s</string> | ||
| 298 | <string name="no_game_present">当剿²¡æœ‰å¯å¯åŠ¨çš„æ¸¸æˆï¼</string> | ||
| 229 | 299 | ||
| 230 | <!-- Region Names --> | 300 | <!-- Region Names --> |
| 231 | <string name="region_japan">日本</string> | 301 | <string name="region_japan">日本</string> |
| @@ -236,7 +306,14 @@ | |||
| 236 | <string name="region_korea">å—韓</string> | 306 | <string name="region_korea">å—韓</string> |
| 237 | <string name="region_taiwan">å°ç£</string> | 307 | <string name="region_taiwan">å°ç£</string> |
| 238 | 308 | ||
| 239 | <!-- Language Names --> | 309 | <!-- Memory Sizes --> |
| 310 | <string name="memory_byte">Byte</string> | ||
| 311 | <string name="memory_kilobyte">KB</string> | ||
| 312 | <string name="memory_megabyte">MB</string> | ||
| 313 | <string name="memory_gigabyte">英國</string> | ||
| 314 | <string name="memory_terabyte">TB</string> | ||
| 315 | <string name="memory_petabyte">PB</string> | ||
| 316 | <string name="memory_exabyte">EB</string> | ||
| 240 | 317 | ||
| 241 | <!-- Renderer APIs --> | 318 | <!-- Renderer APIs --> |
| 242 | <string name="renderer_vulkan">Vulkan</string> | 319 | <string name="renderer_vulkan">Vulkan</string> |
| @@ -274,14 +351,20 @@ | |||
| 274 | <string name="anti_aliasing_fxaa">FXAA</string> | 351 | <string name="anti_aliasing_fxaa">FXAA</string> |
| 275 | <string name="anti_aliasing_smaa">SMAA</string> | 352 | <string name="anti_aliasing_smaa">SMAA</string> |
| 276 | 353 | ||
| 354 | <!-- Screen Layouts --> | ||
| 355 | <string name="screen_layout_landscape">横å‘大å±</string> | ||
| 356 | <string name="screen_layout_portrait">纵å‘å±å¹•</string> | ||
| 357 | <string name="screen_layout_auto">自動</string> | ||
| 358 | |||
| 277 | <!-- Aspect Ratios --> | 359 | <!-- Aspect Ratios --> |
| 278 | <string name="ratio_default">é è¨ (16:9)</string> | 360 | <string name="ratio_default">é è¨ (16:9)</string> |
| 279 | <string name="ratio_force_four_three">強制 4:3</string> | 361 | <string name="ratio_force_four_three">強制 4:3</string> |
| 280 | <string name="ratio_force_twenty_one_nine">強制 21:9</string> | 362 | <string name="ratio_force_twenty_one_nine">強制 21:9</string> |
| 281 | <string name="ratio_force_sixteen_ten">強制 16:10</string> | 363 | <string name="ratio_force_sixteen_ten">強制 16:10</string> |
| 282 | <string name="ratio_stretch">延伸視窗</string> | 364 | <string name="ratio_stretch">延展視窗</string> |
| 283 | 365 | ||
| 284 | <!-- CPU Accuracy --> | 366 | <!-- CPU Accuracy --> |
| 367 | <string name="cpu_accuracy_accurate">高精度</string> | ||
| 285 | <string name="cpu_accuracy_unsafe">低精度</string> | 368 | <string name="cpu_accuracy_unsafe">低精度</string> |
| 286 | <string name="cpu_accuracy_paranoid">ä¸åˆç† (æ…¢)</string> | 369 | <string name="cpu_accuracy_paranoid">ä¸åˆç† (æ…¢)</string> |
| 287 | 370 | ||
| @@ -307,8 +390,22 @@ | |||
| 307 | <string name="theme_mode_light">淺色</string> | 390 | <string name="theme_mode_light">淺色</string> |
| 308 | <string name="theme_mode_dark">深色</string> | 391 | <string name="theme_mode_dark">深色</string> |
| 309 | 392 | ||
| 393 | <!-- Audio output engines --> | ||
| 394 | <string name="cubeb">cubeb</string> | ||
| 395 | |||
| 310 | <!-- Black backgrounds theme --> | 396 | <!-- Black backgrounds theme --> |
| 311 | <string name="use_black_backgrounds">使用黑色背景</string> | 397 | <string name="use_black_backgrounds">黑色背景</string> |
| 312 | <string name="use_black_backgrounds_description">使用深色主題時,套用黑色背景。</string> | 398 | <string name="use_black_backgrounds_description">使用深色主題時,套用黑色背景。</string> |
| 313 | 399 | ||
| 314 | </resources> | 400 | <!-- Picture-In-Picture --> |
| 401 | <string name="picture_in_picture">ç”»ä¸ç”»</string> | ||
| 402 | <string name="picture_in_picture_description">模拟器ä½äºŽåŽå°æ—¶æœ€å°åŒ–窗å£</string> | ||
| 403 | <string name="pause">æš‚åœ</string> | ||
| 404 | <string name="play">开始</string> | ||
| 405 | <string name="mute">éœéŸ³</string> | ||
| 406 | <string name="unmute">å–æ¶ˆéœéŸ³</string> | ||
| 407 | |||
| 408 | <!-- Licenses screen strings --> | ||
| 409 | <string name="licenses">授權</string> | ||
| 410 | <string name="license_fidelityfx_fsr_description">來自 AMD çš„å‡ç´šåœ–åƒå“質</string> | ||
| 411 | </resources> | ||
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index dc10159c9..51bcc49a3 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | <resources> | 2 | <resources> |
| 3 | 3 | ||
| 4 | <string-array name="regionNames"> | 4 | <string-array name="regionNames"> |
| 5 | <item>@string/auto</item> | ||
| 6 | <item>@string/region_australia</item> | 5 | <item>@string/region_australia</item> |
| 7 | <item>@string/region_china</item> | 6 | <item>@string/region_china</item> |
| 8 | <item>@string/region_europe</item> | 7 | <item>@string/region_europe</item> |
| @@ -13,7 +12,6 @@ | |||
| 13 | </string-array> | 12 | </string-array> |
| 14 | 13 | ||
| 15 | <integer-array name="regionValues"> | 14 | <integer-array name="regionValues"> |
| 16 | <item>-1</item> | ||
| 17 | <item>3</item> | 15 | <item>3</item> |
| 18 | <item>4</item> | 16 | <item>4</item> |
| 19 | <item>2</item> | 17 | <item>2</item> |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index b92978140..471af8795 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -72,7 +72,7 @@ | |||
| 72 | <string name="invalid_keys_error">Invalid encryption keys</string> | 72 | <string name="invalid_keys_error">Invalid encryption keys</string> |
| 73 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 73 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 74 | <string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string> | 74 | <string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string> |
| 75 | <string name="gpu_driver_manager">GPU Driver Manager</string> | 75 | <string name="gpu_driver_manager">GPU driver manager</string> |
| 76 | <string name="install_gpu_driver">Install GPU driver</string> | 76 | <string name="install_gpu_driver">Install GPU driver</string> |
| 77 | <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> | 77 | <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> |
| 78 | <string name="advanced_settings">Advanced settings</string> | 78 | <string name="advanced_settings">Advanced settings</string> |
| @@ -91,6 +91,7 @@ | |||
| 91 | <string name="manage_save_data">Manage save data</string> | 91 | <string name="manage_save_data">Manage save data</string> |
| 92 | <string name="manage_save_data_description">Save data found. Please select an option below.</string> | 92 | <string name="manage_save_data_description">Save data found. Please select an option below.</string> |
| 93 | <string name="import_export_saves_description">Import or export save files</string> | 93 | <string name="import_export_saves_description">Import or export save files</string> |
| 94 | <string name="save_files_exporting">Exporting save files…</string> | ||
| 94 | <string name="save_file_imported_success">Imported successfully</string> | 95 | <string name="save_file_imported_success">Imported successfully</string> |
| 95 | <string name="save_file_invalid_zip_structure">Invalid save directory structure</string> | 96 | <string name="save_file_invalid_zip_structure">Invalid save directory structure</string> |
| 96 | <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string> | 97 | <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string> |
| @@ -240,6 +241,7 @@ | |||
| 240 | <string name="shutting_down">Shutting down…</string> | 241 | <string name="shutting_down">Shutting down…</string> |
| 241 | <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> | 242 | <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> |
| 242 | <string name="reset_to_default">Reset to default</string> | 243 | <string name="reset_to_default">Reset to default</string> |
| 244 | <string name="reset_to_default_description">Resets all advanced settings</string> | ||
| 243 | <string name="reset_all_settings">Reset all settings?</string> | 245 | <string name="reset_all_settings">Reset all settings?</string> |
| 244 | <string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string> | 246 | <string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string> |
| 245 | <string name="settings_reset">Settings reset</string> | 247 | <string name="settings_reset">Settings reset</string> |
| @@ -255,6 +257,7 @@ | |||
| 255 | <string name="cancelling">Cancelling</string> | 257 | <string name="cancelling">Cancelling</string> |
| 256 | <string name="install">Install</string> | 258 | <string name="install">Install</string> |
| 257 | <string name="delete">Delete</string> | 259 | <string name="delete">Delete</string> |
| 260 | <string name="export_success">Exported successfully</string> | ||
| 258 | 261 | ||
| 259 | <!-- GPU driver installation --> | 262 | <!-- GPU driver installation --> |
| 260 | <string name="select_gpu_driver">Select GPU driver</string> | 263 | <string name="select_gpu_driver">Select GPU driver</string> |
| @@ -271,10 +274,14 @@ | |||
| 271 | <string name="preferences_settings">Settings</string> | 274 | <string name="preferences_settings">Settings</string> |
| 272 | <string name="preferences_general">General</string> | 275 | <string name="preferences_general">General</string> |
| 273 | <string name="preferences_system">System</string> | 276 | <string name="preferences_system">System</string> |
| 277 | <string name="preferences_system_description">Docked mode, region, language</string> | ||
| 274 | <string name="preferences_graphics">Graphics</string> | 278 | <string name="preferences_graphics">Graphics</string> |
| 279 | <string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string> | ||
| 275 | <string name="preferences_audio">Audio</string> | 280 | <string name="preferences_audio">Audio</string> |
| 281 | <string name="preferences_audio_description">Output engine, volume</string> | ||
| 276 | <string name="preferences_theme">Theme and color</string> | 282 | <string name="preferences_theme">Theme and color</string> |
| 277 | <string name="preferences_debug">Debug</string> | 283 | <string name="preferences_debug">Debug</string> |
| 284 | <string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string> | ||
| 278 | 285 | ||
| 279 | <!-- ROM loading errors --> | 286 | <!-- ROM loading errors --> |
| 280 | <string name="loader_error_encrypted">Your ROM is encrypted</string> | 287 | <string name="loader_error_encrypted">Your ROM is encrypted</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 deleted file mode 100644 index 51b88d9dc..000000000 --- a/src/android/app/src/main/res/xml/locales_config.xml +++ /dev/null | |||
| @@ -1,17 +0,0 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <locale-config xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 3 | <locale android:name="en" /> <!-- English (default) --> | ||
| 4 | <locale android:name="de" /> <!-- German --> | ||
| 5 | <locale android:name="es" /> <!-- Spanish --> | ||
| 6 | <locale android:name="fr" /> <!-- French --> | ||
| 7 | <locale android:name="it" /> <!-- Italian --> | ||
| 8 | <locale android:name="ja" /> <!-- Japanese --> | ||
| 9 | <locale android:name="nb" /> <!-- Norwegian Bokmal --> | ||
| 10 | <locale android:name="pl" /> <!-- Polish --> | ||
| 11 | <locale android:name="pt-rBR" /> <!-- Portuguese (Brazil) --> | ||
| 12 | <locale android:name="pt-RPT" /> <!-- Portuguese (Portugal) --> | ||
| 13 | <locale android:name="ru" /> <!-- Russian --> | ||
| 14 | <locale android:name="uk" /> <!-- Ukranian --> | ||
| 15 | <locale android:name="zh-rCN" /> <!-- Chinese (China) --> | ||
| 16 | <locale android:name="zh-rTW" /> <!-- Chinese (Taiwan) --> | ||
| 17 | </locale-config> | ||
diff --git a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp index 2c16d3769..e2b9eb566 100644 --- a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp +++ b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp | |||
| @@ -1,107 +1,107 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/adsp/apps/opus/opus_decode_object.h" | 4 | #include "audio_core/adsp/apps/opus/opus_decode_object.h" |
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | 6 | ||
| 7 | namespace AudioCore::ADSP::OpusDecoder { | 7 | namespace AudioCore::ADSP::OpusDecoder { |
| 8 | namespace { | 8 | namespace { |
| 9 | bool IsValidChannelCount(u32 channel_count) { | 9 | bool IsValidChannelCount(u32 channel_count) { |
| 10 | return channel_count == 1 || channel_count == 2; | 10 | return channel_count == 1 || channel_count == 2; |
| 11 | } | 11 | } |
| 12 | } // namespace | 12 | } // namespace |
| 13 | 13 | ||
| 14 | u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) { | 14 | u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) { |
| 15 | if (!IsValidChannelCount(channel_count)) { | 15 | if (!IsValidChannelCount(channel_count)) { |
| 16 | return 0; | 16 | return 0; |
| 17 | } | 17 | } |
| 18 | return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count); | 18 | return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count); |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { | 21 | OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { |
| 22 | auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer); | 22 | auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer); |
| 23 | auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2); | 23 | auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2); |
| 24 | 24 | ||
| 25 | if (new_decoder->magic == DecodeObjectMagic) { | 25 | if (new_decoder->magic == DecodeObjectMagic) { |
| 26 | if (!new_decoder->initialized || | 26 | if (!new_decoder->initialized || |
| 27 | (new_decoder->initialized && new_decoder->self == comparison)) { | 27 | (new_decoder->initialized && new_decoder->self == comparison)) { |
| 28 | new_decoder->state_valid = true; | 28 | new_decoder->state_valid = true; |
| 29 | } | 29 | } |
| 30 | } else { | 30 | } else { |
| 31 | new_decoder->initialized = false; | 31 | new_decoder->initialized = false; |
| 32 | new_decoder->state_valid = true; | 32 | new_decoder->state_valid = true; |
| 33 | } | 33 | } |
| 34 | return *new_decoder; | 34 | return *new_decoder; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) { | 37 | s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) { |
| 38 | if (!state_valid) { | 38 | if (!state_valid) { |
| 39 | return OPUS_INVALID_STATE; | 39 | return OPUS_INVALID_STATE; |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | if (initialized) { | 42 | if (initialized) { |
| 43 | return OPUS_OK; | 43 | return OPUS_OK; |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include | 46 | // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include |
| 47 | // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer | 47 | // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer |
| 48 | // provided. | 48 | // provided. |
| 49 | // We could use _create and have libopus allocate it for us, but then we have to separately | 49 | // We could use _create and have libopus allocate it for us, but then we have to separately |
| 50 | // track which decoder is being used between this and multistream in order to call the correct | 50 | // track which decoder is being used between this and multistream in order to call the correct |
| 51 | // destroy from the host side. | 51 | // destroy from the host side. |
| 52 | // This is a bit cringe, but is safe as these objects are only ever initialized inside the given | 52 | // This is a bit cringe, but is safe as these objects are only ever initialized inside the given |
| 53 | // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow. | 53 | // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow. |
| 54 | decoder = (LibOpusDecoder*)(this + 1); | 54 | decoder = (LibOpusDecoder*)(this + 1); |
| 55 | s32 ret = opus_decoder_init(decoder, sample_rate, channel_count); | 55 | s32 ret = opus_decoder_init(decoder, sample_rate, channel_count); |
| 56 | if (ret == OPUS_OK) { | 56 | if (ret == OPUS_OK) { |
| 57 | magic = DecodeObjectMagic; | 57 | magic = DecodeObjectMagic; |
| 58 | initialized = true; | 58 | initialized = true; |
| 59 | state_valid = true; | 59 | state_valid = true; |
| 60 | self = this; | 60 | self = this; |
| 61 | final_range = 0; | 61 | final_range = 0; |
| 62 | } | 62 | } |
| 63 | return ret; | 63 | return ret; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | s32 OpusDecodeObject::Shutdown() { | 66 | s32 OpusDecodeObject::Shutdown() { |
| 67 | if (!state_valid) { | 67 | if (!state_valid) { |
| 68 | return OPUS_INVALID_STATE; | 68 | return OPUS_INVALID_STATE; |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | if (initialized) { | 71 | if (initialized) { |
| 72 | magic = 0x0; | 72 | magic = 0x0; |
| 73 | initialized = false; | 73 | initialized = false; |
| 74 | state_valid = false; | 74 | state_valid = false; |
| 75 | self = nullptr; | 75 | self = nullptr; |
| 76 | final_range = 0; | 76 | final_range = 0; |
| 77 | decoder = nullptr; | 77 | decoder = nullptr; |
| 78 | } | 78 | } |
| 79 | return OPUS_OK; | 79 | return OPUS_OK; |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | s32 OpusDecodeObject::ResetDecoder() { | 82 | s32 OpusDecodeObject::ResetDecoder() { |
| 83 | return opus_decoder_ctl(decoder, OPUS_RESET_STATE); | 83 | return opus_decoder_ctl(decoder, OPUS_RESET_STATE); |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, | 86 | s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, |
| 87 | u64 input_data, u64 input_data_size) { | 87 | u64 input_data, u64 input_data_size) { |
| 88 | ASSERT(initialized); | 88 | ASSERT(initialized); |
| 89 | out_sample_count = 0; | 89 | out_sample_count = 0; |
| 90 | 90 | ||
| 91 | if (!state_valid) { | 91 | if (!state_valid) { |
| 92 | return OPUS_INVALID_STATE; | 92 | return OPUS_INVALID_STATE; |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | auto ret_code_or_samples = opus_decode( | 95 | auto ret_code_or_samples = opus_decode( |
| 96 | decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), | 96 | decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), |
| 97 | reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); | 97 | reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); |
| 98 | 98 | ||
| 99 | if (ret_code_or_samples < OPUS_OK) { | 99 | if (ret_code_or_samples < OPUS_OK) { |
| 100 | return ret_code_or_samples; | 100 | return ret_code_or_samples; |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | out_sample_count = ret_code_or_samples; | 103 | out_sample_count = ret_code_or_samples; |
| 104 | return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); | 104 | return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | } // namespace AudioCore::ADSP::OpusDecoder | 107 | } // namespace AudioCore::ADSP::OpusDecoder |
diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.cpp b/src/audio_core/adsp/apps/opus/opus_decoder.cpp index 2084de128..75f0fb9ad 100644 --- a/src/audio_core/adsp/apps/opus/opus_decoder.cpp +++ b/src/audio_core/adsp/apps/opus/opus_decoder.cpp | |||
| @@ -30,9 +30,9 @@ bool IsValidMultiStreamChannelCount(u32 channel_count) { | |||
| 30 | return channel_count <= OpusStreamCountMax; | 30 | return channel_count <= OpusStreamCountMax; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 sterero_stream_count) { | 33 | bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_count) { |
| 34 | return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 && | 34 | return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 && |
| 35 | sterero_stream_count > 0 && sterero_stream_count <= total_stream_count; | 35 | stereo_stream_count >= 0 && stereo_stream_count <= total_stream_count; |
| 36 | } | 36 | } |
| 37 | } // namespace | 37 | } // namespace |
| 38 | 38 | ||
diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp index f6d362e68..05cf3975d 100644 --- a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp +++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp | |||
| @@ -1,111 +1,111 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h" | 4 | #include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h" |
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | 6 | ||
| 7 | namespace AudioCore::ADSP::OpusDecoder { | 7 | namespace AudioCore::ADSP::OpusDecoder { |
| 8 | 8 | ||
| 9 | namespace { | 9 | namespace { |
| 10 | bool IsValidChannelCount(u32 channel_count) { | 10 | bool IsValidChannelCount(u32 channel_count) { |
| 11 | return channel_count == 1 || channel_count == 2; | 11 | return channel_count == 1 || channel_count == 2; |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { | 14 | bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { |
| 15 | return total_stream_count > 0 && stereo_stream_count > 0 && | 15 | return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 && |
| 16 | stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); | 16 | stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); |
| 17 | } | 17 | } |
| 18 | } // namespace | 18 | } // namespace |
| 19 | 19 | ||
| 20 | u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count, | 20 | u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count, |
| 21 | u32 stereo_stream_count) { | 21 | u32 stereo_stream_count) { |
| 22 | if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) { | 22 | if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) { |
| 23 | return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) + | 23 | return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) + |
| 24 | opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count); | 24 | opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count); |
| 25 | } | 25 | } |
| 26 | return 0; | 26 | return 0; |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) { | 29 | OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) { |
| 30 | auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer); | 30 | auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer); |
| 31 | auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2); | 31 | auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2); |
| 32 | 32 | ||
| 33 | if (new_decoder->magic == DecodeMultiStreamObjectMagic) { | 33 | if (new_decoder->magic == DecodeMultiStreamObjectMagic) { |
| 34 | if (!new_decoder->initialized || | 34 | if (!new_decoder->initialized || |
| 35 | (new_decoder->initialized && new_decoder->self == comparison)) { | 35 | (new_decoder->initialized && new_decoder->self == comparison)) { |
| 36 | new_decoder->state_valid = true; | 36 | new_decoder->state_valid = true; |
| 37 | } | 37 | } |
| 38 | } else { | 38 | } else { |
| 39 | new_decoder->initialized = false; | 39 | new_decoder->initialized = false; |
| 40 | new_decoder->state_valid = true; | 40 | new_decoder->state_valid = true; |
| 41 | } | 41 | } |
| 42 | return *new_decoder; | 42 | return *new_decoder; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count, | 45 | s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count, |
| 46 | u32 channel_count, u32 stereo_stream_count, | 46 | u32 channel_count, u32 stereo_stream_count, |
| 47 | u8* mappings) { | 47 | u8* mappings) { |
| 48 | if (!state_valid) { | 48 | if (!state_valid) { |
| 49 | return OPUS_INVALID_STATE; | 49 | return OPUS_INVALID_STATE; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | if (initialized) { | 52 | if (initialized) { |
| 53 | return OPUS_OK; | 53 | return OPUS_OK; |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | // See OpusDecodeObject::InitializeDecoder for an explanation of this | 56 | // See OpusDecodeObject::InitializeDecoder for an explanation of this |
| 57 | decoder = (LibOpusMSDecoder*)(this + 1); | 57 | decoder = (LibOpusMSDecoder*)(this + 1); |
| 58 | s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count, | 58 | s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count, |
| 59 | stereo_stream_count, mappings); | 59 | stereo_stream_count, mappings); |
| 60 | if (ret == OPUS_OK) { | 60 | if (ret == OPUS_OK) { |
| 61 | magic = DecodeMultiStreamObjectMagic; | 61 | magic = DecodeMultiStreamObjectMagic; |
| 62 | initialized = true; | 62 | initialized = true; |
| 63 | state_valid = true; | 63 | state_valid = true; |
| 64 | self = this; | 64 | self = this; |
| 65 | final_range = 0; | 65 | final_range = 0; |
| 66 | } | 66 | } |
| 67 | return ret; | 67 | return ret; |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | s32 OpusMultiStreamDecodeObject::Shutdown() { | 70 | s32 OpusMultiStreamDecodeObject::Shutdown() { |
| 71 | if (!state_valid) { | 71 | if (!state_valid) { |
| 72 | return OPUS_INVALID_STATE; | 72 | return OPUS_INVALID_STATE; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | if (initialized) { | 75 | if (initialized) { |
| 76 | magic = 0x0; | 76 | magic = 0x0; |
| 77 | initialized = false; | 77 | initialized = false; |
| 78 | state_valid = false; | 78 | state_valid = false; |
| 79 | self = nullptr; | 79 | self = nullptr; |
| 80 | final_range = 0; | 80 | final_range = 0; |
| 81 | decoder = nullptr; | 81 | decoder = nullptr; |
| 82 | } | 82 | } |
| 83 | return OPUS_OK; | 83 | return OPUS_OK; |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | s32 OpusMultiStreamDecodeObject::ResetDecoder() { | 86 | s32 OpusMultiStreamDecodeObject::ResetDecoder() { |
| 87 | return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE); | 87 | return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE); |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data, | 90 | s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data, |
| 91 | u64 output_data_size, u64 input_data, u64 input_data_size) { | 91 | u64 output_data_size, u64 input_data, u64 input_data_size) { |
| 92 | ASSERT(initialized); | 92 | ASSERT(initialized); |
| 93 | out_sample_count = 0; | 93 | out_sample_count = 0; |
| 94 | 94 | ||
| 95 | if (!state_valid) { | 95 | if (!state_valid) { |
| 96 | return OPUS_INVALID_STATE; | 96 | return OPUS_INVALID_STATE; |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | auto ret_code_or_samples = opus_multistream_decode( | 99 | auto ret_code_or_samples = opus_multistream_decode( |
| 100 | decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), | 100 | decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), |
| 101 | reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); | 101 | reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); |
| 102 | 102 | ||
| 103 | if (ret_code_or_samples < OPUS_OK) { | 103 | if (ret_code_or_samples < OPUS_OK) { |
| 104 | return ret_code_or_samples; | 104 | return ret_code_or_samples; |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | out_sample_count = ret_code_or_samples; | 107 | out_sample_count = ret_code_or_samples; |
| 108 | return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); | 108 | return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | } // namespace AudioCore::ADSP::OpusDecoder | 111 | } // namespace AudioCore::ADSP::OpusDecoder |
diff --git a/src/audio_core/opus/decoder.cpp b/src/audio_core/opus/decoder.cpp index 5b23fce14..b7fed5304 100644 --- a/src/audio_core/opus/decoder.cpp +++ b/src/audio_core/opus/decoder.cpp | |||
| @@ -1,179 +1,179 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/opus/decoder.h" | 4 | #include "audio_core/opus/decoder.h" |
| 5 | #include "audio_core/opus/hardware_opus.h" | 5 | #include "audio_core/opus/hardware_opus.h" |
| 6 | #include "audio_core/opus/parameters.h" | 6 | #include "audio_core/opus/parameters.h" |
| 7 | #include "common/alignment.h" | 7 | #include "common/alignment.h" |
| 8 | #include "common/swap.h" | 8 | #include "common/swap.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | 10 | ||
| 11 | namespace AudioCore::OpusDecoder { | 11 | namespace AudioCore::OpusDecoder { |
| 12 | using namespace Service::Audio; | 12 | using namespace Service::Audio; |
| 13 | namespace { | 13 | namespace { |
| 14 | OpusPacketHeader ReverseHeader(OpusPacketHeader header) { | 14 | OpusPacketHeader ReverseHeader(OpusPacketHeader header) { |
| 15 | OpusPacketHeader out; | 15 | OpusPacketHeader out; |
| 16 | out.size = Common::swap32(header.size); | 16 | out.size = Common::swap32(header.size); |
| 17 | out.final_range = Common::swap32(header.final_range); | 17 | out.final_range = Common::swap32(header.final_range); |
| 18 | return out; | 18 | return out; |
| 19 | } | 19 | } |
| 20 | } // namespace | 20 | } // namespace |
| 21 | 21 | ||
| 22 | OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_) | 22 | OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_) |
| 23 | : system{system_}, hardware_opus{hardware_opus_} {} | 23 | : system{system_}, hardware_opus{hardware_opus_} {} |
| 24 | 24 | ||
| 25 | OpusDecoder::~OpusDecoder() { | 25 | OpusDecoder::~OpusDecoder() { |
| 26 | if (decode_object_initialized) { | 26 | if (decode_object_initialized) { |
| 27 | hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size); | 27 | hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size); |
| 28 | } | 28 | } |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, | 31 | Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, |
| 32 | u64 transfer_memory_size) { | 32 | u64 transfer_memory_size) { |
| 33 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | 33 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; |
| 34 | shared_buffer_size = transfer_memory_size; | 34 | shared_buffer_size = transfer_memory_size; |
| 35 | shared_buffer = std::make_unique<u8[]>(shared_buffer_size); | 35 | shared_buffer = std::make_unique<u8[]>(shared_buffer_size); |
| 36 | shared_memory_mapped = true; | 36 | shared_memory_mapped = true; |
| 37 | 37 | ||
| 38 | buffer_size = | 38 | buffer_size = |
| 39 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); | 39 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); |
| 40 | 40 | ||
| 41 | out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; | 41 | out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; |
| 42 | size_t in_data_size{0x600u}; | 42 | size_t in_data_size{0x600u}; |
| 43 | in_data = {out_data.data() - in_data_size, in_data_size}; | 43 | in_data = {out_data.data() - in_data_size, in_data_size}; |
| 44 | 44 | ||
| 45 | ON_RESULT_FAILURE { | 45 | ON_RESULT_FAILURE { |
| 46 | if (shared_memory_mapped) { | 46 | if (shared_memory_mapped) { |
| 47 | shared_memory_mapped = false; | 47 | shared_memory_mapped = false; |
| 48 | ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); | 48 | ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); |
| 49 | } | 49 | } |
| 50 | }; | 50 | }; |
| 51 | 51 | ||
| 52 | R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count, | 52 | R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count, |
| 53 | shared_buffer.get(), shared_buffer_size)); | 53 | shared_buffer.get(), shared_buffer_size)); |
| 54 | 54 | ||
| 55 | sample_rate = params.sample_rate; | 55 | sample_rate = params.sample_rate; |
| 56 | channel_count = params.channel_count; | 56 | channel_count = params.channel_count; |
| 57 | use_large_frame_size = params.use_large_frame_size; | 57 | use_large_frame_size = params.use_large_frame_size; |
| 58 | decode_object_initialized = true; | 58 | decode_object_initialized = true; |
| 59 | R_SUCCEED(); | 59 | R_SUCCEED(); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params, | 62 | Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params, |
| 63 | Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { | 63 | Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { |
| 64 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | 64 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; |
| 65 | shared_buffer_size = transfer_memory_size; | 65 | shared_buffer_size = transfer_memory_size; |
| 66 | shared_buffer = std::make_unique<u8[]>(shared_buffer_size); | 66 | shared_buffer = std::make_unique<u8[]>(shared_buffer_size); |
| 67 | shared_memory_mapped = true; | 67 | shared_memory_mapped = true; |
| 68 | 68 | ||
| 69 | buffer_size = | 69 | buffer_size = |
| 70 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); | 70 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); |
| 71 | 71 | ||
| 72 | out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; | 72 | out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; |
| 73 | size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)}; | 73 | size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)}; |
| 74 | in_data = {out_data.data() - in_data_size, in_data_size}; | 74 | in_data = {out_data.data() - in_data_size, in_data_size}; |
| 75 | 75 | ||
| 76 | ON_RESULT_FAILURE { | 76 | ON_RESULT_FAILURE { |
| 77 | if (shared_memory_mapped) { | 77 | if (shared_memory_mapped) { |
| 78 | shared_memory_mapped = false; | 78 | shared_memory_mapped = false; |
| 79 | ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); | 79 | ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); |
| 80 | } | 80 | } |
| 81 | }; | 81 | }; |
| 82 | 82 | ||
| 83 | R_TRY(hardware_opus.InitializeMultiStreamDecodeObject( | 83 | R_TRY(hardware_opus.InitializeMultiStreamDecodeObject( |
| 84 | params.sample_rate, params.channel_count, params.total_stream_count, | 84 | params.sample_rate, params.channel_count, params.total_stream_count, |
| 85 | params.stereo_stream_count, params.mappings.data(), shared_buffer.get(), | 85 | params.stereo_stream_count, params.mappings.data(), shared_buffer.get(), |
| 86 | shared_buffer_size)); | 86 | shared_buffer_size)); |
| 87 | 87 | ||
| 88 | sample_rate = params.sample_rate; | 88 | sample_rate = params.sample_rate; |
| 89 | channel_count = params.channel_count; | 89 | channel_count = params.channel_count; |
| 90 | total_stream_count = params.total_stream_count; | 90 | total_stream_count = params.total_stream_count; |
| 91 | stereo_stream_count = params.stereo_stream_count; | 91 | stereo_stream_count = params.stereo_stream_count; |
| 92 | use_large_frame_size = params.use_large_frame_size; | 92 | use_large_frame_size = params.use_large_frame_size; |
| 93 | decode_object_initialized = true; | 93 | decode_object_initialized = true; |
| 94 | R_SUCCEED(); | 94 | R_SUCCEED(); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken, | 97 | Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken, |
| 98 | u32* out_sample_count, std::span<const u8> input_data, | 98 | u32* out_sample_count, std::span<const u8> input_data, |
| 99 | std::span<u8> output_data, bool reset) { | 99 | std::span<u8> output_data, bool reset) { |
| 100 | u32 out_samples; | 100 | u32 out_samples; |
| 101 | u64 time_taken{}; | 101 | u64 time_taken{}; |
| 102 | 102 | ||
| 103 | R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); | 103 | R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); |
| 104 | 104 | ||
| 105 | auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; | 105 | auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; |
| 106 | OpusPacketHeader header{ReverseHeader(*header_p)}; | 106 | OpusPacketHeader header{ReverseHeader(*header_p)}; |
| 107 | 107 | ||
| 108 | R_UNLESS(in_data.size_bytes() >= header.size && | 108 | R_UNLESS(in_data.size_bytes() >= header.size && |
| 109 | header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), | 109 | header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), |
| 110 | ResultBufferTooSmall); | 110 | ResultBufferTooSmall); |
| 111 | 111 | ||
| 112 | if (!shared_memory_mapped) { | 112 | if (!shared_memory_mapped) { |
| 113 | R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); | 113 | R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); |
| 114 | shared_memory_mapped = true; | 114 | shared_memory_mapped = true; |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); | 117 | std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); |
| 118 | 118 | ||
| 119 | R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(), | 119 | R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(), |
| 120 | channel_count, in_data.data(), header.size, | 120 | channel_count, in_data.data(), header.size, |
| 121 | shared_buffer.get(), time_taken, reset)); | 121 | shared_buffer.get(), time_taken, reset)); |
| 122 | 122 | ||
| 123 | std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); | 123 | std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); |
| 124 | 124 | ||
| 125 | *out_data_size = header.size + sizeof(OpusPacketHeader); | 125 | *out_data_size = header.size + sizeof(OpusPacketHeader); |
| 126 | *out_sample_count = out_samples; | 126 | *out_sample_count = out_samples; |
| 127 | if (out_time_taken) { | 127 | if (out_time_taken) { |
| 128 | *out_time_taken = time_taken / 1000; | 128 | *out_time_taken = time_taken / 1000; |
| 129 | } | 129 | } |
| 130 | R_SUCCEED(); | 130 | R_SUCCEED(); |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) { | 133 | Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) { |
| 134 | R_SUCCEED_IF(shared_memory_mapped); | 134 | R_SUCCEED_IF(shared_memory_mapped); |
| 135 | shared_memory_mapped = true; | 135 | shared_memory_mapped = true; |
| 136 | R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); | 136 | R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, | 139 | Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, |
| 140 | u32* out_sample_count, | 140 | u32* out_sample_count, |
| 141 | std::span<const u8> input_data, | 141 | std::span<const u8> input_data, |
| 142 | std::span<u8> output_data, bool reset) { | 142 | std::span<u8> output_data, bool reset) { |
| 143 | u32 out_samples; | 143 | u32 out_samples; |
| 144 | u64 time_taken{}; | 144 | u64 time_taken{}; |
| 145 | 145 | ||
| 146 | R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); | 146 | R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); |
| 147 | 147 | ||
| 148 | auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; | 148 | auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; |
| 149 | OpusPacketHeader header{ReverseHeader(*header_p)}; | 149 | OpusPacketHeader header{ReverseHeader(*header_p)}; |
| 150 | 150 | ||
| 151 | LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", | 151 | LOG_TRACE(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", |
| 152 | header.size, input_data.size_bytes(), in_data.size_bytes()); | 152 | header.size, input_data.size_bytes(), in_data.size_bytes()); |
| 153 | 153 | ||
| 154 | R_UNLESS(in_data.size_bytes() >= header.size && | 154 | R_UNLESS(in_data.size_bytes() >= header.size && |
| 155 | header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), | 155 | header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), |
| 156 | ResultBufferTooSmall); | 156 | ResultBufferTooSmall); |
| 157 | 157 | ||
| 158 | if (!shared_memory_mapped) { | 158 | if (!shared_memory_mapped) { |
| 159 | R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); | 159 | R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); |
| 160 | shared_memory_mapped = true; | 160 | shared_memory_mapped = true; |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); | 163 | std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); |
| 164 | 164 | ||
| 165 | R_TRY(hardware_opus.DecodeInterleavedForMultiStream( | 165 | R_TRY(hardware_opus.DecodeInterleavedForMultiStream( |
| 166 | out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(), | 166 | out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(), |
| 167 | header.size, shared_buffer.get(), time_taken, reset)); | 167 | header.size, shared_buffer.get(), time_taken, reset)); |
| 168 | 168 | ||
| 169 | std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); | 169 | std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); |
| 170 | 170 | ||
| 171 | *out_data_size = header.size + sizeof(OpusPacketHeader); | 171 | *out_data_size = header.size + sizeof(OpusPacketHeader); |
| 172 | *out_sample_count = out_samples; | 172 | *out_sample_count = out_samples; |
| 173 | if (out_time_taken) { | 173 | if (out_time_taken) { |
| 174 | *out_time_taken = time_taken / 1000; | 174 | *out_time_taken = time_taken / 1000; |
| 175 | } | 175 | } |
| 176 | R_SUCCEED(); | 176 | R_SUCCEED(); |
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | } // namespace AudioCore::OpusDecoder | 179 | } // namespace AudioCore::OpusDecoder |
diff --git a/src/audio_core/opus/decoder.h b/src/audio_core/opus/decoder.h index d08d8a4a4..fd728958a 100644 --- a/src/audio_core/opus/decoder.h +++ b/src/audio_core/opus/decoder.h | |||
| @@ -1,53 +1,53 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <span> | 6 | #include <span> |
| 7 | 7 | ||
| 8 | #include "audio_core/opus/parameters.h" | 8 | #include "audio_core/opus/parameters.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/hle/kernel/k_transfer_memory.h" | 10 | #include "core/hle/kernel/k_transfer_memory.h" |
| 11 | #include "core/hle/service/audio/errors.h" | 11 | #include "core/hle/service/audio/errors.h" |
| 12 | 12 | ||
| 13 | namespace Core { | 13 | namespace Core { |
| 14 | class System; | 14 | class System; |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | namespace AudioCore::OpusDecoder { | 17 | namespace AudioCore::OpusDecoder { |
| 18 | class HardwareOpus; | 18 | class HardwareOpus; |
| 19 | 19 | ||
| 20 | class OpusDecoder { | 20 | class OpusDecoder { |
| 21 | public: | 21 | public: |
| 22 | explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_); | 22 | explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_); |
| 23 | ~OpusDecoder(); | 23 | ~OpusDecoder(); |
| 24 | 24 | ||
| 25 | Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, | 25 | Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, |
| 26 | u64 transfer_memory_size); | 26 | u64 transfer_memory_size); |
| 27 | Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, | 27 | Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, |
| 28 | u64 transfer_memory_size); | 28 | u64 transfer_memory_size); |
| 29 | Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count, | 29 | Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count, |
| 30 | std::span<const u8> input_data, std::span<u8> output_data, bool reset); | 30 | std::span<const u8> input_data, std::span<u8> output_data, bool reset); |
| 31 | Result SetContext([[maybe_unused]] std::span<const u8> context); | 31 | Result SetContext([[maybe_unused]] std::span<const u8> context); |
| 32 | Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, | 32 | Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, |
| 33 | u32* out_sample_count, std::span<const u8> input_data, | 33 | u32* out_sample_count, std::span<const u8> input_data, |
| 34 | std::span<u8> output_data, bool reset); | 34 | std::span<u8> output_data, bool reset); |
| 35 | 35 | ||
| 36 | private: | 36 | private: |
| 37 | Core::System& system; | 37 | Core::System& system; |
| 38 | HardwareOpus& hardware_opus; | 38 | HardwareOpus& hardware_opus; |
| 39 | std::unique_ptr<u8[]> shared_buffer{}; | 39 | std::unique_ptr<u8[]> shared_buffer{}; |
| 40 | u64 shared_buffer_size; | 40 | u64 shared_buffer_size; |
| 41 | std::span<u8> in_data{}; | 41 | std::span<u8> in_data{}; |
| 42 | std::span<u8> out_data{}; | 42 | std::span<u8> out_data{}; |
| 43 | u64 buffer_size{}; | 43 | u64 buffer_size{}; |
| 44 | s32 sample_rate{}; | 44 | s32 sample_rate{}; |
| 45 | s32 channel_count{}; | 45 | s32 channel_count{}; |
| 46 | bool use_large_frame_size{false}; | 46 | bool use_large_frame_size{false}; |
| 47 | s32 total_stream_count{}; | 47 | s32 total_stream_count{}; |
| 48 | s32 stereo_stream_count{}; | 48 | s32 stereo_stream_count{}; |
| 49 | bool shared_memory_mapped{false}; | 49 | bool shared_memory_mapped{false}; |
| 50 | bool decode_object_initialized{false}; | 50 | bool decode_object_initialized{false}; |
| 51 | }; | 51 | }; |
| 52 | 52 | ||
| 53 | } // namespace AudioCore::OpusDecoder | 53 | } // namespace AudioCore::OpusDecoder |
diff --git a/src/audio_core/opus/decoder_manager.cpp b/src/audio_core/opus/decoder_manager.cpp index 4a5382973..1464880a1 100644 --- a/src/audio_core/opus/decoder_manager.cpp +++ b/src/audio_core/opus/decoder_manager.cpp | |||
| @@ -1,102 +1,102 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "audio_core/adsp/apps/opus/opus_decoder.h" | 4 | #include "audio_core/adsp/apps/opus/opus_decoder.h" |
| 5 | #include "audio_core/opus/decoder_manager.h" | 5 | #include "audio_core/opus/decoder_manager.h" |
| 6 | #include "common/alignment.h" | 6 | #include "common/alignment.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | 8 | ||
| 9 | namespace AudioCore::OpusDecoder { | 9 | namespace AudioCore::OpusDecoder { |
| 10 | using namespace Service::Audio; | 10 | using namespace Service::Audio; |
| 11 | 11 | ||
| 12 | namespace { | 12 | namespace { |
| 13 | bool IsValidChannelCount(u32 channel_count) { | 13 | bool IsValidChannelCount(u32 channel_count) { |
| 14 | return channel_count == 1 || channel_count == 2; | 14 | return channel_count == 1 || channel_count == 2; |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | bool IsValidMultiStreamChannelCount(u32 channel_count) { | 17 | bool IsValidMultiStreamChannelCount(u32 channel_count) { |
| 18 | return channel_count > 0 && channel_count <= OpusStreamCountMax; | 18 | return channel_count > 0 && channel_count <= OpusStreamCountMax; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | bool IsValidSampleRate(u32 sample_rate) { | 21 | bool IsValidSampleRate(u32 sample_rate) { |
| 22 | return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 || | 22 | return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 || |
| 23 | sample_rate == 24'000 || sample_rate == 48'000; | 23 | sample_rate == 24'000 || sample_rate == 48'000; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) { | 26 | bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) { |
| 27 | return total_stream_count > 0 && stereo_stream_count > 0 && | 27 | return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 && |
| 28 | stereo_stream_count <= total_stream_count && | 28 | stereo_stream_count <= total_stream_count && |
| 29 | total_stream_count + stereo_stream_count <= channel_count; | 29 | total_stream_count + stereo_stream_count <= channel_count; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | } // namespace | 32 | } // namespace |
| 33 | 33 | ||
| 34 | OpusDecoderManager::OpusDecoderManager(Core::System& system_) | 34 | OpusDecoderManager::OpusDecoderManager(Core::System& system_) |
| 35 | : system{system_}, hardware_opus{system} { | 35 | : system{system_}, hardware_opus{system} { |
| 36 | for (u32 i = 0; i < MaxChannels; i++) { | 36 | for (u32 i = 0; i < MaxChannels; i++) { |
| 37 | required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i); | 37 | required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i); |
| 38 | } | 38 | } |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) { | 41 | Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) { |
| 42 | OpusParametersEx ex{ | 42 | OpusParametersEx ex{ |
| 43 | .sample_rate = params.sample_rate, | 43 | .sample_rate = params.sample_rate, |
| 44 | .channel_count = params.channel_count, | 44 | .channel_count = params.channel_count, |
| 45 | .use_large_frame_size = false, | 45 | .use_large_frame_size = false, |
| 46 | }; | 46 | }; |
| 47 | R_RETURN(GetWorkBufferSizeExEx(ex, out_size)); | 47 | R_RETURN(GetWorkBufferSizeExEx(ex, out_size)); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) { | 50 | Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) { |
| 51 | R_RETURN(GetWorkBufferSizeExEx(params, out_size)); | 51 | R_RETURN(GetWorkBufferSizeExEx(params, out_size)); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) { | 54 | Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) { |
| 55 | R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount); | 55 | R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount); |
| 56 | R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); | 56 | R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); |
| 57 | 57 | ||
| 58 | auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]}; | 58 | auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]}; |
| 59 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | 59 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; |
| 60 | work_buffer_size += | 60 | work_buffer_size += |
| 61 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); | 61 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); |
| 62 | out_size = work_buffer_size + 0x600; | 62 | out_size = work_buffer_size + 0x600; |
| 63 | R_SUCCEED(); | 63 | R_SUCCEED(); |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, | 66 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, |
| 67 | u64& out_size) { | 67 | u64& out_size) { |
| 68 | OpusMultiStreamParametersEx ex{ | 68 | OpusMultiStreamParametersEx ex{ |
| 69 | .sample_rate = params.sample_rate, | 69 | .sample_rate = params.sample_rate, |
| 70 | .channel_count = params.channel_count, | 70 | .channel_count = params.channel_count, |
| 71 | .total_stream_count = params.total_stream_count, | 71 | .total_stream_count = params.total_stream_count, |
| 72 | .stereo_stream_count = params.stereo_stream_count, | 72 | .stereo_stream_count = params.stereo_stream_count, |
| 73 | .use_large_frame_size = false, | 73 | .use_large_frame_size = false, |
| 74 | .mappings = {}, | 74 | .mappings = {}, |
| 75 | }; | 75 | }; |
| 76 | R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size)); | 76 | R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size)); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, | 79 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, |
| 80 | u64& out_size) { | 80 | u64& out_size) { |
| 81 | R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size)); | 81 | R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size)); |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, | 84 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, |
| 85 | u64& out_size) { | 85 | u64& out_size) { |
| 86 | R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount); | 86 | R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount); |
| 87 | R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); | 87 | R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); |
| 88 | R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count, | 88 | R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count, |
| 89 | params.stereo_stream_count), | 89 | params.stereo_stream_count), |
| 90 | ResultInvalidOpusSampleRate); | 90 | ResultInvalidOpusSampleRate); |
| 91 | 91 | ||
| 92 | auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream( | 92 | auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream( |
| 93 | params.total_stream_count, params.stereo_stream_count)}; | 93 | params.total_stream_count, params.stereo_stream_count)}; |
| 94 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | 94 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; |
| 95 | work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64); | 95 | work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64); |
| 96 | work_buffer_size += | 96 | work_buffer_size += |
| 97 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); | 97 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); |
| 98 | out_size = work_buffer_size; | 98 | out_size = work_buffer_size; |
| 99 | R_SUCCEED(); | 99 | R_SUCCEED(); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | } // namespace AudioCore::OpusDecoder | 102 | } // namespace AudioCore::OpusDecoder |
diff --git a/src/audio_core/opus/decoder_manager.h b/src/audio_core/opus/decoder_manager.h index 466e1967b..70ebc4bab 100644 --- a/src/audio_core/opus/decoder_manager.h +++ b/src/audio_core/opus/decoder_manager.h | |||
| @@ -1,38 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "audio_core/opus/hardware_opus.h" | 6 | #include "audio_core/opus/hardware_opus.h" |
| 7 | #include "audio_core/opus/parameters.h" | 7 | #include "audio_core/opus/parameters.h" |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/hle/service/audio/errors.h" | 9 | #include "core/hle/service/audio/errors.h" |
| 10 | 10 | ||
| 11 | namespace Core { | 11 | namespace Core { |
| 12 | class System; | 12 | class System; |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | namespace AudioCore::OpusDecoder { | 15 | namespace AudioCore::OpusDecoder { |
| 16 | 16 | ||
| 17 | class OpusDecoderManager { | 17 | class OpusDecoderManager { |
| 18 | public: | 18 | public: |
| 19 | OpusDecoderManager(Core::System& system); | 19 | OpusDecoderManager(Core::System& system); |
| 20 | 20 | ||
| 21 | HardwareOpus& GetHardwareOpus() { | 21 | HardwareOpus& GetHardwareOpus() { |
| 22 | return hardware_opus; | 22 | return hardware_opus; |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | Result GetWorkBufferSize(OpusParameters& params, u64& out_size); | 25 | Result GetWorkBufferSize(OpusParameters& params, u64& out_size); |
| 26 | Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size); | 26 | Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size); |
| 27 | Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size); | 27 | Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size); |
| 28 | Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size); | 28 | Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size); |
| 29 | Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size); | 29 | Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size); |
| 30 | Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size); | 30 | Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size); |
| 31 | 31 | ||
| 32 | private: | 32 | private: |
| 33 | Core::System& system; | 33 | Core::System& system; |
| 34 | HardwareOpus hardware_opus; | 34 | HardwareOpus hardware_opus; |
| 35 | std::array<u64, MaxChannels> required_workbuffer_sizes{}; | 35 | std::array<u64, MaxChannels> required_workbuffer_sizes{}; |
| 36 | }; | 36 | }; |
| 37 | 37 | ||
| 38 | } // namespace AudioCore::OpusDecoder | 38 | } // namespace AudioCore::OpusDecoder |
diff --git a/src/audio_core/opus/hardware_opus.cpp b/src/audio_core/opus/hardware_opus.cpp index d6544dcb0..5ff71ab2d 100644 --- a/src/audio_core/opus/hardware_opus.cpp +++ b/src/audio_core/opus/hardware_opus.cpp | |||
| @@ -1,241 +1,241 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <array> | 4 | #include <array> |
| 5 | 5 | ||
| 6 | #include "audio_core/audio_core.h" | 6 | #include "audio_core/audio_core.h" |
| 7 | #include "audio_core/opus/hardware_opus.h" | 7 | #include "audio_core/opus/hardware_opus.h" |
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | 9 | ||
| 10 | namespace AudioCore::OpusDecoder { | 10 | namespace AudioCore::OpusDecoder { |
| 11 | namespace { | 11 | namespace { |
| 12 | using namespace Service::Audio; | 12 | using namespace Service::Audio; |
| 13 | 13 | ||
| 14 | static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) { | 14 | static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) { |
| 15 | s32 error{static_cast<s32>(error_code)}; | 15 | s32 error{static_cast<s32>(error_code)}; |
| 16 | ASSERT(error <= OPUS_OK); | 16 | ASSERT(error <= OPUS_OK); |
| 17 | switch (error) { | 17 | switch (error) { |
| 18 | case OPUS_ALLOC_FAIL: | 18 | case OPUS_ALLOC_FAIL: |
| 19 | R_THROW(ResultLibOpusAllocFail); | 19 | R_THROW(ResultLibOpusAllocFail); |
| 20 | case OPUS_INVALID_STATE: | 20 | case OPUS_INVALID_STATE: |
| 21 | R_THROW(ResultLibOpusInvalidState); | 21 | R_THROW(ResultLibOpusInvalidState); |
| 22 | case OPUS_UNIMPLEMENTED: | 22 | case OPUS_UNIMPLEMENTED: |
| 23 | R_THROW(ResultLibOpusUnimplemented); | 23 | R_THROW(ResultLibOpusUnimplemented); |
| 24 | case OPUS_INVALID_PACKET: | 24 | case OPUS_INVALID_PACKET: |
| 25 | R_THROW(ResultLibOpusInvalidPacket); | 25 | R_THROW(ResultLibOpusInvalidPacket); |
| 26 | case OPUS_INTERNAL_ERROR: | 26 | case OPUS_INTERNAL_ERROR: |
| 27 | R_THROW(ResultLibOpusInternalError); | 27 | R_THROW(ResultLibOpusInternalError); |
| 28 | case OPUS_BUFFER_TOO_SMALL: | 28 | case OPUS_BUFFER_TOO_SMALL: |
| 29 | R_THROW(ResultBufferTooSmall); | 29 | R_THROW(ResultBufferTooSmall); |
| 30 | case OPUS_BAD_ARG: | 30 | case OPUS_BAD_ARG: |
| 31 | R_THROW(ResultLibOpusBadArg); | 31 | R_THROW(ResultLibOpusBadArg); |
| 32 | case OPUS_OK: | 32 | case OPUS_OK: |
| 33 | R_RETURN(ResultSuccess); | 33 | R_RETURN(ResultSuccess); |
| 34 | } | 34 | } |
| 35 | UNREACHABLE(); | 35 | UNREACHABLE(); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | } // namespace | 38 | } // namespace |
| 39 | 39 | ||
| 40 | HardwareOpus::HardwareOpus(Core::System& system_) | 40 | HardwareOpus::HardwareOpus(Core::System& system_) |
| 41 | : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} { | 41 | : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} { |
| 42 | opus_decoder.SetSharedMemory(shared_memory); | 42 | opus_decoder.SetSharedMemory(shared_memory); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | u64 HardwareOpus::GetWorkBufferSize(u32 channel) { | 45 | u64 HardwareOpus::GetWorkBufferSize(u32 channel) { |
| 46 | if (!opus_decoder.IsRunning()) { | 46 | if (!opus_decoder.IsRunning()) { |
| 47 | return 0; | 47 | return 0; |
| 48 | } | 48 | } |
| 49 | std::scoped_lock l{mutex}; | 49 | std::scoped_lock l{mutex}; |
| 50 | shared_memory.host_send_data[0] = channel; | 50 | shared_memory.host_send_data[0] = channel; |
| 51 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize); | 51 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize); |
| 52 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | 52 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); |
| 53 | if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) { | 53 | if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) { |
| 54 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | 54 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", |
| 55 | ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg); | 55 | ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg); |
| 56 | return 0; | 56 | return 0; |
| 57 | } | 57 | } |
| 58 | return shared_memory.dsp_return_data[0]; | 58 | return shared_memory.dsp_return_data[0]; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) { | 61 | u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) { |
| 62 | std::scoped_lock l{mutex}; | 62 | std::scoped_lock l{mutex}; |
| 63 | shared_memory.host_send_data[0] = total_stream_count; | 63 | shared_memory.host_send_data[0] = total_stream_count; |
| 64 | shared_memory.host_send_data[1] = stereo_stream_count; | 64 | shared_memory.host_send_data[1] = stereo_stream_count; |
| 65 | opus_decoder.Send(ADSP::Direction::DSP, | 65 | opus_decoder.Send(ADSP::Direction::DSP, |
| 66 | ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream); | 66 | ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream); |
| 67 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | 67 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); |
| 68 | if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) { | 68 | if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) { |
| 69 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | 69 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", |
| 70 | ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg); | 70 | ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg); |
| 71 | return 0; | 71 | return 0; |
| 72 | } | 72 | } |
| 73 | return shared_memory.dsp_return_data[0]; | 73 | return shared_memory.dsp_return_data[0]; |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, | 76 | Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, |
| 77 | u64 buffer_size) { | 77 | u64 buffer_size) { |
| 78 | std::scoped_lock l{mutex}; | 78 | std::scoped_lock l{mutex}; |
| 79 | shared_memory.host_send_data[0] = (u64)buffer; | 79 | shared_memory.host_send_data[0] = (u64)buffer; |
| 80 | shared_memory.host_send_data[1] = buffer_size; | 80 | shared_memory.host_send_data[1] = buffer_size; |
| 81 | shared_memory.host_send_data[2] = sample_rate; | 81 | shared_memory.host_send_data[2] = sample_rate; |
| 82 | shared_memory.host_send_data[3] = channel_count; | 82 | shared_memory.host_send_data[3] = channel_count; |
| 83 | 83 | ||
| 84 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject); | 84 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject); |
| 85 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | 85 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); |
| 86 | if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) { | 86 | if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) { |
| 87 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | 87 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", |
| 88 | ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg); | 88 | ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg); |
| 89 | R_THROW(ResultInvalidOpusDSPReturnCode); | 89 | R_THROW(ResultInvalidOpusDSPReturnCode); |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | 92 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, | 95 | Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, |
| 96 | u32 total_stream_count, | 96 | u32 total_stream_count, |
| 97 | u32 stereo_stream_count, void* mappings, | 97 | u32 stereo_stream_count, void* mappings, |
| 98 | void* buffer, u64 buffer_size) { | 98 | void* buffer, u64 buffer_size) { |
| 99 | std::scoped_lock l{mutex}; | 99 | std::scoped_lock l{mutex}; |
| 100 | shared_memory.host_send_data[0] = (u64)buffer; | 100 | shared_memory.host_send_data[0] = (u64)buffer; |
| 101 | shared_memory.host_send_data[1] = buffer_size; | 101 | shared_memory.host_send_data[1] = buffer_size; |
| 102 | shared_memory.host_send_data[2] = sample_rate; | 102 | shared_memory.host_send_data[2] = sample_rate; |
| 103 | shared_memory.host_send_data[3] = channel_count; | 103 | shared_memory.host_send_data[3] = channel_count; |
| 104 | shared_memory.host_send_data[4] = total_stream_count; | 104 | shared_memory.host_send_data[4] = total_stream_count; |
| 105 | shared_memory.host_send_data[5] = stereo_stream_count; | 105 | shared_memory.host_send_data[5] = stereo_stream_count; |
| 106 | 106 | ||
| 107 | ASSERT(channel_count <= MaxChannels); | 107 | ASSERT(channel_count <= MaxChannels); |
| 108 | std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8)); | 108 | std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8)); |
| 109 | 109 | ||
| 110 | opus_decoder.Send(ADSP::Direction::DSP, | 110 | opus_decoder.Send(ADSP::Direction::DSP, |
| 111 | ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject); | 111 | ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject); |
| 112 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | 112 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); |
| 113 | if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) { | 113 | if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) { |
| 114 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | 114 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", |
| 115 | ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg); | 115 | ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg); |
| 116 | R_THROW(ResultInvalidOpusDSPReturnCode); | 116 | R_THROW(ResultInvalidOpusDSPReturnCode); |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | 119 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); |
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { | 122 | Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { |
| 123 | std::scoped_lock l{mutex}; | 123 | std::scoped_lock l{mutex}; |
| 124 | shared_memory.host_send_data[0] = (u64)buffer; | 124 | shared_memory.host_send_data[0] = (u64)buffer; |
| 125 | shared_memory.host_send_data[1] = buffer_size; | 125 | shared_memory.host_send_data[1] = buffer_size; |
| 126 | 126 | ||
| 127 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject); | 127 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject); |
| 128 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | 128 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); |
| 129 | ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, | 129 | ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, |
| 130 | "Expected Opus shutdown code {}, got {}", | 130 | "Expected Opus shutdown code {}, got {}", |
| 131 | ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg); | 131 | ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg); |
| 132 | 132 | ||
| 133 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | 133 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) { | 136 | Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) { |
| 137 | std::scoped_lock l{mutex}; | 137 | std::scoped_lock l{mutex}; |
| 138 | shared_memory.host_send_data[0] = (u64)buffer; | 138 | shared_memory.host_send_data[0] = (u64)buffer; |
| 139 | shared_memory.host_send_data[1] = buffer_size; | 139 | shared_memory.host_send_data[1] = buffer_size; |
| 140 | 140 | ||
| 141 | opus_decoder.Send(ADSP::Direction::DSP, | 141 | opus_decoder.Send(ADSP::Direction::DSP, |
| 142 | ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject); | 142 | ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject); |
| 143 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | 143 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); |
| 144 | ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, | 144 | ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, |
| 145 | "Expected Opus shutdown code {}, got {}", | 145 | "Expected Opus shutdown code {}, got {}", |
| 146 | ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg); | 146 | ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg); |
| 147 | 147 | ||
| 148 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | 148 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, | 151 | Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, |
| 152 | u64 output_data_size, u32 channel_count, void* input_data, | 152 | u64 output_data_size, u32 channel_count, void* input_data, |
| 153 | u64 input_data_size, void* buffer, u64& out_time_taken, | 153 | u64 input_data_size, void* buffer, u64& out_time_taken, |
| 154 | bool reset) { | 154 | bool reset) { |
| 155 | std::scoped_lock l{mutex}; | 155 | std::scoped_lock l{mutex}; |
| 156 | shared_memory.host_send_data[0] = (u64)buffer; | 156 | shared_memory.host_send_data[0] = (u64)buffer; |
| 157 | shared_memory.host_send_data[1] = (u64)input_data; | 157 | shared_memory.host_send_data[1] = (u64)input_data; |
| 158 | shared_memory.host_send_data[2] = input_data_size; | 158 | shared_memory.host_send_data[2] = input_data_size; |
| 159 | shared_memory.host_send_data[3] = (u64)output_data; | 159 | shared_memory.host_send_data[3] = (u64)output_data; |
| 160 | shared_memory.host_send_data[4] = output_data_size; | 160 | shared_memory.host_send_data[4] = output_data_size; |
| 161 | shared_memory.host_send_data[5] = 0; | 161 | shared_memory.host_send_data[5] = 0; |
| 162 | shared_memory.host_send_data[6] = reset; | 162 | shared_memory.host_send_data[6] = reset; |
| 163 | 163 | ||
| 164 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved); | 164 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved); |
| 165 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | 165 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); |
| 166 | if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) { | 166 | if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) { |
| 167 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | 167 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", |
| 168 | ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg); | 168 | ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg); |
| 169 | R_THROW(ResultInvalidOpusDSPReturnCode); | 169 | R_THROW(ResultInvalidOpusDSPReturnCode); |
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; | 172 | auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; |
| 173 | if (error_code == OPUS_OK) { | 173 | if (error_code == OPUS_OK) { |
| 174 | out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); | 174 | out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); |
| 175 | out_time_taken = 1000 * shared_memory.dsp_return_data[2]; | 175 | out_time_taken = 1000 * shared_memory.dsp_return_data[2]; |
| 176 | } | 176 | } |
| 177 | R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); | 177 | R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, | 180 | Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, |
| 181 | u64 output_data_size, u32 channel_count, | 181 | u64 output_data_size, u32 channel_count, |
| 182 | void* input_data, u64 input_data_size, | 182 | void* input_data, u64 input_data_size, |
| 183 | void* buffer, u64& out_time_taken, | 183 | void* buffer, u64& out_time_taken, |
| 184 | bool reset) { | 184 | bool reset) { |
| 185 | std::scoped_lock l{mutex}; | 185 | std::scoped_lock l{mutex}; |
| 186 | shared_memory.host_send_data[0] = (u64)buffer; | 186 | shared_memory.host_send_data[0] = (u64)buffer; |
| 187 | shared_memory.host_send_data[1] = (u64)input_data; | 187 | shared_memory.host_send_data[1] = (u64)input_data; |
| 188 | shared_memory.host_send_data[2] = input_data_size; | 188 | shared_memory.host_send_data[2] = input_data_size; |
| 189 | shared_memory.host_send_data[3] = (u64)output_data; | 189 | shared_memory.host_send_data[3] = (u64)output_data; |
| 190 | shared_memory.host_send_data[4] = output_data_size; | 190 | shared_memory.host_send_data[4] = output_data_size; |
| 191 | shared_memory.host_send_data[5] = 0; | 191 | shared_memory.host_send_data[5] = 0; |
| 192 | shared_memory.host_send_data[6] = reset; | 192 | shared_memory.host_send_data[6] = reset; |
| 193 | 193 | ||
| 194 | opus_decoder.Send(ADSP::Direction::DSP, | 194 | opus_decoder.Send(ADSP::Direction::DSP, |
| 195 | ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream); | 195 | ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream); |
| 196 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | 196 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); |
| 197 | if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) { | 197 | if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) { |
| 198 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | 198 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", |
| 199 | ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg); | 199 | ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg); |
| 200 | R_THROW(ResultInvalidOpusDSPReturnCode); | 200 | R_THROW(ResultInvalidOpusDSPReturnCode); |
| 201 | } | 201 | } |
| 202 | 202 | ||
| 203 | auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; | 203 | auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; |
| 204 | if (error_code == OPUS_OK) { | 204 | if (error_code == OPUS_OK) { |
| 205 | out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); | 205 | out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); |
| 206 | out_time_taken = 1000 * shared_memory.dsp_return_data[2]; | 206 | out_time_taken = 1000 * shared_memory.dsp_return_data[2]; |
| 207 | } | 207 | } |
| 208 | R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); | 208 | R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); |
| 209 | } | 209 | } |
| 210 | 210 | ||
| 211 | Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) { | 211 | Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) { |
| 212 | std::scoped_lock l{mutex}; | 212 | std::scoped_lock l{mutex}; |
| 213 | shared_memory.host_send_data[0] = (u64)buffer; | 213 | shared_memory.host_send_data[0] = (u64)buffer; |
| 214 | shared_memory.host_send_data[1] = buffer_size; | 214 | shared_memory.host_send_data[1] = buffer_size; |
| 215 | 215 | ||
| 216 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory); | 216 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory); |
| 217 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | 217 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); |
| 218 | if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) { | 218 | if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) { |
| 219 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | 219 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", |
| 220 | ADSP::OpusDecoder::Message::MapMemoryOK, msg); | 220 | ADSP::OpusDecoder::Message::MapMemoryOK, msg); |
| 221 | R_THROW(ResultInvalidOpusDSPReturnCode); | 221 | R_THROW(ResultInvalidOpusDSPReturnCode); |
| 222 | } | 222 | } |
| 223 | R_SUCCEED(); | 223 | R_SUCCEED(); |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) { | 226 | Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) { |
| 227 | std::scoped_lock l{mutex}; | 227 | std::scoped_lock l{mutex}; |
| 228 | shared_memory.host_send_data[0] = (u64)buffer; | 228 | shared_memory.host_send_data[0] = (u64)buffer; |
| 229 | shared_memory.host_send_data[1] = buffer_size; | 229 | shared_memory.host_send_data[1] = buffer_size; |
| 230 | 230 | ||
| 231 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory); | 231 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory); |
| 232 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | 232 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); |
| 233 | if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) { | 233 | if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) { |
| 234 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | 234 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", |
| 235 | ADSP::OpusDecoder::Message::UnmapMemoryOK, msg); | 235 | ADSP::OpusDecoder::Message::UnmapMemoryOK, msg); |
| 236 | R_THROW(ResultInvalidOpusDSPReturnCode); | 236 | R_THROW(ResultInvalidOpusDSPReturnCode); |
| 237 | } | 237 | } |
| 238 | R_SUCCEED(); | 238 | R_SUCCEED(); |
| 239 | } | 239 | } |
| 240 | 240 | ||
| 241 | } // namespace AudioCore::OpusDecoder | 241 | } // namespace AudioCore::OpusDecoder |
diff --git a/src/audio_core/opus/hardware_opus.h b/src/audio_core/opus/hardware_opus.h index 7013a6b40..b10184baa 100644 --- a/src/audio_core/opus/hardware_opus.h +++ b/src/audio_core/opus/hardware_opus.h | |||
| @@ -1,45 +1,45 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <mutex> | 6 | #include <mutex> |
| 7 | #include <opus.h> | 7 | #include <opus.h> |
| 8 | 8 | ||
| 9 | #include "audio_core/adsp/apps/opus/opus_decoder.h" | 9 | #include "audio_core/adsp/apps/opus/opus_decoder.h" |
| 10 | #include "audio_core/adsp/apps/opus/shared_memory.h" | 10 | #include "audio_core/adsp/apps/opus/shared_memory.h" |
| 11 | #include "audio_core/adsp/mailbox.h" | 11 | #include "audio_core/adsp/mailbox.h" |
| 12 | #include "core/hle/service/audio/errors.h" | 12 | #include "core/hle/service/audio/errors.h" |
| 13 | 13 | ||
| 14 | namespace AudioCore::OpusDecoder { | 14 | namespace AudioCore::OpusDecoder { |
| 15 | class HardwareOpus { | 15 | class HardwareOpus { |
| 16 | public: | 16 | public: |
| 17 | HardwareOpus(Core::System& system); | 17 | HardwareOpus(Core::System& system); |
| 18 | 18 | ||
| 19 | u64 GetWorkBufferSize(u32 channel); | 19 | u64 GetWorkBufferSize(u32 channel); |
| 20 | u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count); | 20 | u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count); |
| 21 | 21 | ||
| 22 | Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, | 22 | Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, |
| 23 | u64 buffer_size); | 23 | u64 buffer_size); |
| 24 | Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, | 24 | Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, |
| 25 | u32 totaL_stream_count, u32 stereo_stream_count, | 25 | u32 totaL_stream_count, u32 stereo_stream_count, |
| 26 | void* mappings, void* buffer, u64 buffer_size); | 26 | void* mappings, void* buffer, u64 buffer_size); |
| 27 | Result ShutdownDecodeObject(void* buffer, u64 buffer_size); | 27 | Result ShutdownDecodeObject(void* buffer, u64 buffer_size); |
| 28 | Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size); | 28 | Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size); |
| 29 | Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size, | 29 | Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size, |
| 30 | u32 channel_count, void* input_data, u64 input_data_size, void* buffer, | 30 | u32 channel_count, void* input_data, u64 input_data_size, void* buffer, |
| 31 | u64& out_time_taken, bool reset); | 31 | u64& out_time_taken, bool reset); |
| 32 | Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, | 32 | Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, |
| 33 | u64 output_data_size, u32 channel_count, | 33 | u64 output_data_size, u32 channel_count, |
| 34 | void* input_data, u64 input_data_size, void* buffer, | 34 | void* input_data, u64 input_data_size, void* buffer, |
| 35 | u64& out_time_taken, bool reset); | 35 | u64& out_time_taken, bool reset); |
| 36 | Result MapMemory(void* buffer, u64 buffer_size); | 36 | Result MapMemory(void* buffer, u64 buffer_size); |
| 37 | Result UnmapMemory(void* buffer, u64 buffer_size); | 37 | Result UnmapMemory(void* buffer, u64 buffer_size); |
| 38 | 38 | ||
| 39 | private: | 39 | private: |
| 40 | Core::System& system; | 40 | Core::System& system; |
| 41 | std::mutex mutex; | 41 | std::mutex mutex; |
| 42 | ADSP::OpusDecoder::OpusDecoder& opus_decoder; | 42 | ADSP::OpusDecoder::OpusDecoder& opus_decoder; |
| 43 | ADSP::OpusDecoder::SharedMemory shared_memory; | 43 | ADSP::OpusDecoder::SharedMemory shared_memory; |
| 44 | }; | 44 | }; |
| 45 | } // namespace AudioCore::OpusDecoder | 45 | } // namespace AudioCore::OpusDecoder |
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index bbb598bc5..51a23fe15 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp | |||
| @@ -146,7 +146,7 @@ public: | |||
| 146 | return; | 146 | return; |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | paused = true; | 149 | SignalPause(); |
| 150 | if (cubeb_stream_stop(stream_backend) != CUBEB_OK) { | 150 | if (cubeb_stream_stop(stream_backend) != CUBEB_OK) { |
| 151 | LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); | 151 | LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); |
| 152 | } | 152 | } |
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index 7b89151de..96e0efce2 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp | |||
| @@ -111,7 +111,7 @@ public: | |||
| 111 | if (device == 0 || paused) { | 111 | if (device == 0 || paused) { |
| 112 | return; | 112 | return; |
| 113 | } | 113 | } |
| 114 | paused = true; | 114 | SignalPause(); |
| 115 | SDL_PauseAudioDevice(device, 1); | 115 | SDL_PauseAudioDevice(device, 1); |
| 116 | } | 116 | } |
| 117 | 117 | ||
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index d66d04fae..2a09db599 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp | |||
| @@ -282,11 +282,19 @@ u64 SinkStream::GetExpectedPlayedSampleCount() { | |||
| 282 | void SinkStream::WaitFreeSpace(std::stop_token stop_token) { | 282 | void SinkStream::WaitFreeSpace(std::stop_token stop_token) { |
| 283 | std::unique_lock lk{release_mutex}; | 283 | std::unique_lock lk{release_mutex}; |
| 284 | release_cv.wait_for(lk, std::chrono::milliseconds(5), | 284 | release_cv.wait_for(lk, std::chrono::milliseconds(5), |
| 285 | [this]() { return queued_buffers < max_queue_size; }); | 285 | [this]() { return paused || queued_buffers < max_queue_size; }); |
| 286 | if (queued_buffers > max_queue_size + 3) { | 286 | if (queued_buffers > max_queue_size + 3) { |
| 287 | Common::CondvarWait(release_cv, lk, stop_token, | 287 | Common::CondvarWait(release_cv, lk, stop_token, |
| 288 | [this] { return queued_buffers < max_queue_size; }); | 288 | [this] { return paused || queued_buffers < max_queue_size; }); |
| 289 | } | 289 | } |
| 290 | } | 290 | } |
| 291 | 291 | ||
| 292 | void SinkStream::SignalPause() { | ||
| 293 | { | ||
| 294 | std::scoped_lock lk{release_mutex}; | ||
| 295 | paused = true; | ||
| 296 | } | ||
| 297 | release_cv.notify_one(); | ||
| 298 | } | ||
| 299 | |||
| 292 | } // namespace AudioCore::Sink | 300 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h index 6a4996ca3..f2ccd19b8 100644 --- a/src/audio_core/sink/sink_stream.h +++ b/src/audio_core/sink/sink_stream.h | |||
| @@ -214,6 +214,12 @@ public: | |||
| 214 | void WaitFreeSpace(std::stop_token stop_token); | 214 | void WaitFreeSpace(std::stop_token stop_token); |
| 215 | 215 | ||
| 216 | protected: | 216 | protected: |
| 217 | /** | ||
| 218 | * Unblocks the ADSP if the stream is paused. | ||
| 219 | */ | ||
| 220 | void SignalPause(); | ||
| 221 | |||
| 222 | protected: | ||
| 217 | /// Core system | 223 | /// Core system |
| 218 | Core::System& system; | 224 | Core::System& system; |
| 219 | /// Type of this stream | 225 | /// Type of this stream |
diff --git a/src/common/arm64/native_clock.cpp b/src/common/arm64/native_clock.cpp index 88fdba527..f437d7187 100644 --- a/src/common/arm64/native_clock.cpp +++ b/src/common/arm64/native_clock.cpp | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #ifdef ANDROID | ||
| 5 | #include <sys/system_properties.h> | ||
| 6 | #endif | ||
| 4 | #include "common/arm64/native_clock.h" | 7 | #include "common/arm64/native_clock.h" |
| 5 | 8 | ||
| 6 | namespace Common::Arm64 { | 9 | namespace Common::Arm64 { |
| @@ -65,7 +68,23 @@ bool NativeClock::IsNative() const { | |||
| 65 | 68 | ||
| 66 | u64 NativeClock::GetHostCNTFRQ() { | 69 | u64 NativeClock::GetHostCNTFRQ() { |
| 67 | u64 cntfrq_el0 = 0; | 70 | u64 cntfrq_el0 = 0; |
| 68 | asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); | 71 | std::string_view board{""}; |
| 72 | #ifdef ANDROID | ||
| 73 | char buffer[PROP_VALUE_MAX]; | ||
| 74 | int len{__system_property_get("ro.product.board", buffer)}; | ||
| 75 | board = std::string_view(buffer, static_cast<size_t>(len)); | ||
| 76 | #endif | ||
| 77 | if (board == "s5e9925") { // Exynos 2200 | ||
| 78 | cntfrq_el0 = 25600000; | ||
| 79 | } else if (board == "exynos2100") { // Exynos 2100 | ||
| 80 | cntfrq_el0 = 26000000; | ||
| 81 | } else if (board == "exynos9810") { // Exynos 9810 | ||
| 82 | cntfrq_el0 = 26000000; | ||
| 83 | } else if (board == "s5e8825") { // Exynos 1280 | ||
| 84 | cntfrq_el0 = 26000000; | ||
| 85 | } else { | ||
| 86 | asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); | ||
| 87 | } | ||
| 69 | return cntfrq_el0; | 88 | return cntfrq_el0; |
| 70 | } | 89 | } |
| 71 | 90 | ||
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index 4b1690269..166dc3dce 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp | |||
| @@ -9,12 +9,12 @@ PageTable::PageTable() = default; | |||
| 9 | 9 | ||
| 10 | PageTable::~PageTable() noexcept = default; | 10 | PageTable::~PageTable() noexcept = default; |
| 11 | 11 | ||
| 12 | bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, | 12 | bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context, |
| 13 | u64 address) const { | 13 | Common::ProcessAddress address) const { |
| 14 | // Setup invalid defaults. | 14 | // Setup invalid defaults. |
| 15 | out_entry.phys_addr = 0; | 15 | out_entry->phys_addr = 0; |
| 16 | out_entry.block_size = page_size; | 16 | out_entry->block_size = page_size; |
| 17 | out_context.next_page = 0; | 17 | out_context->next_page = 0; |
| 18 | 18 | ||
| 19 | // Validate that we can read the actual entry. | 19 | // Validate that we can read the actual entry. |
| 20 | const auto page = address / page_size; | 20 | const auto page = address / page_size; |
| @@ -29,20 +29,20 @@ bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_ | |||
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | // Populate the results. | 31 | // Populate the results. |
| 32 | out_entry.phys_addr = phys_addr + address; | 32 | out_entry->phys_addr = phys_addr + GetInteger(address); |
| 33 | out_context.next_page = page + 1; | 33 | out_context->next_page = page + 1; |
| 34 | out_context.next_offset = address + page_size; | 34 | out_context->next_offset = GetInteger(address) + page_size; |
| 35 | 35 | ||
| 36 | return true; | 36 | return true; |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const { | 39 | bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const { |
| 40 | // Setup invalid defaults. | 40 | // Setup invalid defaults. |
| 41 | out_entry.phys_addr = 0; | 41 | out_entry->phys_addr = 0; |
| 42 | out_entry.block_size = page_size; | 42 | out_entry->block_size = page_size; |
| 43 | 43 | ||
| 44 | // Validate that we can read the actual entry. | 44 | // Validate that we can read the actual entry. |
| 45 | const auto page = context.next_page; | 45 | const auto page = context->next_page; |
| 46 | if (page >= backing_addr.size()) { | 46 | if (page >= backing_addr.size()) { |
| 47 | return false; | 47 | return false; |
| 48 | } | 48 | } |
| @@ -54,9 +54,9 @@ bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& c | |||
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | // Populate the results. | 56 | // Populate the results. |
| 57 | out_entry.phys_addr = phys_addr + context.next_offset; | 57 | out_entry->phys_addr = phys_addr + context->next_offset; |
| 58 | context.next_page = page + 1; | 58 | context->next_page = page + 1; |
| 59 | context.next_offset += page_size; | 59 | context->next_offset += page_size; |
| 60 | 60 | ||
| 61 | return true; | 61 | return true; |
| 62 | } | 62 | } |
diff --git a/src/common/page_table.h b/src/common/page_table.h index e653d52ad..5340f7d86 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <atomic> | 6 | #include <atomic> |
| 7 | 7 | ||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/typed_address.h" | ||
| 9 | #include "common/virtual_buffer.h" | 10 | #include "common/virtual_buffer.h" |
| 10 | 11 | ||
| 11 | namespace Common { | 12 | namespace Common { |
| @@ -100,9 +101,9 @@ struct PageTable { | |||
| 100 | PageTable(PageTable&&) noexcept = default; | 101 | PageTable(PageTable&&) noexcept = default; |
| 101 | PageTable& operator=(PageTable&&) noexcept = default; | 102 | PageTable& operator=(PageTable&&) noexcept = default; |
| 102 | 103 | ||
| 103 | bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context, | 104 | bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context, |
| 104 | u64 address) const; | 105 | Common::ProcessAddress address) const; |
| 105 | bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const; | 106 | bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const; |
| 106 | 107 | ||
| 107 | /** | 108 | /** |
| 108 | * Resizes the page table to be able to accommodate enough pages within | 109 | * Resizes the page table to be able to accommodate enough pages within |
| @@ -117,6 +118,16 @@ struct PageTable { | |||
| 117 | return current_address_space_width_in_bits; | 118 | return current_address_space_width_in_bits; |
| 118 | } | 119 | } |
| 119 | 120 | ||
| 121 | bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr, | ||
| 122 | Common::ProcessAddress virt_addr) const { | ||
| 123 | if (virt_addr > (1ULL << this->GetAddressSpaceBits())) { | ||
| 124 | return false; | ||
| 125 | } | ||
| 126 | |||
| 127 | *out_phys_addr = backing_addr[virt_addr / page_size] + GetInteger(virt_addr); | ||
| 128 | return true; | ||
| 129 | } | ||
| 130 | |||
| 120 | /** | 131 | /** |
| 121 | * Vector of memory pointers backing each page. An entry can only be non-null if the | 132 | * Vector of memory pointers backing each page. An entry can only be non-null if the |
| 122 | * corresponding attribute element is of type `Memory`. | 133 | * corresponding attribute element is of type `Memory`. |
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 98b43e49c..a10131eb2 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -203,10 +203,12 @@ const char* TranslateCategory(Category category) { | |||
| 203 | case Category::Ui: | 203 | case Category::Ui: |
| 204 | case Category::UiGeneral: | 204 | case Category::UiGeneral: |
| 205 | return "UI"; | 205 | return "UI"; |
| 206 | case Category::UiAudio: | ||
| 207 | return "UiAudio"; | ||
| 206 | case Category::UiLayout: | 208 | case Category::UiLayout: |
| 207 | return "UiLayout"; | 209 | return "UILayout"; |
| 208 | case Category::UiGameList: | 210 | case Category::UiGameList: |
| 209 | return "UiGameList"; | 211 | return "UIGameList"; |
| 210 | case Category::Screenshots: | 212 | case Category::Screenshots: |
| 211 | return "Screenshots"; | 213 | return "Screenshots"; |
| 212 | case Category::Shortcuts: | 214 | case Category::Shortcuts: |
diff --git a/src/common/settings.h b/src/common/settings.h index 9317075f7..b929fd957 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -153,7 +153,7 @@ struct Values { | |||
| 153 | true, | 153 | true, |
| 154 | true}; | 154 | true}; |
| 155 | Setting<bool, false> audio_muted{ | 155 | Setting<bool, false> audio_muted{ |
| 156 | linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true}; | 156 | linkage, false, "audio_muted", Category::Audio, Specialization::Default, true, true}; |
| 157 | Setting<bool, false> dump_audio_commands{ | 157 | Setting<bool, false> dump_audio_commands{ |
| 158 | linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false}; | 158 | linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false}; |
| 159 | 159 | ||
| @@ -232,7 +232,11 @@ struct Values { | |||
| 232 | SwitchableSetting<bool> use_asynchronous_gpu_emulation{ | 232 | SwitchableSetting<bool> use_asynchronous_gpu_emulation{ |
| 233 | linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; | 233 | linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; |
| 234 | SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage, | 234 | SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage, |
| 235 | #ifdef ANDROID | ||
| 236 | AstcDecodeMode::Cpu, | ||
| 237 | #else | ||
| 235 | AstcDecodeMode::Gpu, | 238 | AstcDecodeMode::Gpu, |
| 239 | #endif | ||
| 236 | AstcDecodeMode::Cpu, | 240 | AstcDecodeMode::Cpu, |
| 237 | AstcDecodeMode::CpuAsynchronous, | 241 | AstcDecodeMode::CpuAsynchronous, |
| 238 | "accelerate_astc", | 242 | "accelerate_astc", |
| @@ -304,7 +308,11 @@ struct Values { | |||
| 304 | linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true}; | 308 | linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true}; |
| 305 | 309 | ||
| 306 | SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage, | 310 | SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage, |
| 311 | #ifdef ANDROID | ||
| 312 | GpuAccuracy::Normal, | ||
| 313 | #else | ||
| 307 | GpuAccuracy::High, | 314 | GpuAccuracy::High, |
| 315 | #endif | ||
| 308 | GpuAccuracy::Normal, | 316 | GpuAccuracy::Normal, |
| 309 | GpuAccuracy::Extreme, | 317 | GpuAccuracy::Extreme, |
| 310 | "gpu_accuracy", | 318 | "gpu_accuracy", |
| @@ -313,20 +321,38 @@ struct Values { | |||
| 313 | true, | 321 | true, |
| 314 | true}; | 322 | true}; |
| 315 | GpuAccuracy current_gpu_accuracy{GpuAccuracy::High}; | 323 | GpuAccuracy current_gpu_accuracy{GpuAccuracy::High}; |
| 316 | SwitchableSetting<AnisotropyMode, true> max_anisotropy{ | 324 | SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage, |
| 317 | linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16, | 325 | #ifdef ANDROID |
| 318 | "max_anisotropy", Category::RendererAdvanced}; | 326 | AnisotropyMode::Default, |
| 327 | #else | ||
| 328 | AnisotropyMode::Automatic, | ||
| 329 | #endif | ||
| 330 | AnisotropyMode::Automatic, | ||
| 331 | AnisotropyMode::X16, | ||
| 332 | "max_anisotropy", | ||
| 333 | Category::RendererAdvanced}; | ||
| 319 | SwitchableSetting<AstcRecompression, true> astc_recompression{linkage, | 334 | SwitchableSetting<AstcRecompression, true> astc_recompression{linkage, |
| 320 | AstcRecompression::Uncompressed, | 335 | AstcRecompression::Uncompressed, |
| 321 | AstcRecompression::Uncompressed, | 336 | AstcRecompression::Uncompressed, |
| 322 | AstcRecompression::Bc3, | 337 | AstcRecompression::Bc3, |
| 323 | "astc_recompression", | 338 | "astc_recompression", |
| 324 | Category::RendererAdvanced}; | 339 | Category::RendererAdvanced}; |
| 325 | SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation", | 340 | SwitchableSetting<bool> async_presentation{linkage, |
| 326 | Category::RendererAdvanced}; | 341 | #ifdef ANDROID |
| 342 | true, | ||
| 343 | #else | ||
| 344 | false, | ||
| 345 | #endif | ||
| 346 | "async_presentation", Category::RendererAdvanced}; | ||
| 327 | SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock", | 347 | SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock", |
| 328 | Category::RendererAdvanced}; | 348 | Category::RendererAdvanced}; |
| 329 | SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing", | 349 | SwitchableSetting<bool> use_reactive_flushing{linkage, |
| 350 | #ifdef ANDROID | ||
| 351 | false, | ||
| 352 | #else | ||
| 353 | true, | ||
| 354 | #endif | ||
| 355 | "use_reactive_flushing", | ||
| 330 | Category::RendererAdvanced}; | 356 | Category::RendererAdvanced}; |
| 331 | SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", | 357 | SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", |
| 332 | Category::RendererAdvanced}; | 358 | Category::RendererAdvanced}; |
| @@ -358,6 +384,8 @@ struct Values { | |||
| 358 | Category::RendererDebug}; | 384 | Category::RendererDebug}; |
| 359 | // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control | 385 | // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control |
| 360 | bool renderer_amdvlk_depth_bias_workaround{}; | 386 | bool renderer_amdvlk_depth_bias_workaround{}; |
| 387 | Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder", | ||
| 388 | Category::RendererDebug}; | ||
| 361 | 389 | ||
| 362 | // System | 390 | // System |
| 363 | SwitchableSetting<Language, true> language_index{linkage, | 391 | SwitchableSetting<Language, true> language_index{linkage, |
| @@ -390,7 +418,11 @@ struct Values { | |||
| 390 | Setting<s32> current_user{linkage, 0, "current_user", Category::System}; | 418 | Setting<s32> current_user{linkage, 0, "current_user", Category::System}; |
| 391 | 419 | ||
| 392 | SwitchableSetting<ConsoleMode> use_docked_mode{linkage, | 420 | SwitchableSetting<ConsoleMode> use_docked_mode{linkage, |
| 421 | #ifdef ANDROID | ||
| 422 | ConsoleMode::Handheld, | ||
| 423 | #else | ||
| 393 | ConsoleMode::Docked, | 424 | ConsoleMode::Docked, |
| 425 | #endif | ||
| 394 | "use_docked_mode", | 426 | "use_docked_mode", |
| 395 | Category::System, | 427 | Category::System, |
| 396 | Specialization::Radio, | 428 | Specialization::Radio, |
diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 1800ab10d..7943223eb 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h | |||
| @@ -32,6 +32,7 @@ enum class Category : u32 { | |||
| 32 | AddOns, | 32 | AddOns, |
| 33 | Controls, | 33 | Controls, |
| 34 | Ui, | 34 | Ui, |
| 35 | UiAudio, | ||
| 35 | UiGeneral, | 36 | UiGeneral, |
| 36 | UiLayout, | 37 | UiLayout, |
| 37 | UiGameList, | 38 | UiGameList, |
diff --git a/src/common/settings_input.cpp b/src/common/settings_input.cpp index 0a6eea3cf..a6007e7b2 100644 --- a/src/common/settings_input.cpp +++ b/src/common/settings_input.cpp | |||
| @@ -6,10 +6,11 @@ | |||
| 6 | namespace Settings { | 6 | namespace Settings { |
| 7 | namespace NativeButton { | 7 | namespace NativeButton { |
| 8 | const std::array<const char*, NumButtons> mapping = {{ | 8 | const std::array<const char*, NumButtons> mapping = {{ |
| 9 | "button_a", "button_b", "button_x", "button_y", "button_lstick", | 9 | "button_a", "button_b", "button_x", "button_y", "button_lstick", |
| 10 | "button_rstick", "button_l", "button_r", "button_zl", "button_zr", | 10 | "button_rstick", "button_l", "button_r", "button_zl", "button_zr", |
| 11 | "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright", | 11 | "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright", |
| 12 | "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot", | 12 | "button_ddown", "button_slleft", "button_srleft", "button_home", "button_screenshot", |
| 13 | "button_slright", "button_srright", | ||
| 13 | }}; | 14 | }}; |
| 14 | } | 15 | } |
| 15 | 16 | ||
diff --git a/src/common/settings_input.h b/src/common/settings_input.h index 46f38c703..53a95ef8f 100644 --- a/src/common/settings_input.h +++ b/src/common/settings_input.h | |||
| @@ -29,12 +29,15 @@ enum Values : int { | |||
| 29 | DRight, | 29 | DRight, |
| 30 | DDown, | 30 | DDown, |
| 31 | 31 | ||
| 32 | SL, | 32 | SLLeft, |
| 33 | SR, | 33 | SRLeft, |
| 34 | 34 | ||
| 35 | Home, | 35 | Home, |
| 36 | Screenshot, | 36 | Screenshot, |
| 37 | 37 | ||
| 38 | SLRight, | ||
| 39 | SRRight, | ||
| 40 | |||
| 38 | NumButtons, | 41 | NumButtons, |
| 39 | }; | 42 | }; |
| 40 | 43 | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e4f499135..66c10fc3f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -271,8 +271,9 @@ add_library(core STATIC | |||
| 271 | hle/kernel/k_page_heap.h | 271 | hle/kernel/k_page_heap.h |
| 272 | hle/kernel/k_page_group.cpp | 272 | hle/kernel/k_page_group.cpp |
| 273 | hle/kernel/k_page_group.h | 273 | hle/kernel/k_page_group.h |
| 274 | hle/kernel/k_page_table.cpp | ||
| 275 | hle/kernel/k_page_table.h | 274 | hle/kernel/k_page_table.h |
| 275 | hle/kernel/k_page_table_base.cpp | ||
| 276 | hle/kernel/k_page_table_base.h | ||
| 276 | hle/kernel/k_page_table_manager.h | 277 | hle/kernel/k_page_table_manager.h |
| 277 | hle/kernel/k_page_table_slab_heap.h | 278 | hle/kernel/k_page_table_slab_heap.h |
| 278 | hle/kernel/k_port.cpp | 279 | hle/kernel/k_port.cpp |
| @@ -280,6 +281,7 @@ add_library(core STATIC | |||
| 280 | hle/kernel/k_priority_queue.h | 281 | hle/kernel/k_priority_queue.h |
| 281 | hle/kernel/k_process.cpp | 282 | hle/kernel/k_process.cpp |
| 282 | hle/kernel/k_process.h | 283 | hle/kernel/k_process.h |
| 284 | hle/kernel/k_process_page_table.h | ||
| 283 | hle/kernel/k_readable_event.cpp | 285 | hle/kernel/k_readable_event.cpp |
| 284 | hle/kernel/k_readable_event.h | 286 | hle/kernel/k_readable_event.h |
| 285 | hle/kernel/k_resource_limit.cpp | 287 | hle/kernel/k_resource_limit.cpp |
| @@ -330,8 +332,6 @@ add_library(core STATIC | |||
| 330 | hle/kernel/physical_core.cpp | 332 | hle/kernel/physical_core.cpp |
| 331 | hle/kernel/physical_core.h | 333 | hle/kernel/physical_core.h |
| 332 | hle/kernel/physical_memory.h | 334 | hle/kernel/physical_memory.h |
| 333 | hle/kernel/process_capability.cpp | ||
| 334 | hle/kernel/process_capability.h | ||
| 335 | hle/kernel/slab_helpers.h | 335 | hle/kernel/slab_helpers.h |
| 336 | hle/kernel/svc.cpp | 336 | hle/kernel/svc.cpp |
| 337 | hle/kernel/svc.h | 337 | hle/kernel/svc.h |
| @@ -521,17 +521,28 @@ add_library(core STATIC | |||
| 521 | hle/service/grc/grc.h | 521 | hle/service/grc/grc.h |
| 522 | hle/service/hid/hid.cpp | 522 | hle/service/hid/hid.cpp |
| 523 | hle/service/hid/hid.h | 523 | hle/service/hid/hid.h |
| 524 | hle/service/hid/hid_debug_server.cpp | ||
| 525 | hle/service/hid/hid_debug_server.h | ||
| 526 | hle/service/hid/hid_firmware_settings.cpp | ||
| 527 | hle/service/hid/hid_firmware_settings.h | ||
| 528 | hle/service/hid/hid_server.cpp | ||
| 529 | hle/service/hid/hid_server.h | ||
| 530 | hle/service/hid/hid_system_server.cpp | ||
| 531 | hle/service/hid/hid_system_server.h | ||
| 532 | hle/service/hid/hid_util.h | ||
| 524 | hle/service/hid/hidbus.cpp | 533 | hle/service/hid/hidbus.cpp |
| 525 | hle/service/hid/hidbus.h | 534 | hle/service/hid/hidbus.h |
| 526 | hle/service/hid/irs.cpp | 535 | hle/service/hid/irs.cpp |
| 527 | hle/service/hid/irs.h | 536 | hle/service/hid/irs.h |
| 528 | hle/service/hid/irs_ring_lifo.h | 537 | hle/service/hid/irs_ring_lifo.h |
| 538 | hle/service/hid/resource_manager.cpp | ||
| 539 | hle/service/hid/resource_manager.h | ||
| 529 | hle/service/hid/ring_lifo.h | 540 | hle/service/hid/ring_lifo.h |
| 530 | hle/service/hid/xcd.cpp | 541 | hle/service/hid/xcd.cpp |
| 531 | hle/service/hid/xcd.h | 542 | hle/service/hid/xcd.h |
| 532 | hle/service/hid/errors.h | 543 | hle/service/hid/errors.h |
| 533 | hle/service/hid/controllers/console_sixaxis.cpp | 544 | hle/service/hid/controllers/console_six_axis.cpp |
| 534 | hle/service/hid/controllers/console_sixaxis.h | 545 | hle/service/hid/controllers/console_six_axis.h |
| 535 | hle/service/hid/controllers/controller_base.cpp | 546 | hle/service/hid/controllers/controller_base.cpp |
| 536 | hle/service/hid/controllers/controller_base.h | 547 | hle/service/hid/controllers/controller_base.h |
| 537 | hle/service/hid/controllers/debug_pad.cpp | 548 | hle/service/hid/controllers/debug_pad.cpp |
| @@ -546,6 +557,10 @@ add_library(core STATIC | |||
| 546 | hle/service/hid/controllers/npad.h | 557 | hle/service/hid/controllers/npad.h |
| 547 | hle/service/hid/controllers/palma.cpp | 558 | hle/service/hid/controllers/palma.cpp |
| 548 | hle/service/hid/controllers/palma.h | 559 | hle/service/hid/controllers/palma.h |
| 560 | hle/service/hid/controllers/seven_six_axis.cpp | ||
| 561 | hle/service/hid/controllers/seven_six_axis.h | ||
| 562 | hle/service/hid/controllers/six_axis.cpp | ||
| 563 | hle/service/hid/controllers/six_axis.h | ||
| 549 | hle/service/hid/controllers/stubbed.cpp | 564 | hle/service/hid/controllers/stubbed.cpp |
| 550 | hle/service/hid/controllers/stubbed.h | 565 | hle/service/hid/controllers/stubbed.h |
| 551 | hle/service/hid/controllers/touchscreen.cpp | 566 | hle/service/hid/controllers/touchscreen.cpp |
| @@ -715,6 +730,7 @@ add_library(core STATIC | |||
| 715 | hle/service/nvnflinger/producer_listener.h | 730 | hle/service/nvnflinger/producer_listener.h |
| 716 | hle/service/nvnflinger/status.h | 731 | hle/service/nvnflinger/status.h |
| 717 | hle/service/nvnflinger/ui/fence.h | 732 | hle/service/nvnflinger/ui/fence.h |
| 733 | hle/service/nvnflinger/ui/graphic_buffer.cpp | ||
| 718 | hle/service/nvnflinger/ui/graphic_buffer.h | 734 | hle/service/nvnflinger/ui/graphic_buffer.h |
| 719 | hle/service/nvnflinger/window.h | 735 | hle/service/nvnflinger/window.h |
| 720 | hle/service/olsc/olsc.cpp | 736 | hle/service/olsc/olsc.cpp |
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 5e27dde58..558fba5bd 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp | |||
| @@ -153,6 +153,14 @@ void ARM_Interface::Run() { | |||
| 153 | Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())}; | 153 | Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())}; |
| 154 | HaltReason hr{}; | 154 | HaltReason hr{}; |
| 155 | 155 | ||
| 156 | // If the thread is scheduled for termination, exit the thread. | ||
| 157 | if (current_thread->HasDpc()) { | ||
| 158 | if (current_thread->IsTerminationRequested()) { | ||
| 159 | current_thread->Exit(); | ||
| 160 | UNREACHABLE(); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 156 | // Notify the debugger and go to sleep if a step was performed | 164 | // Notify the debugger and go to sleep if a step was performed |
| 157 | // and this thread has been scheduled again. | 165 | // and this thread has been scheduled again. |
| 158 | if (current_thread->GetStepState() == StepState::StepPerformed) { | 166 | if (current_thread->GetStepState() == StepState::StepPerformed) { |
| @@ -174,14 +182,6 @@ void ARM_Interface::Run() { | |||
| 174 | } | 182 | } |
| 175 | system.ExitCPUProfile(); | 183 | system.ExitCPUProfile(); |
| 176 | 184 | ||
| 177 | // If the thread is scheduled for termination, exit the thread. | ||
| 178 | if (current_thread->HasDpc()) { | ||
| 179 | if (current_thread->IsTerminationRequested()) { | ||
| 180 | current_thread->Exit(); | ||
| 181 | UNREACHABLE(); | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | // Notify the debugger and go to sleep if a breakpoint was hit, | 185 | // Notify the debugger and go to sleep if a breakpoint was hit, |
| 186 | // or if the thread is unable to continue for any reason. | 186 | // or if the thread is unable to continue for any reason. |
| 187 | if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) { | 187 | if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) { |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index e671b270f..d6b5abc68 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -76,6 +76,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { | |||
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | void CoreTiming::ClearPendingEvents() { | 78 | void CoreTiming::ClearPendingEvents() { |
| 79 | std::scoped_lock lock{basic_lock}; | ||
| 79 | event_queue.clear(); | 80 | event_queue.clear(); |
| 80 | } | 81 | } |
| 81 | 82 | ||
| @@ -113,6 +114,7 @@ bool CoreTiming::IsRunning() const { | |||
| 113 | } | 114 | } |
| 114 | 115 | ||
| 115 | bool CoreTiming::HasPendingEvents() const { | 116 | bool CoreTiming::HasPendingEvents() const { |
| 117 | std::scoped_lock lock{basic_lock}; | ||
| 116 | return !(wait_set && event_queue.empty()); | 118 | return !(wait_set && event_queue.empty()); |
| 117 | } | 119 | } |
| 118 | 120 | ||
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 26a8b93a7..21548f0a9 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -161,7 +161,7 @@ private: | |||
| 161 | std::shared_ptr<EventType> ev_lost; | 161 | std::shared_ptr<EventType> ev_lost; |
| 162 | Common::Event event{}; | 162 | Common::Event event{}; |
| 163 | Common::Event pause_event{}; | 163 | Common::Event pause_event{}; |
| 164 | std::mutex basic_lock; | 164 | mutable std::mutex basic_lock; |
| 165 | std::mutex advance_lock; | 165 | std::mutex advance_lock; |
| 166 | std::unique_ptr<std::jthread> timer_thread; | 166 | std::unique_ptr<std::jthread> timer_thread; |
| 167 | std::atomic<bool> paused{}; | 167 | std::atomic<bool> paused{}; |
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 6f5f5156b..148dd3e39 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp | |||
| @@ -562,6 +562,120 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ | |||
| 562 | } | 562 | } |
| 563 | } | 563 | } |
| 564 | 564 | ||
| 565 | static VAddr GetModuleEnd(Kernel::KProcessPageTable& page_table, VAddr base) { | ||
| 566 | Kernel::KMemoryInfo mem_info; | ||
| 567 | Kernel::Svc::MemoryInfo svc_mem_info; | ||
| 568 | Kernel::Svc::PageInfo page_info; | ||
| 569 | VAddr cur_addr{base}; | ||
| 570 | |||
| 571 | // Expect: r-x Code (.text) | ||
| 572 | R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); | ||
| 573 | svc_mem_info = mem_info.GetSvcMemoryInfo(); | ||
| 574 | cur_addr = svc_mem_info.base_address + svc_mem_info.size; | ||
| 575 | if (svc_mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 576 | svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { | ||
| 577 | return cur_addr - 1; | ||
| 578 | } | ||
| 579 | |||
| 580 | // Expect: r-- Code (.rodata) | ||
| 581 | R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); | ||
| 582 | svc_mem_info = mem_info.GetSvcMemoryInfo(); | ||
| 583 | cur_addr = svc_mem_info.base_address + svc_mem_info.size; | ||
| 584 | if (svc_mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 585 | svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) { | ||
| 586 | return cur_addr - 1; | ||
| 587 | } | ||
| 588 | |||
| 589 | // Expect: rw- CodeData (.data) | ||
| 590 | R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); | ||
| 591 | svc_mem_info = mem_info.GetSvcMemoryInfo(); | ||
| 592 | cur_addr = svc_mem_info.base_address + svc_mem_info.size; | ||
| 593 | return cur_addr - 1; | ||
| 594 | } | ||
| 595 | |||
| 596 | static Loader::AppLoader::Modules FindModules(Core::System& system) { | ||
| 597 | Loader::AppLoader::Modules modules; | ||
| 598 | |||
| 599 | auto& page_table = system.ApplicationProcess()->GetPageTable(); | ||
| 600 | auto& memory = system.ApplicationMemory(); | ||
| 601 | VAddr cur_addr = 0; | ||
| 602 | |||
| 603 | // Look for executable sections in Code or AliasCode regions. | ||
| 604 | while (true) { | ||
| 605 | Kernel::KMemoryInfo mem_info{}; | ||
| 606 | Kernel::Svc::PageInfo page_info{}; | ||
| 607 | R_ASSERT( | ||
| 608 | page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); | ||
| 609 | auto svc_mem_info = mem_info.GetSvcMemoryInfo(); | ||
| 610 | |||
| 611 | if (svc_mem_info.permission == Kernel::Svc::MemoryPermission::ReadExecute && | ||
| 612 | (svc_mem_info.state == Kernel::Svc::MemoryState::Code || | ||
| 613 | svc_mem_info.state == Kernel::Svc::MemoryState::AliasCode)) { | ||
| 614 | // Try to read the module name from its path. | ||
| 615 | constexpr s32 PathLengthMax = 0x200; | ||
| 616 | struct { | ||
| 617 | u32 zero; | ||
| 618 | s32 path_length; | ||
| 619 | std::array<char, PathLengthMax> path; | ||
| 620 | } module_path; | ||
| 621 | |||
| 622 | if (memory.ReadBlock(svc_mem_info.base_address + svc_mem_info.size, &module_path, | ||
| 623 | sizeof(module_path))) { | ||
| 624 | if (module_path.zero == 0 && module_path.path_length > 0) { | ||
| 625 | // Truncate module name. | ||
| 626 | module_path.path[PathLengthMax - 1] = '\0'; | ||
| 627 | |||
| 628 | // Ignore leading directories. | ||
| 629 | char* path_pointer = module_path.path.data(); | ||
| 630 | |||
| 631 | for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) && | ||
| 632 | module_path.path[i] != '\0'; | ||
| 633 | i++) { | ||
| 634 | if (module_path.path[i] == '/' || module_path.path[i] == '\\') { | ||
| 635 | path_pointer = module_path.path.data() + i + 1; | ||
| 636 | } | ||
| 637 | } | ||
| 638 | |||
| 639 | // Insert output. | ||
| 640 | modules.emplace(svc_mem_info.base_address, path_pointer); | ||
| 641 | } | ||
| 642 | } | ||
| 643 | } | ||
| 644 | |||
| 645 | // Check if we're done. | ||
| 646 | const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size; | ||
| 647 | if (next_address <= cur_addr) { | ||
| 648 | break; | ||
| 649 | } | ||
| 650 | |||
| 651 | cur_addr = next_address; | ||
| 652 | } | ||
| 653 | |||
| 654 | return modules; | ||
| 655 | } | ||
| 656 | |||
| 657 | static VAddr FindMainModuleEntrypoint(Core::System& system) { | ||
| 658 | Loader::AppLoader::Modules modules; | ||
| 659 | system.GetAppLoader().ReadNSOModules(modules); | ||
| 660 | |||
| 661 | // Do we have a module named main? | ||
| 662 | const auto main = std::find_if(modules.begin(), modules.end(), | ||
| 663 | [](const auto& key) { return key.second == "main"; }); | ||
| 664 | |||
| 665 | if (main != modules.end()) { | ||
| 666 | return main->first; | ||
| 667 | } | ||
| 668 | |||
| 669 | // Do we have any loaded executable sections? | ||
| 670 | modules = FindModules(system); | ||
| 671 | if (!modules.empty()) { | ||
| 672 | return modules.begin()->first; | ||
| 673 | } | ||
| 674 | |||
| 675 | // As a last resort, use the start of the code region. | ||
| 676 | return GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart()); | ||
| 677 | } | ||
| 678 | |||
| 565 | void GDBStub::HandleQuery(std::string_view command) { | 679 | void GDBStub::HandleQuery(std::string_view command) { |
| 566 | if (command.starts_with("TStatus")) { | 680 | if (command.starts_with("TStatus")) { |
| 567 | // no tracepoint support | 681 | // no tracepoint support |
| @@ -573,21 +687,10 @@ void GDBStub::HandleQuery(std::string_view command) { | |||
| 573 | const auto target_xml{arch->GetTargetXML()}; | 687 | const auto target_xml{arch->GetTargetXML()}; |
| 574 | SendReply(PaginateBuffer(target_xml, command.substr(30))); | 688 | SendReply(PaginateBuffer(target_xml, command.substr(30))); |
| 575 | } else if (command.starts_with("Offsets")) { | 689 | } else if (command.starts_with("Offsets")) { |
| 576 | Loader::AppLoader::Modules modules; | 690 | const auto main_offset = FindMainModuleEntrypoint(system); |
| 577 | system.GetAppLoader().ReadNSOModules(modules); | 691 | SendReply(fmt::format("TextSeg={:x}", main_offset)); |
| 578 | |||
| 579 | const auto main = std::find_if(modules.begin(), modules.end(), | ||
| 580 | [](const auto& key) { return key.second == "main"; }); | ||
| 581 | if (main != modules.end()) { | ||
| 582 | SendReply(fmt::format("TextSeg={:x}", main->first)); | ||
| 583 | } else { | ||
| 584 | SendReply(fmt::format( | ||
| 585 | "TextSeg={:x}", | ||
| 586 | GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart()))); | ||
| 587 | } | ||
| 588 | } else if (command.starts_with("Xfer:libraries:read::")) { | 692 | } else if (command.starts_with("Xfer:libraries:read::")) { |
| 589 | Loader::AppLoader::Modules modules; | 693 | auto modules = FindModules(system); |
| 590 | system.GetAppLoader().ReadNSOModules(modules); | ||
| 591 | 694 | ||
| 592 | std::string buffer; | 695 | std::string buffer; |
| 593 | buffer += R"(<?xml version="1.0"?>)"; | 696 | buffer += R"(<?xml version="1.0"?>)"; |
| @@ -727,32 +830,6 @@ static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::Memory | |||
| 727 | } | 830 | } |
| 728 | } | 831 | } |
| 729 | 832 | ||
| 730 | static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) { | ||
| 731 | Kernel::Svc::MemoryInfo mem_info; | ||
| 732 | VAddr cur_addr{base}; | ||
| 733 | |||
| 734 | // Expect: r-x Code (.text) | ||
| 735 | mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | ||
| 736 | cur_addr = mem_info.base_address + mem_info.size; | ||
| 737 | if (mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 738 | mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { | ||
| 739 | return cur_addr - 1; | ||
| 740 | } | ||
| 741 | |||
| 742 | // Expect: r-- Code (.rodata) | ||
| 743 | mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | ||
| 744 | cur_addr = mem_info.base_address + mem_info.size; | ||
| 745 | if (mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 746 | mem_info.permission != Kernel::Svc::MemoryPermission::Read) { | ||
| 747 | return cur_addr - 1; | ||
| 748 | } | ||
| 749 | |||
| 750 | // Expect: rw- CodeData (.data) | ||
| 751 | mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | ||
| 752 | cur_addr = mem_info.base_address + mem_info.size; | ||
| 753 | return cur_addr - 1; | ||
| 754 | } | ||
| 755 | |||
| 756 | void GDBStub::HandleRcmd(const std::vector<u8>& command) { | 833 | void GDBStub::HandleRcmd(const std::vector<u8>& command) { |
| 757 | std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; | 834 | std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; |
| 758 | std::string reply; | 835 | std::string reply; |
| @@ -767,7 +844,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { | |||
| 767 | 844 | ||
| 768 | if (command_str == "get fastmem") { | 845 | if (command_str == "get fastmem") { |
| 769 | if (Settings::IsFastmemEnabled()) { | 846 | if (Settings::IsFastmemEnabled()) { |
| 770 | const auto& impl = page_table.PageTableImpl(); | 847 | const auto& impl = page_table.GetImpl(); |
| 771 | const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena); | 848 | const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena); |
| 772 | const auto region_bits = impl.current_address_space_width_in_bits; | 849 | const auto region_bits = impl.current_address_space_width_in_bits; |
| 773 | const auto region_size = 1ULL << region_bits; | 850 | const auto region_size = 1ULL << region_bits; |
| @@ -779,26 +856,27 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { | |||
| 779 | reply = "Fastmem is not enabled.\n"; | 856 | reply = "Fastmem is not enabled.\n"; |
| 780 | } | 857 | } |
| 781 | } else if (command_str == "get info") { | 858 | } else if (command_str == "get info") { |
| 782 | Loader::AppLoader::Modules modules; | 859 | auto modules = FindModules(system); |
| 783 | system.GetAppLoader().ReadNSOModules(modules); | ||
| 784 | 860 | ||
| 785 | reply = fmt::format("Process: {:#x} ({})\n" | 861 | reply = fmt::format("Process: {:#x} ({})\n" |
| 786 | "Program Id: {:#018x}\n", | 862 | "Program Id: {:#018x}\n", |
| 787 | process->GetProcessId(), process->GetName(), process->GetProgramId()); | 863 | process->GetProcessId(), process->GetName(), process->GetProgramId()); |
| 788 | reply += fmt::format("Layout:\n" | 864 | reply += fmt::format( |
| 789 | " Alias: {:#012x} - {:#012x}\n" | 865 | "Layout:\n" |
| 790 | " Heap: {:#012x} - {:#012x}\n" | 866 | " Alias: {:#012x} - {:#012x}\n" |
| 791 | " Aslr: {:#012x} - {:#012x}\n" | 867 | " Heap: {:#012x} - {:#012x}\n" |
| 792 | " Stack: {:#012x} - {:#012x}\n" | 868 | " Aslr: {:#012x} - {:#012x}\n" |
| 793 | "Modules:\n", | 869 | " Stack: {:#012x} - {:#012x}\n" |
| 794 | GetInteger(page_table.GetAliasRegionStart()), | 870 | "Modules:\n", |
| 795 | GetInteger(page_table.GetAliasRegionEnd()), | 871 | GetInteger(page_table.GetAliasRegionStart()), |
| 796 | GetInteger(page_table.GetHeapRegionStart()), | 872 | GetInteger(page_table.GetAliasRegionStart()) + page_table.GetAliasRegionSize() - 1, |
| 797 | GetInteger(page_table.GetHeapRegionEnd()), | 873 | GetInteger(page_table.GetHeapRegionStart()), |
| 798 | GetInteger(page_table.GetAliasCodeRegionStart()), | 874 | GetInteger(page_table.GetHeapRegionStart()) + page_table.GetHeapRegionSize() - 1, |
| 799 | GetInteger(page_table.GetAliasCodeRegionEnd()), | 875 | GetInteger(page_table.GetAliasCodeRegionStart()), |
| 800 | GetInteger(page_table.GetStackRegionStart()), | 876 | GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize() - |
| 801 | GetInteger(page_table.GetStackRegionEnd())); | 877 | 1, |
| 878 | GetInteger(page_table.GetStackRegionStart()), | ||
| 879 | GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1); | ||
| 802 | 880 | ||
| 803 | for (const auto& [vaddr, name] : modules) { | 881 | for (const auto& [vaddr, name] : modules) { |
| 804 | reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, | 882 | reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, |
| @@ -811,27 +889,34 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { | |||
| 811 | while (true) { | 889 | while (true) { |
| 812 | using MemoryAttribute = Kernel::Svc::MemoryAttribute; | 890 | using MemoryAttribute = Kernel::Svc::MemoryAttribute; |
| 813 | 891 | ||
| 814 | auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | 892 | Kernel::KMemoryInfo mem_info{}; |
| 815 | 893 | Kernel::Svc::PageInfo page_info{}; | |
| 816 | if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible || | 894 | R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), |
| 817 | mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) { | 895 | cur_addr)); |
| 818 | const char* state = GetMemoryStateName(mem_info.state); | 896 | auto svc_mem_info = mem_info.GetSvcMemoryInfo(); |
| 819 | const char* perm = GetMemoryPermissionString(mem_info); | 897 | |
| 820 | 898 | if (svc_mem_info.state != Kernel::Svc::MemoryState::Inaccessible || | |
| 821 | const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; | 899 | svc_mem_info.base_address + svc_mem_info.size - 1 != |
| 822 | const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; | 900 | std::numeric_limits<u64>::max()) { |
| 823 | const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; | 901 | const char* state = GetMemoryStateName(svc_mem_info.state); |
| 824 | const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; | 902 | const char* perm = GetMemoryPermissionString(svc_mem_info); |
| 903 | |||
| 904 | const char l = True(svc_mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; | ||
| 905 | const char i = | ||
| 906 | True(svc_mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; | ||
| 907 | const char d = | ||
| 908 | True(svc_mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; | ||
| 909 | const char u = True(svc_mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; | ||
| 825 | const char p = | 910 | const char p = |
| 826 | True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-'; | 911 | True(svc_mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-'; |
| 827 | 912 | ||
| 828 | reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", | 913 | reply += fmt::format( |
| 829 | mem_info.base_address, | 914 | " {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", svc_mem_info.base_address, |
| 830 | mem_info.base_address + mem_info.size - 1, perm, state, l, i, | 915 | svc_mem_info.base_address + svc_mem_info.size - 1, perm, state, l, i, d, u, p, |
| 831 | d, u, p, mem_info.ipc_count, mem_info.device_count); | 916 | svc_mem_info.ipc_count, svc_mem_info.device_count); |
| 832 | } | 917 | } |
| 833 | 918 | ||
| 834 | const uintptr_t next_address = mem_info.base_address + mem_info.size; | 919 | const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size; |
| 835 | if (next_address <= cur_addr) { | 920 | if (next_address <= cur_addr) { |
| 836 | break; | 921 | break; |
| 837 | } | 922 | } |
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 1c580de57..1eb1f439a 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp | |||
| @@ -35,13 +35,14 @@ struct RomFSHeader { | |||
| 35 | static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); | 35 | static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); |
| 36 | 36 | ||
| 37 | struct DirectoryEntry { | 37 | struct DirectoryEntry { |
| 38 | u32_le parent; | ||
| 38 | u32_le sibling; | 39 | u32_le sibling; |
| 39 | u32_le child_dir; | 40 | u32_le child_dir; |
| 40 | u32_le child_file; | 41 | u32_le child_file; |
| 41 | u32_le hash; | 42 | u32_le hash; |
| 42 | u32_le name_length; | 43 | u32_le name_length; |
| 43 | }; | 44 | }; |
| 44 | static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size."); | 45 | static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size."); |
| 45 | 46 | ||
| 46 | struct FileEntry { | 47 | struct FileEntry { |
| 47 | u32_le parent; | 48 | u32_le parent; |
| @@ -64,25 +65,22 @@ std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offs | |||
| 64 | return {entry, string}; | 65 | return {entry, string}; |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset, | 68 | void ProcessFile(const VirtualFile& file, std::size_t file_offset, std::size_t data_offset, |
| 68 | u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) { | 69 | u32 this_file_offset, std::shared_ptr<VectorVfsDirectory>& parent) { |
| 69 | while (true) { | 70 | while (this_file_offset != ROMFS_ENTRY_EMPTY) { |
| 70 | auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); | 71 | auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); |
| 71 | 72 | ||
| 72 | parent->AddFile(std::make_shared<OffsetVfsFile>( | 73 | parent->AddFile(std::make_shared<OffsetVfsFile>( |
| 73 | file, entry.first.size, entry.first.offset + data_offset, entry.second)); | 74 | file, entry.first.size, entry.first.offset + data_offset, entry.second)); |
| 74 | 75 | ||
| 75 | if (entry.first.sibling == ROMFS_ENTRY_EMPTY) | ||
| 76 | break; | ||
| 77 | |||
| 78 | this_file_offset = entry.first.sibling; | 76 | this_file_offset = entry.first.sibling; |
| 79 | } | 77 | } |
| 80 | } | 78 | } |
| 81 | 79 | ||
| 82 | void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset, | 80 | void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size_t file_offset, |
| 83 | std::size_t data_offset, u32 this_dir_offset, | 81 | std::size_t data_offset, u32 this_dir_offset, |
| 84 | std::shared_ptr<VectorVfsDirectory> parent) { | 82 | std::shared_ptr<VectorVfsDirectory>& parent) { |
| 85 | while (true) { | 83 | while (this_dir_offset != ROMFS_ENTRY_EMPTY) { |
| 86 | auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); | 84 | auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); |
| 87 | auto current = std::make_shared<VectorVfsDirectory>( | 85 | auto current = std::make_shared<VectorVfsDirectory>( |
| 88 | std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second); | 86 | std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second); |
| @@ -97,14 +95,12 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file | |||
| 97 | } | 95 | } |
| 98 | 96 | ||
| 99 | parent->AddDirectory(current); | 97 | parent->AddDirectory(current); |
| 100 | if (entry.first.sibling == ROMFS_ENTRY_EMPTY) | ||
| 101 | break; | ||
| 102 | this_dir_offset = entry.first.sibling; | 98 | this_dir_offset = entry.first.sibling; |
| 103 | } | 99 | } |
| 104 | } | 100 | } |
| 105 | } // Anonymous namespace | 101 | } // Anonymous namespace |
| 106 | 102 | ||
| 107 | VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { | 103 | VirtualDir ExtractRomFS(VirtualFile file) { |
| 108 | RomFSHeader header{}; | 104 | RomFSHeader header{}; |
| 109 | if (file->ReadObject(&header) != sizeof(RomFSHeader)) | 105 | if (file->ReadObject(&header) != sizeof(RomFSHeader)) |
| 110 | return nullptr; | 106 | return nullptr; |
| @@ -113,27 +109,17 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { | |||
| 113 | return nullptr; | 109 | return nullptr; |
| 114 | 110 | ||
| 115 | const u64 file_offset = header.file_meta.offset; | 111 | const u64 file_offset = header.file_meta.offset; |
| 116 | const u64 dir_offset = header.directory_meta.offset + 4; | 112 | const u64 dir_offset = header.directory_meta.offset; |
| 117 | |||
| 118 | auto root = | ||
| 119 | std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, | ||
| 120 | file->GetName(), file->GetContainingDirectory()); | ||
| 121 | |||
| 122 | ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root); | ||
| 123 | 113 | ||
| 124 | VirtualDir out = std::move(root); | 114 | auto root_container = std::make_shared<VectorVfsDirectory>(); |
| 125 | 115 | ||
| 126 | if (type == RomFSExtractionType::SingleDiscard) | 116 | ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container); |
| 127 | return out->GetSubdirectories().front(); | ||
| 128 | 117 | ||
| 129 | while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) { | 118 | if (auto root = root_container->GetSubdirectory(""); root) { |
| 130 | if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" && | 119 | return std::make_shared<CachedVfsDirectory>(std::move(root)); |
| 131 | type == RomFSExtractionType::Truncated) | ||
| 132 | break; | ||
| 133 | out = out->GetSubdirectories().front(); | ||
| 134 | } | 120 | } |
| 135 | 121 | ||
| 136 | return std::make_shared<CachedVfsDirectory>(std::move(out)); | 122 | return nullptr; |
| 137 | } | 123 | } |
| 138 | 124 | ||
| 139 | VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { | 125 | VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { |
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index 5d7f0c2a8..b75ff1aad 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h | |||
| @@ -7,16 +7,9 @@ | |||
| 7 | 7 | ||
| 8 | namespace FileSys { | 8 | namespace FileSys { |
| 9 | 9 | ||
| 10 | enum class RomFSExtractionType { | ||
| 11 | Full, // Includes data directory | ||
| 12 | Truncated, // Traverses into data directory | ||
| 13 | SingleDiscard, // Traverses into the first subdirectory of root | ||
| 14 | }; | ||
| 15 | |||
| 16 | // Converts a RomFS binary blob to VFS Filesystem | 10 | // Converts a RomFS binary blob to VFS Filesystem |
| 17 | // Returns nullptr on failure | 11 | // Returns nullptr on failure |
| 18 | VirtualDir ExtractRomFS(VirtualFile file, | 12 | VirtualDir ExtractRomFS(VirtualFile file); |
| 19 | RomFSExtractionType type = RomFSExtractionType::Truncated); | ||
| 20 | 13 | ||
| 21 | // Converts a VFS filesystem into a RomFS binary | 14 | // Converts a VFS filesystem into a RomFS binary |
| 22 | // Returns nullptr on failure | 15 | // Returns nullptr on failure |
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h index 79114bb6d..fae15a556 100644 --- a/src/core/hid/emulated_console.h +++ b/src/core/hid/emulated_console.h | |||
| @@ -38,14 +38,6 @@ using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>; | |||
| 38 | using ConsoleMotionValues = ConsoleMotionInfo; | 38 | using ConsoleMotionValues = ConsoleMotionInfo; |
| 39 | using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>; | 39 | using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>; |
| 40 | 40 | ||
| 41 | struct TouchFinger { | ||
| 42 | u64 last_touch{}; | ||
| 43 | Common::Point<float> position{}; | ||
| 44 | u32 id{}; | ||
| 45 | TouchAttribute attribute{}; | ||
| 46 | bool pressed{}; | ||
| 47 | }; | ||
| 48 | |||
| 49 | // Contains all motion related data that is used on the services | 41 | // Contains all motion related data that is used on the services |
| 50 | struct ConsoleMotion { | 42 | struct ConsoleMotion { |
| 51 | Common::Vec3f accel{}; | 43 | Common::Vec3f accel{}; |
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 2af3f06fc..a6e681e15 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "common/thread.h" | 8 | #include "common/thread.h" |
| 9 | #include "core/hid/emulated_controller.h" | 9 | #include "core/hid/emulated_controller.h" |
| 10 | #include "core/hid/input_converter.h" | 10 | #include "core/hid/input_converter.h" |
| 11 | #include "core/hle/service/hid/hid_util.h" | ||
| 11 | 12 | ||
| 12 | namespace Core::HID { | 13 | namespace Core::HID { |
| 13 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | 14 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; |
| @@ -82,7 +83,7 @@ Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleInde | |||
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | void EmulatedController::ReloadFromSettings() { | 85 | void EmulatedController::ReloadFromSettings() { |
| 85 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 86 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 86 | const auto& player = Settings::values.players.GetValue()[player_index]; | 87 | const auto& player = Settings::values.players.GetValue()[player_index]; |
| 87 | 88 | ||
| 88 | for (std::size_t index = 0; index < player.buttons.size(); ++index) { | 89 | for (std::size_t index = 0; index < player.buttons.size(); ++index) { |
| @@ -96,18 +97,7 @@ void EmulatedController::ReloadFromSettings() { | |||
| 96 | } | 97 | } |
| 97 | 98 | ||
| 98 | controller.color_values = {}; | 99 | controller.color_values = {}; |
| 99 | controller.colors_state.fullkey = { | 100 | ReloadColorsFromSettings(); |
| 100 | .body = GetNpadColor(player.body_color_left), | ||
| 101 | .button = GetNpadColor(player.button_color_left), | ||
| 102 | }; | ||
| 103 | controller.colors_state.left = { | ||
| 104 | .body = GetNpadColor(player.body_color_left), | ||
| 105 | .button = GetNpadColor(player.button_color_left), | ||
| 106 | }; | ||
| 107 | controller.colors_state.right = { | ||
| 108 | .body = GetNpadColor(player.body_color_right), | ||
| 109 | .button = GetNpadColor(player.button_color_right), | ||
| 110 | }; | ||
| 111 | 101 | ||
| 112 | ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs); | 102 | ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs); |
| 113 | 103 | ||
| @@ -128,6 +118,30 @@ void EmulatedController::ReloadFromSettings() { | |||
| 128 | ReloadInput(); | 118 | ReloadInput(); |
| 129 | } | 119 | } |
| 130 | 120 | ||
| 121 | void EmulatedController::ReloadColorsFromSettings() { | ||
| 122 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); | ||
| 123 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 124 | |||
| 125 | // Avoid updating colors if overridden by physical controller | ||
| 126 | if (controller.color_values[LeftIndex].body != 0 && | ||
| 127 | controller.color_values[RightIndex].body != 0) { | ||
| 128 | return; | ||
| 129 | } | ||
| 130 | |||
| 131 | controller.colors_state.fullkey = { | ||
| 132 | .body = GetNpadColor(player.body_color_left), | ||
| 133 | .button = GetNpadColor(player.button_color_left), | ||
| 134 | }; | ||
| 135 | controller.colors_state.left = { | ||
| 136 | .body = GetNpadColor(player.body_color_left), | ||
| 137 | .button = GetNpadColor(player.button_color_left), | ||
| 138 | }; | ||
| 139 | controller.colors_state.right = { | ||
| 140 | .body = GetNpadColor(player.body_color_right), | ||
| 141 | .button = GetNpadColor(player.button_color_right), | ||
| 142 | }; | ||
| 143 | } | ||
| 144 | |||
| 131 | void EmulatedController::LoadDevices() { | 145 | void EmulatedController::LoadDevices() { |
| 132 | // TODO(german77): Use more buttons to detect the correct device | 146 | // TODO(german77): Use more buttons to detect the correct device |
| 133 | const auto left_joycon = button_params[Settings::NativeButton::DRight]; | 147 | const auto left_joycon = button_params[Settings::NativeButton::DRight]; |
| @@ -202,7 +216,7 @@ void EmulatedController::LoadDevices() { | |||
| 202 | } | 216 | } |
| 203 | 217 | ||
| 204 | void EmulatedController::LoadTASParams() { | 218 | void EmulatedController::LoadTASParams() { |
| 205 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 219 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 206 | Common::ParamPackage common_params{}; | 220 | Common::ParamPackage common_params{}; |
| 207 | common_params.Set("engine", "tas"); | 221 | common_params.Set("engine", "tas"); |
| 208 | common_params.Set("port", static_cast<int>(player_index)); | 222 | common_params.Set("port", static_cast<int>(player_index)); |
| @@ -230,10 +244,12 @@ void EmulatedController::LoadTASParams() { | |||
| 230 | tas_button_params[Settings::NativeButton::DUp].Set("button", 13); | 244 | tas_button_params[Settings::NativeButton::DUp].Set("button", 13); |
| 231 | tas_button_params[Settings::NativeButton::DRight].Set("button", 14); | 245 | tas_button_params[Settings::NativeButton::DRight].Set("button", 14); |
| 232 | tas_button_params[Settings::NativeButton::DDown].Set("button", 15); | 246 | tas_button_params[Settings::NativeButton::DDown].Set("button", 15); |
| 233 | tas_button_params[Settings::NativeButton::SL].Set("button", 16); | 247 | tas_button_params[Settings::NativeButton::SLLeft].Set("button", 16); |
| 234 | tas_button_params[Settings::NativeButton::SR].Set("button", 17); | 248 | tas_button_params[Settings::NativeButton::SRLeft].Set("button", 17); |
| 235 | tas_button_params[Settings::NativeButton::Home].Set("button", 18); | 249 | tas_button_params[Settings::NativeButton::Home].Set("button", 18); |
| 236 | tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19); | 250 | tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19); |
| 251 | tas_button_params[Settings::NativeButton::SLRight].Set("button", 20); | ||
| 252 | tas_button_params[Settings::NativeButton::SRRight].Set("button", 21); | ||
| 237 | 253 | ||
| 238 | tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); | 254 | tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); |
| 239 | tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); | 255 | tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); |
| @@ -249,7 +265,7 @@ void EmulatedController::LoadTASParams() { | |||
| 249 | } | 265 | } |
| 250 | 266 | ||
| 251 | void EmulatedController::LoadVirtualGamepadParams() { | 267 | void EmulatedController::LoadVirtualGamepadParams() { |
| 252 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 268 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 253 | Common::ParamPackage common_params{}; | 269 | Common::ParamPackage common_params{}; |
| 254 | common_params.Set("engine", "virtual_gamepad"); | 270 | common_params.Set("engine", "virtual_gamepad"); |
| 255 | common_params.Set("port", static_cast<int>(player_index)); | 271 | common_params.Set("port", static_cast<int>(player_index)); |
| @@ -283,10 +299,12 @@ void EmulatedController::LoadVirtualGamepadParams() { | |||
| 283 | virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); | 299 | virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); |
| 284 | virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); | 300 | virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); |
| 285 | virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); | 301 | virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); |
| 286 | virtual_button_params[Settings::NativeButton::SL].Set("button", 16); | 302 | virtual_button_params[Settings::NativeButton::SLLeft].Set("button", 16); |
| 287 | virtual_button_params[Settings::NativeButton::SR].Set("button", 17); | 303 | virtual_button_params[Settings::NativeButton::SRLeft].Set("button", 17); |
| 288 | virtual_button_params[Settings::NativeButton::Home].Set("button", 18); | 304 | virtual_button_params[Settings::NativeButton::Home].Set("button", 18); |
| 289 | virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); | 305 | virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); |
| 306 | virtual_button_params[Settings::NativeButton::SLRight].Set("button", 20); | ||
| 307 | virtual_button_params[Settings::NativeButton::SRRight].Set("button", 21); | ||
| 290 | 308 | ||
| 291 | virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); | 309 | virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); |
| 292 | virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); | 310 | virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); |
| @@ -491,9 +509,11 @@ void EmulatedController::ReloadInput() { | |||
| 491 | }); | 509 | }); |
| 492 | } | 510 | } |
| 493 | turbo_button_state = 0; | 511 | turbo_button_state = 0; |
| 512 | is_initalized = true; | ||
| 494 | } | 513 | } |
| 495 | 514 | ||
| 496 | void EmulatedController::UnloadInput() { | 515 | void EmulatedController::UnloadInput() { |
| 516 | is_initalized = false; | ||
| 497 | for (auto& button : button_devices) { | 517 | for (auto& button : button_devices) { |
| 498 | button.reset(); | 518 | button.reset(); |
| 499 | } | 519 | } |
| @@ -598,7 +618,7 @@ bool EmulatedController::IsConfiguring() const { | |||
| 598 | } | 618 | } |
| 599 | 619 | ||
| 600 | void EmulatedController::SaveCurrentConfig() { | 620 | void EmulatedController::SaveCurrentConfig() { |
| 601 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 621 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 602 | auto& player = Settings::values.players.GetValue()[player_index]; | 622 | auto& player = Settings::values.players.GetValue()[player_index]; |
| 603 | player.connected = is_connected; | 623 | player.connected = is_connected; |
| 604 | player.controller_type = MapNPadToSettingsType(npad_type); | 624 | player.controller_type = MapNPadToSettingsType(npad_type); |
| @@ -854,12 +874,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback | |||
| 854 | controller.npad_button_state.down.Assign(current_status.value); | 874 | controller.npad_button_state.down.Assign(current_status.value); |
| 855 | controller.debug_pad_button_state.d_down.Assign(current_status.value); | 875 | controller.debug_pad_button_state.d_down.Assign(current_status.value); |
| 856 | break; | 876 | break; |
| 857 | case Settings::NativeButton::SL: | 877 | case Settings::NativeButton::SLLeft: |
| 858 | controller.npad_button_state.left_sl.Assign(current_status.value); | 878 | controller.npad_button_state.left_sl.Assign(current_status.value); |
| 879 | break; | ||
| 880 | case Settings::NativeButton::SLRight: | ||
| 859 | controller.npad_button_state.right_sl.Assign(current_status.value); | 881 | controller.npad_button_state.right_sl.Assign(current_status.value); |
| 860 | break; | 882 | break; |
| 861 | case Settings::NativeButton::SR: | 883 | case Settings::NativeButton::SRLeft: |
| 862 | controller.npad_button_state.left_sr.Assign(current_status.value); | 884 | controller.npad_button_state.left_sr.Assign(current_status.value); |
| 885 | break; | ||
| 886 | case Settings::NativeButton::SRRight: | ||
| 863 | controller.npad_button_state.right_sr.Assign(current_status.value); | 887 | controller.npad_button_state.right_sr.Assign(current_status.value); |
| 864 | break; | 888 | break; |
| 865 | case Settings::NativeButton::Home: | 889 | case Settings::NativeButton::Home: |
| @@ -1091,30 +1115,30 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac | |||
| 1091 | 1115 | ||
| 1092 | bool is_charging = false; | 1116 | bool is_charging = false; |
| 1093 | bool is_powered = false; | 1117 | bool is_powered = false; |
| 1094 | NpadBatteryLevel battery_level = 0; | 1118 | NpadBatteryLevel battery_level = NpadBatteryLevel::Empty; |
| 1095 | switch (controller.battery_values[index]) { | 1119 | switch (controller.battery_values[index]) { |
| 1096 | case Common::Input::BatteryLevel::Charging: | 1120 | case Common::Input::BatteryLevel::Charging: |
| 1097 | is_charging = true; | 1121 | is_charging = true; |
| 1098 | is_powered = true; | 1122 | is_powered = true; |
| 1099 | battery_level = 6; | 1123 | battery_level = NpadBatteryLevel::Full; |
| 1100 | break; | 1124 | break; |
| 1101 | case Common::Input::BatteryLevel::Medium: | 1125 | case Common::Input::BatteryLevel::Medium: |
| 1102 | battery_level = 6; | 1126 | battery_level = NpadBatteryLevel::High; |
| 1103 | break; | 1127 | break; |
| 1104 | case Common::Input::BatteryLevel::Low: | 1128 | case Common::Input::BatteryLevel::Low: |
| 1105 | battery_level = 4; | 1129 | battery_level = NpadBatteryLevel::Low; |
| 1106 | break; | 1130 | break; |
| 1107 | case Common::Input::BatteryLevel::Critical: | 1131 | case Common::Input::BatteryLevel::Critical: |
| 1108 | battery_level = 2; | 1132 | battery_level = NpadBatteryLevel::Critical; |
| 1109 | break; | 1133 | break; |
| 1110 | case Common::Input::BatteryLevel::Empty: | 1134 | case Common::Input::BatteryLevel::Empty: |
| 1111 | battery_level = 0; | 1135 | battery_level = NpadBatteryLevel::Empty; |
| 1112 | break; | 1136 | break; |
| 1113 | case Common::Input::BatteryLevel::None: | 1137 | case Common::Input::BatteryLevel::None: |
| 1114 | case Common::Input::BatteryLevel::Full: | 1138 | case Common::Input::BatteryLevel::Full: |
| 1115 | default: | 1139 | default: |
| 1116 | is_powered = true; | 1140 | is_powered = true; |
| 1117 | battery_level = 8; | 1141 | battery_level = NpadBatteryLevel::Full; |
| 1118 | break; | 1142 | break; |
| 1119 | } | 1143 | } |
| 1120 | 1144 | ||
| @@ -1185,13 +1209,16 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { | |||
| 1185 | } | 1209 | } |
| 1186 | 1210 | ||
| 1187 | bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { | 1211 | bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { |
| 1212 | if (!is_initalized) { | ||
| 1213 | return false; | ||
| 1214 | } | ||
| 1188 | if (device_index >= output_devices.size()) { | 1215 | if (device_index >= output_devices.size()) { |
| 1189 | return false; | 1216 | return false; |
| 1190 | } | 1217 | } |
| 1191 | if (!output_devices[device_index]) { | 1218 | if (!output_devices[device_index]) { |
| 1192 | return false; | 1219 | return false; |
| 1193 | } | 1220 | } |
| 1194 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 1221 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 1195 | const auto& player = Settings::values.players.GetValue()[player_index]; | 1222 | const auto& player = Settings::values.players.GetValue()[player_index]; |
| 1196 | const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; | 1223 | const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; |
| 1197 | 1224 | ||
| @@ -1217,9 +1244,13 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v | |||
| 1217 | } | 1244 | } |
| 1218 | 1245 | ||
| 1219 | bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { | 1246 | bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { |
| 1220 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 1247 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 1221 | const auto& player = Settings::values.players.GetValue()[player_index]; | 1248 | const auto& player = Settings::values.players.GetValue()[player_index]; |
| 1222 | 1249 | ||
| 1250 | if (!is_initalized) { | ||
| 1251 | return false; | ||
| 1252 | } | ||
| 1253 | |||
| 1223 | if (!player.vibration_enabled) { | 1254 | if (!player.vibration_enabled) { |
| 1224 | return false; | 1255 | return false; |
| 1225 | } | 1256 | } |
| @@ -1239,6 +1270,10 @@ Common::Input::DriverResult EmulatedController::SetPollingMode( | |||
| 1239 | EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { | 1270 | EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { |
| 1240 | LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index); | 1271 | LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index); |
| 1241 | 1272 | ||
| 1273 | if (!is_initalized) { | ||
| 1274 | return Common::Input::DriverResult::InvalidHandle; | ||
| 1275 | } | ||
| 1276 | |||
| 1242 | auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)]; | 1277 | auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)]; |
| 1243 | auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1278 | auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 1244 | auto& nfc_output_device = output_devices[3]; | 1279 | auto& nfc_output_device = output_devices[3]; |
| @@ -1284,6 +1319,10 @@ bool EmulatedController::SetCameraFormat( | |||
| 1284 | Core::IrSensor::ImageTransferProcessorFormat camera_format) { | 1319 | Core::IrSensor::ImageTransferProcessorFormat camera_format) { |
| 1285 | LOG_INFO(Service_HID, "Set camera format {}", camera_format); | 1320 | LOG_INFO(Service_HID, "Set camera format {}", camera_format); |
| 1286 | 1321 | ||
| 1322 | if (!is_initalized) { | ||
| 1323 | return false; | ||
| 1324 | } | ||
| 1325 | |||
| 1287 | auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1326 | auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 1288 | auto& camera_output_device = output_devices[2]; | 1327 | auto& camera_output_device = output_devices[2]; |
| 1289 | 1328 | ||
| @@ -1307,6 +1346,11 @@ void EmulatedController::SetRingParam(Common::ParamPackage param) { | |||
| 1307 | } | 1346 | } |
| 1308 | 1347 | ||
| 1309 | bool EmulatedController::HasNfc() const { | 1348 | bool EmulatedController::HasNfc() const { |
| 1349 | |||
| 1350 | if (!is_initalized) { | ||
| 1351 | return false; | ||
| 1352 | } | ||
| 1353 | |||
| 1310 | const auto& nfc_output_device = output_devices[3]; | 1354 | const auto& nfc_output_device = output_devices[3]; |
| 1311 | 1355 | ||
| 1312 | switch (npad_type) { | 1356 | switch (npad_type) { |
| @@ -1344,6 +1388,10 @@ bool EmulatedController::RemoveNfcHandle() { | |||
| 1344 | } | 1388 | } |
| 1345 | 1389 | ||
| 1346 | bool EmulatedController::StartNfcPolling() { | 1390 | bool EmulatedController::StartNfcPolling() { |
| 1391 | if (!is_initalized) { | ||
| 1392 | return false; | ||
| 1393 | } | ||
| 1394 | |||
| 1347 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1395 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 1348 | auto& nfc_virtual_output_device = output_devices[3]; | 1396 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1349 | 1397 | ||
| @@ -1355,6 +1403,10 @@ bool EmulatedController::StartNfcPolling() { | |||
| 1355 | } | 1403 | } |
| 1356 | 1404 | ||
| 1357 | bool EmulatedController::StopNfcPolling() { | 1405 | bool EmulatedController::StopNfcPolling() { |
| 1406 | if (!is_initalized) { | ||
| 1407 | return false; | ||
| 1408 | } | ||
| 1409 | |||
| 1358 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1410 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 1359 | auto& nfc_virtual_output_device = output_devices[3]; | 1411 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1360 | 1412 | ||
| @@ -1366,6 +1418,10 @@ bool EmulatedController::StopNfcPolling() { | |||
| 1366 | } | 1418 | } |
| 1367 | 1419 | ||
| 1368 | bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { | 1420 | bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { |
| 1421 | if (!is_initalized) { | ||
| 1422 | return false; | ||
| 1423 | } | ||
| 1424 | |||
| 1369 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1425 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 1370 | auto& nfc_virtual_output_device = output_devices[3]; | 1426 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1371 | 1427 | ||
| @@ -1378,6 +1434,10 @@ bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { | |||
| 1378 | 1434 | ||
| 1379 | bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, | 1435 | bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, |
| 1380 | Common::Input::MifareRequest& out_data) { | 1436 | Common::Input::MifareRequest& out_data) { |
| 1437 | if (!is_initalized) { | ||
| 1438 | return false; | ||
| 1439 | } | ||
| 1440 | |||
| 1381 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1441 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 1382 | auto& nfc_virtual_output_device = output_devices[3]; | 1442 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1383 | 1443 | ||
| @@ -1390,6 +1450,10 @@ bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& requ | |||
| 1390 | } | 1450 | } |
| 1391 | 1451 | ||
| 1392 | bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { | 1452 | bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { |
| 1453 | if (!is_initalized) { | ||
| 1454 | return false; | ||
| 1455 | } | ||
| 1456 | |||
| 1393 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1457 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 1394 | auto& nfc_virtual_output_device = output_devices[3]; | 1458 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1395 | 1459 | ||
| @@ -1401,6 +1465,10 @@ bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& req | |||
| 1401 | } | 1465 | } |
| 1402 | 1466 | ||
| 1403 | bool EmulatedController::WriteNfc(const std::vector<u8>& data) { | 1467 | bool EmulatedController::WriteNfc(const std::vector<u8>& data) { |
| 1468 | if (!is_initalized) { | ||
| 1469 | return false; | ||
| 1470 | } | ||
| 1471 | |||
| 1404 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1472 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 1405 | auto& nfc_virtual_output_device = output_devices[3]; | 1473 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1406 | 1474 | ||
| @@ -1412,6 +1480,10 @@ bool EmulatedController::WriteNfc(const std::vector<u8>& data) { | |||
| 1412 | } | 1480 | } |
| 1413 | 1481 | ||
| 1414 | void EmulatedController::SetLedPattern() { | 1482 | void EmulatedController::SetLedPattern() { |
| 1483 | if (!is_initalized) { | ||
| 1484 | return; | ||
| 1485 | } | ||
| 1486 | |||
| 1415 | for (auto& device : output_devices) { | 1487 | for (auto& device : output_devices) { |
| 1416 | if (!device) { | 1488 | if (!device) { |
| 1417 | continue; | 1489 | continue; |
| @@ -1627,7 +1699,7 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { | |||
| 1627 | } | 1699 | } |
| 1628 | if (is_connected) { | 1700 | if (is_connected) { |
| 1629 | LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", | 1701 | LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", |
| 1630 | NpadIdTypeToIndex(npad_id_type)); | 1702 | Service::HID::NpadIdTypeToIndex(npad_id_type)); |
| 1631 | } | 1703 | } |
| 1632 | npad_type = npad_type_; | 1704 | npad_type = npad_type_; |
| 1633 | } | 1705 | } |
| @@ -1877,12 +1949,16 @@ NpadButton EmulatedController::GetTurboButtonMask() const { | |||
| 1877 | case Settings::NativeButton::DDown: | 1949 | case Settings::NativeButton::DDown: |
| 1878 | button_mask.down.Assign(1); | 1950 | button_mask.down.Assign(1); |
| 1879 | break; | 1951 | break; |
| 1880 | case Settings::NativeButton::SL: | 1952 | case Settings::NativeButton::SLLeft: |
| 1881 | button_mask.left_sl.Assign(1); | 1953 | button_mask.left_sl.Assign(1); |
| 1954 | break; | ||
| 1955 | case Settings::NativeButton::SLRight: | ||
| 1882 | button_mask.right_sl.Assign(1); | 1956 | button_mask.right_sl.Assign(1); |
| 1883 | break; | 1957 | break; |
| 1884 | case Settings::NativeButton::SR: | 1958 | case Settings::NativeButton::SRLeft: |
| 1885 | button_mask.left_sr.Assign(1); | 1959 | button_mask.left_sr.Assign(1); |
| 1960 | break; | ||
| 1961 | case Settings::NativeButton::SRRight: | ||
| 1886 | button_mask.right_sr.Assign(1); | 1962 | button_mask.right_sr.Assign(1); |
| 1887 | break; | 1963 | break; |
| 1888 | default: | 1964 | default: |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index d4500583e..d6e20ab66 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -253,6 +253,9 @@ public: | |||
| 253 | /// Overrides current mapped devices with the stored configuration and reloads all input devices | 253 | /// Overrides current mapped devices with the stored configuration and reloads all input devices |
| 254 | void ReloadFromSettings(); | 254 | void ReloadFromSettings(); |
| 255 | 255 | ||
| 256 | /// Updates current colors with the ones stored in the configuration | ||
| 257 | void ReloadColorsFromSettings(); | ||
| 258 | |||
| 256 | /// Saves the current mapped configuration | 259 | /// Saves the current mapped configuration |
| 257 | void SaveCurrentConfig(); | 260 | void SaveCurrentConfig(); |
| 258 | 261 | ||
| @@ -556,6 +559,7 @@ private: | |||
| 556 | NpadStyleTag supported_style_tag{NpadStyleSet::All}; | 559 | NpadStyleTag supported_style_tag{NpadStyleSet::All}; |
| 557 | bool is_connected{false}; | 560 | bool is_connected{false}; |
| 558 | bool is_configuring{false}; | 561 | bool is_configuring{false}; |
| 562 | bool is_initalized{false}; | ||
| 559 | bool system_buttons_enabled{true}; | 563 | bool system_buttons_enabled{true}; |
| 560 | f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; | 564 | f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; |
| 561 | u32 turbo_button_state{0}; | 565 | u32 turbo_button_state{0}; |
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp index cf53c04d9..2cf25a870 100644 --- a/src/core/hid/hid_core.cpp +++ b/src/core/hid/hid_core.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "core/hid/emulated_controller.h" | 6 | #include "core/hid/emulated_controller.h" |
| 7 | #include "core/hid/emulated_devices.h" | 7 | #include "core/hid/emulated_devices.h" |
| 8 | #include "core/hid/hid_core.h" | 8 | #include "core/hid/hid_core.h" |
| 9 | #include "core/hle/service/hid/hid_util.h" | ||
| 9 | 10 | ||
| 10 | namespace Core::HID { | 11 | namespace Core::HID { |
| 11 | 12 | ||
| @@ -98,11 +99,11 @@ const EmulatedDevices* HIDCore::GetEmulatedDevices() const { | |||
| 98 | } | 99 | } |
| 99 | 100 | ||
| 100 | EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) { | 101 | EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) { |
| 101 | return GetEmulatedController(IndexToNpadIdType(index)); | 102 | return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); |
| 102 | } | 103 | } |
| 103 | 104 | ||
| 104 | const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const { | 105 | const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const { |
| 105 | return GetEmulatedController(IndexToNpadIdType(index)); | 106 | return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); |
| 106 | } | 107 | } |
| 107 | 108 | ||
| 108 | void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) { | 109 | void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) { |
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index 00beb40dd..4bf285f36 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/point.h" | 9 | #include "common/point.h" |
| 10 | #include "common/uuid.h" | 10 | #include "common/uuid.h" |
| 11 | #include "common/vector_math.h" | ||
| 11 | 12 | ||
| 12 | namespace Core::HID { | 13 | namespace Core::HID { |
| 13 | 14 | ||
| @@ -218,6 +219,13 @@ enum class NpadIdType : u32 { | |||
| 218 | Invalid = 0xFFFFFFFF, | 219 | Invalid = 0xFFFFFFFF, |
| 219 | }; | 220 | }; |
| 220 | 221 | ||
| 222 | enum class NpadInterfaceType : u8 { | ||
| 223 | Bluetooth = 1, | ||
| 224 | Rail = 2, | ||
| 225 | Usb = 3, | ||
| 226 | Embedded = 4, | ||
| 227 | }; | ||
| 228 | |||
| 221 | // This is nn::hid::NpadStyleIndex | 229 | // This is nn::hid::NpadStyleIndex |
| 222 | enum class NpadStyleIndex : u8 { | 230 | enum class NpadStyleIndex : u8 { |
| 223 | None = 0, | 231 | None = 0, |
| @@ -302,6 +310,15 @@ enum class TouchScreenModeForNx : u8 { | |||
| 302 | Heat2, | 310 | Heat2, |
| 303 | }; | 311 | }; |
| 304 | 312 | ||
| 313 | // This is nn::hid::system::NpadBatteryLevel | ||
| 314 | enum class NpadBatteryLevel : u32 { | ||
| 315 | Empty, | ||
| 316 | Critical, | ||
| 317 | Low, | ||
| 318 | High, | ||
| 319 | Full, | ||
| 320 | }; | ||
| 321 | |||
| 305 | // This is nn::hid::NpadStyleTag | 322 | // This is nn::hid::NpadStyleTag |
| 306 | struct NpadStyleTag { | 323 | struct NpadStyleTag { |
| 307 | union { | 324 | union { |
| @@ -347,6 +364,14 @@ struct TouchState { | |||
| 347 | }; | 364 | }; |
| 348 | static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); | 365 | static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); |
| 349 | 366 | ||
| 367 | struct TouchFinger { | ||
| 368 | u64 last_touch{}; | ||
| 369 | Common::Point<float> position{}; | ||
| 370 | u32 id{}; | ||
| 371 | TouchAttribute attribute{}; | ||
| 372 | bool pressed{}; | ||
| 373 | }; | ||
| 374 | |||
| 350 | // This is nn::hid::TouchScreenConfigurationForNx | 375 | // This is nn::hid::TouchScreenConfigurationForNx |
| 351 | struct TouchScreenConfigurationForNx { | 376 | struct TouchScreenConfigurationForNx { |
| 352 | TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; | 377 | TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; |
| @@ -385,16 +410,12 @@ struct NpadGcTriggerState { | |||
| 385 | }; | 410 | }; |
| 386 | static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); | 411 | static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); |
| 387 | 412 | ||
| 388 | // This is nn::hid::system::NpadBatteryLevel | ||
| 389 | using NpadBatteryLevel = u32; | ||
| 390 | static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size"); | ||
| 391 | |||
| 392 | // This is nn::hid::system::NpadPowerInfo | 413 | // This is nn::hid::system::NpadPowerInfo |
| 393 | struct NpadPowerInfo { | 414 | struct NpadPowerInfo { |
| 394 | bool is_powered{}; | 415 | bool is_powered{}; |
| 395 | bool is_charging{}; | 416 | bool is_charging{}; |
| 396 | INSERT_PADDING_BYTES(0x6); | 417 | INSERT_PADDING_BYTES(0x6); |
| 397 | NpadBatteryLevel battery_level{8}; | 418 | NpadBatteryLevel battery_level{NpadBatteryLevel::Full}; |
| 398 | }; | 419 | }; |
| 399 | static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); | 420 | static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); |
| 400 | 421 | ||
| @@ -578,6 +599,29 @@ struct SixAxisSensorIcInformation { | |||
| 578 | static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, | 599 | static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, |
| 579 | "SixAxisSensorIcInformation is an invalid size"); | 600 | "SixAxisSensorIcInformation is an invalid size"); |
| 580 | 601 | ||
| 602 | // This is nn::hid::SixAxisSensorAttribute | ||
| 603 | struct SixAxisSensorAttribute { | ||
| 604 | union { | ||
| 605 | u32 raw{}; | ||
| 606 | BitField<0, 1, u32> is_connected; | ||
| 607 | BitField<1, 1, u32> is_interpolated; | ||
| 608 | }; | ||
| 609 | }; | ||
| 610 | static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size"); | ||
| 611 | |||
| 612 | // This is nn::hid::SixAxisSensorState | ||
| 613 | struct SixAxisSensorState { | ||
| 614 | s64 delta_time{}; | ||
| 615 | s64 sampling_number{}; | ||
| 616 | Common::Vec3f accel{}; | ||
| 617 | Common::Vec3f gyro{}; | ||
| 618 | Common::Vec3f rotation{}; | ||
| 619 | std::array<Common::Vec3f, 3> orientation{}; | ||
| 620 | SixAxisSensorAttribute attribute{}; | ||
| 621 | INSERT_PADDING_BYTES(4); // Reserved | ||
| 622 | }; | ||
| 623 | static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); | ||
| 624 | |||
| 581 | // This is nn::hid::VibrationDeviceHandle | 625 | // This is nn::hid::VibrationDeviceHandle |
| 582 | struct VibrationDeviceHandle { | 626 | struct VibrationDeviceHandle { |
| 583 | NpadStyleIndex npad_type{NpadStyleIndex::None}; | 627 | NpadStyleIndex npad_type{NpadStyleIndex::None}; |
| @@ -688,60 +732,4 @@ struct UniquePadId { | |||
| 688 | }; | 732 | }; |
| 689 | static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); | 733 | static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); |
| 690 | 734 | ||
| 691 | /// Converts a NpadIdType to an array index. | ||
| 692 | constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) { | ||
| 693 | switch (npad_id_type) { | ||
| 694 | case NpadIdType::Player1: | ||
| 695 | return 0; | ||
| 696 | case NpadIdType::Player2: | ||
| 697 | return 1; | ||
| 698 | case NpadIdType::Player3: | ||
| 699 | return 2; | ||
| 700 | case NpadIdType::Player4: | ||
| 701 | return 3; | ||
| 702 | case NpadIdType::Player5: | ||
| 703 | return 4; | ||
| 704 | case NpadIdType::Player6: | ||
| 705 | return 5; | ||
| 706 | case NpadIdType::Player7: | ||
| 707 | return 6; | ||
| 708 | case NpadIdType::Player8: | ||
| 709 | return 7; | ||
| 710 | case NpadIdType::Handheld: | ||
| 711 | return 8; | ||
| 712 | case NpadIdType::Other: | ||
| 713 | return 9; | ||
| 714 | default: | ||
| 715 | return 0; | ||
| 716 | } | ||
| 717 | } | ||
| 718 | |||
| 719 | /// Converts an array index to a NpadIdType | ||
| 720 | constexpr NpadIdType IndexToNpadIdType(size_t index) { | ||
| 721 | switch (index) { | ||
| 722 | case 0: | ||
| 723 | return NpadIdType::Player1; | ||
| 724 | case 1: | ||
| 725 | return NpadIdType::Player2; | ||
| 726 | case 2: | ||
| 727 | return NpadIdType::Player3; | ||
| 728 | case 3: | ||
| 729 | return NpadIdType::Player4; | ||
| 730 | case 4: | ||
| 731 | return NpadIdType::Player5; | ||
| 732 | case 5: | ||
| 733 | return NpadIdType::Player6; | ||
| 734 | case 6: | ||
| 735 | return NpadIdType::Player7; | ||
| 736 | case 7: | ||
| 737 | return NpadIdType::Player8; | ||
| 738 | case 8: | ||
| 739 | return NpadIdType::Handheld; | ||
| 740 | case 9: | ||
| 741 | return NpadIdType::Other; | ||
| 742 | default: | ||
| 743 | return NpadIdType::Invalid; | ||
| 744 | } | ||
| 745 | } | ||
| 746 | |||
| 747 | } // namespace Core::HID | 735 | } // namespace Core::HID |
diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp index 76d6b8ab0..a6bdd28f2 100644 --- a/src/core/hid/input_interpreter.cpp +++ b/src/core/hid/input_interpreter.cpp | |||
| @@ -5,21 +5,22 @@ | |||
| 5 | #include "core/hid/hid_types.h" | 5 | #include "core/hid/hid_types.h" |
| 6 | #include "core/hid/input_interpreter.h" | 6 | #include "core/hid/input_interpreter.h" |
| 7 | #include "core/hle/service/hid/controllers/npad.h" | 7 | #include "core/hle/service/hid/controllers/npad.h" |
| 8 | #include "core/hle/service/hid/hid.h" | 8 | #include "core/hle/service/hid/hid_server.h" |
| 9 | #include "core/hle/service/hid/resource_manager.h" | ||
| 9 | #include "core/hle/service/sm/sm.h" | 10 | #include "core/hle/service/sm/sm.h" |
| 10 | 11 | ||
| 11 | InputInterpreter::InputInterpreter(Core::System& system) | 12 | InputInterpreter::InputInterpreter(Core::System& system) |
| 12 | : npad{system.ServiceManager() | 13 | : npad{system.ServiceManager() |
| 13 | .GetService<Service::HID::Hid>("hid") | 14 | .GetService<Service::HID::IHidServer>("hid") |
| 14 | ->GetAppletResource() | 15 | ->GetResourceManager() |
| 15 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} { | 16 | ->GetNpad()} { |
| 16 | ResetButtonStates(); | 17 | ResetButtonStates(); |
| 17 | } | 18 | } |
| 18 | 19 | ||
| 19 | InputInterpreter::~InputInterpreter() = default; | 20 | InputInterpreter::~InputInterpreter() = default; |
| 20 | 21 | ||
| 21 | void InputInterpreter::PollInput() { | 22 | void InputInterpreter::PollInput() { |
| 22 | const auto button_state = npad.GetAndResetPressState(); | 23 | const auto button_state = npad->GetAndResetPressState(); |
| 23 | 24 | ||
| 24 | previous_index = current_index; | 25 | previous_index = current_index; |
| 25 | current_index = (current_index + 1) % button_states.size(); | 26 | current_index = (current_index + 1) % button_states.size(); |
diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h index 8c521b381..3569aac93 100644 --- a/src/core/hid/input_interpreter.h +++ b/src/core/hid/input_interpreter.h | |||
| @@ -16,7 +16,7 @@ enum class NpadButton : u64; | |||
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | namespace Service::HID { | 18 | namespace Service::HID { |
| 19 | class Controller_NPad; | 19 | class NPad; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| @@ -101,7 +101,7 @@ public: | |||
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | private: | 103 | private: |
| 104 | Service::HID::Controller_NPad& npad; | 104 | std::shared_ptr<Service::HID::NPad> npad; |
| 105 | 105 | ||
| 106 | /// Stores 9 consecutive button states polled from HID. | 106 | /// Stores 9 consecutive button states polled from HID. |
| 107 | std::array<Core::HID::NpadButton, 9> button_states{}; | 107 | std::array<Core::HID::NpadButton, 9> button_states{}; |
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp index 59364efa1..37fa39a73 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp | |||
| @@ -222,7 +222,7 @@ Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* | |||
| 222 | }; | 222 | }; |
| 223 | 223 | ||
| 224 | // We succeeded. | 224 | // We succeeded. |
| 225 | *out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr); | 225 | *out = KPageTable::GetHeapVirtualAddress(kernel, paddr); |
| 226 | R_SUCCEED(); | 226 | R_SUCCEED(); |
| 227 | } | 227 | } |
| 228 | 228 | ||
| @@ -238,8 +238,17 @@ void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress addres | |||
| 238 | ASSERT(Common::IsAligned(size, alignment)); | 238 | ASSERT(Common::IsAligned(size, alignment)); |
| 239 | 239 | ||
| 240 | // Close the secure region's pages. | 240 | // Close the secure region's pages. |
| 241 | kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address), | 241 | kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel, address), |
| 242 | size / PageSize); | 242 | size / PageSize); |
| 243 | } | 243 | } |
| 244 | 244 | ||
| 245 | // Insecure Memory. | ||
| 246 | KResourceLimit* KSystemControl::GetInsecureMemoryResourceLimit(KernelCore& kernel) { | ||
| 247 | return kernel.GetSystemResourceLimit(); | ||
| 248 | } | ||
| 249 | |||
| 250 | u32 KSystemControl::GetInsecureMemoryPool() { | ||
| 251 | return static_cast<u32>(KMemoryManager::Pool::SystemNonSecure); | ||
| 252 | } | ||
| 253 | |||
| 245 | } // namespace Kernel::Board::Nintendo::Nx | 254 | } // namespace Kernel::Board::Nintendo::Nx |
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h index ff1feec70..60c5e58b7 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h | |||
| @@ -8,7 +8,8 @@ | |||
| 8 | 8 | ||
| 9 | namespace Kernel { | 9 | namespace Kernel { |
| 10 | class KernelCore; | 10 | class KernelCore; |
| 11 | } | 11 | class KResourceLimit; |
| 12 | } // namespace Kernel | ||
| 12 | 13 | ||
| 13 | namespace Kernel::Board::Nintendo::Nx { | 14 | namespace Kernel::Board::Nintendo::Nx { |
| 14 | 15 | ||
| @@ -40,6 +41,10 @@ public: | |||
| 40 | u32 pool); | 41 | u32 pool); |
| 41 | static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size, | 42 | static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size, |
| 42 | u32 pool); | 43 | u32 pool); |
| 44 | |||
| 45 | // Insecure Memory. | ||
| 46 | static KResourceLimit* GetInsecureMemoryResourceLimit(KernelCore& kernel); | ||
| 47 | static u32 GetInsecureMemoryPool(); | ||
| 43 | }; | 48 | }; |
| 44 | 49 | ||
| 45 | } // namespace Kernel::Board::Nintendo::Nx | 50 | } // namespace Kernel::Board::Nintendo::Nx |
diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp index e7da7a21d..274fee493 100644 --- a/src/core/hle/kernel/k_capabilities.cpp +++ b/src/core/hle/kernel/k_capabilities.cpp | |||
| @@ -4,14 +4,16 @@ | |||
| 4 | #include "core/hardware_properties.h" | 4 | #include "core/hardware_properties.h" |
| 5 | #include "core/hle/kernel/k_capabilities.h" | 5 | #include "core/hle/kernel/k_capabilities.h" |
| 6 | #include "core/hle/kernel/k_memory_layout.h" | 6 | #include "core/hle/kernel/k_memory_layout.h" |
| 7 | #include "core/hle/kernel/k_page_table.h" | 7 | #include "core/hle/kernel/k_process_page_table.h" |
| 8 | #include "core/hle/kernel/k_trace.h" | ||
| 8 | #include "core/hle/kernel/kernel.h" | 9 | #include "core/hle/kernel/kernel.h" |
| 9 | #include "core/hle/kernel/svc_results.h" | 10 | #include "core/hle/kernel/svc_results.h" |
| 10 | #include "core/hle/kernel/svc_version.h" | 11 | #include "core/hle/kernel/svc_version.h" |
| 11 | 12 | ||
| 12 | namespace Kernel { | 13 | namespace Kernel { |
| 13 | 14 | ||
| 14 | Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table) { | 15 | Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, |
| 16 | KProcessPageTable* page_table) { | ||
| 15 | // We're initializing an initial process. | 17 | // We're initializing an initial process. |
| 16 | m_svc_access_flags.reset(); | 18 | m_svc_access_flags.reset(); |
| 17 | m_irq_access_flags.reset(); | 19 | m_irq_access_flags.reset(); |
| @@ -41,7 +43,8 @@ Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTabl | |||
| 41 | R_RETURN(this->SetCapabilities(kern_caps, page_table)); | 43 | R_RETURN(this->SetCapabilities(kern_caps, page_table)); |
| 42 | } | 44 | } |
| 43 | 45 | ||
| 44 | Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table) { | 46 | Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, |
| 47 | KProcessPageTable* page_table) { | ||
| 45 | // We're initializing a user process. | 48 | // We're initializing a user process. |
| 46 | m_svc_access_flags.reset(); | 49 | m_svc_access_flags.reset(); |
| 47 | m_irq_access_flags.reset(); | 50 | m_irq_access_flags.reset(); |
| @@ -121,7 +124,7 @@ Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) { | |||
| 121 | R_SUCCEED(); | 124 | R_SUCCEED(); |
| 122 | } | 125 | } |
| 123 | 126 | ||
| 124 | Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table) { | 127 | Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table) { |
| 125 | const auto range_pack = MapRange{cap}; | 128 | const auto range_pack = MapRange{cap}; |
| 126 | const auto size_pack = MapRangeSize{size_cap}; | 129 | const auto size_pack = MapRangeSize{size_cap}; |
| 127 | 130 | ||
| @@ -142,16 +145,13 @@ Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* p | |||
| 142 | ? KMemoryPermission::UserRead | 145 | ? KMemoryPermission::UserRead |
| 143 | : KMemoryPermission::UserReadWrite; | 146 | : KMemoryPermission::UserReadWrite; |
| 144 | if (MapRangeSize{size_cap}.normal) { | 147 | if (MapRangeSize{size_cap}.normal) { |
| 145 | // R_RETURN(page_table->MapStatic(phys_addr, size, perm)); | 148 | R_RETURN(page_table->MapStatic(phys_addr, size, perm)); |
| 146 | } else { | 149 | } else { |
| 147 | // R_RETURN(page_table->MapIo(phys_addr, size, perm)); | 150 | R_RETURN(page_table->MapIo(phys_addr, size, perm)); |
| 148 | } | 151 | } |
| 149 | |||
| 150 | UNIMPLEMENTED(); | ||
| 151 | R_SUCCEED(); | ||
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) { | 154 | Result KCapabilities::MapIoPage_(const u32 cap, KProcessPageTable* page_table) { |
| 155 | // Get/validate address/size | 155 | // Get/validate address/size |
| 156 | const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize; | 156 | const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize; |
| 157 | const size_t num_pages = 1; | 157 | const size_t num_pages = 1; |
| @@ -160,10 +160,7 @@ Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) { | |||
| 160 | R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress); | 160 | R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress); |
| 161 | 161 | ||
| 162 | // Do the mapping. | 162 | // Do the mapping. |
| 163 | // R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite)); | 163 | R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission::UserReadWrite)); |
| 164 | |||
| 165 | UNIMPLEMENTED(); | ||
| 166 | R_SUCCEED(); | ||
| 167 | } | 164 | } |
| 168 | 165 | ||
| 169 | template <typename F> | 166 | template <typename F> |
| @@ -200,13 +197,11 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) { | |||
| 200 | R_SUCCEED(); | 197 | R_SUCCEED(); |
| 201 | } | 198 | } |
| 202 | 199 | ||
| 203 | Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) { | 200 | Result KCapabilities::MapRegion_(const u32 cap, KProcessPageTable* page_table) { |
| 204 | // Map each region into the process's page table. | 201 | // Map each region into the process's page table. |
| 205 | return ProcessMapRegionCapability( | 202 | return ProcessMapRegionCapability( |
| 206 | cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result { | 203 | cap, [page_table](KMemoryRegionType region_type, KMemoryPermission perm) -> Result { |
| 207 | // R_RETURN(page_table->MapRegion(region_type, perm)); | 204 | R_RETURN(page_table->MapRegion(region_type, perm)); |
| 208 | UNIMPLEMENTED(); | ||
| 209 | R_SUCCEED(); | ||
| 210 | }); | 205 | }); |
| 211 | } | 206 | } |
| 212 | 207 | ||
| @@ -280,7 +275,7 @@ Result KCapabilities::SetDebugFlagsCapability(const u32 cap) { | |||
| 280 | } | 275 | } |
| 281 | 276 | ||
| 282 | Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc, | 277 | Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc, |
| 283 | KPageTable* page_table) { | 278 | KProcessPageTable* page_table) { |
| 284 | // Validate this is a capability we can act on. | 279 | // Validate this is a capability we can act on. |
| 285 | const auto type = GetCapabilityType(cap); | 280 | const auto type = GetCapabilityType(cap); |
| 286 | R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument); | 281 | R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument); |
| @@ -318,7 +313,7 @@ Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc, | |||
| 318 | } | 313 | } |
| 319 | } | 314 | } |
| 320 | 315 | ||
| 321 | Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* page_table) { | 316 | Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table) { |
| 322 | u32 set_flags = 0, set_svc = 0; | 317 | u32 set_flags = 0, set_svc = 0; |
| 323 | 318 | ||
| 324 | for (size_t i = 0; i < caps.size(); i++) { | 319 | for (size_t i = 0; i < caps.size(); i++) { |
| @@ -335,6 +330,8 @@ Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* pag | |||
| 335 | 330 | ||
| 336 | // Map the range. | 331 | // Map the range. |
| 337 | R_TRY(this->MapRange_(cap, size_cap, page_table)); | 332 | R_TRY(this->MapRange_(cap, size_cap, page_table)); |
| 333 | } else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) { | ||
| 334 | continue; | ||
| 338 | } else { | 335 | } else { |
| 339 | R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table)); | 336 | R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table)); |
| 340 | } | 337 | } |
diff --git a/src/core/hle/kernel/k_capabilities.h b/src/core/hle/kernel/k_capabilities.h index ebd4eedb1..013d952ad 100644 --- a/src/core/hle/kernel/k_capabilities.h +++ b/src/core/hle/kernel/k_capabilities.h | |||
| @@ -15,15 +15,15 @@ | |||
| 15 | 15 | ||
| 16 | namespace Kernel { | 16 | namespace Kernel { |
| 17 | 17 | ||
| 18 | class KPageTable; | 18 | class KProcessPageTable; |
| 19 | class KernelCore; | 19 | class KernelCore; |
| 20 | 20 | ||
| 21 | class KCapabilities { | 21 | class KCapabilities { |
| 22 | public: | 22 | public: |
| 23 | constexpr explicit KCapabilities() = default; | 23 | constexpr explicit KCapabilities() = default; |
| 24 | 24 | ||
| 25 | Result InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table); | 25 | Result InitializeForKip(std::span<const u32> kern_caps, KProcessPageTable* page_table); |
| 26 | Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table); | 26 | Result InitializeForUser(std::span<const u32> user_caps, KProcessPageTable* page_table); |
| 27 | 27 | ||
| 28 | static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps); | 28 | static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps); |
| 29 | 29 | ||
| @@ -264,9 +264,9 @@ private: | |||
| 264 | 264 | ||
| 265 | Result SetCorePriorityCapability(const u32 cap); | 265 | Result SetCorePriorityCapability(const u32 cap); |
| 266 | Result SetSyscallMaskCapability(const u32 cap, u32& set_svc); | 266 | Result SetSyscallMaskCapability(const u32 cap, u32& set_svc); |
| 267 | Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table); | 267 | Result MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table); |
| 268 | Result MapIoPage_(const u32 cap, KPageTable* page_table); | 268 | Result MapIoPage_(const u32 cap, KProcessPageTable* page_table); |
| 269 | Result MapRegion_(const u32 cap, KPageTable* page_table); | 269 | Result MapRegion_(const u32 cap, KProcessPageTable* page_table); |
| 270 | Result SetInterruptPairCapability(const u32 cap); | 270 | Result SetInterruptPairCapability(const u32 cap); |
| 271 | Result SetProgramTypeCapability(const u32 cap); | 271 | Result SetProgramTypeCapability(const u32 cap); |
| 272 | Result SetKernelVersionCapability(const u32 cap); | 272 | Result SetKernelVersionCapability(const u32 cap); |
| @@ -277,8 +277,9 @@ private: | |||
| 277 | static Result ProcessMapRegionCapability(const u32 cap, F f); | 277 | static Result ProcessMapRegionCapability(const u32 cap, F f); |
| 278 | static Result CheckMapRegion(KernelCore& kernel, const u32 cap); | 278 | static Result CheckMapRegion(KernelCore& kernel, const u32 cap); |
| 279 | 279 | ||
| 280 | Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table); | 280 | Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, |
| 281 | Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table); | 281 | KProcessPageTable* page_table); |
| 282 | Result SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table); | ||
| 282 | 283 | ||
| 283 | private: | 284 | private: |
| 284 | Svc::SvcAccessFlagSet m_svc_access_flags{}; | 285 | Svc::SvcAccessFlagSet m_svc_access_flags{}; |
diff --git a/src/core/hle/kernel/k_device_address_space.cpp b/src/core/hle/kernel/k_device_address_space.cpp index f48896715..f0703f795 100644 --- a/src/core/hle/kernel/k_device_address_space.cpp +++ b/src/core/hle/kernel/k_device_address_space.cpp | |||
| @@ -54,7 +54,7 @@ Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) { | |||
| 54 | R_SUCCEED(); | 54 | R_SUCCEED(); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_address, | 57 | Result KDeviceAddressSpace::Map(KProcessPageTable* page_table, KProcessAddress process_address, |
| 58 | size_t size, u64 device_address, u32 option, bool is_aligned) { | 58 | size_t size, u64 device_address, u32 option, bool is_aligned) { |
| 59 | // Check that the address falls within the space. | 59 | // Check that the address falls within the space. |
| 60 | R_UNLESS((m_space_address <= device_address && | 60 | R_UNLESS((m_space_address <= device_address && |
| @@ -113,7 +113,7 @@ Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_ | |||
| 113 | R_SUCCEED(); | 113 | R_SUCCEED(); |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | Result KDeviceAddressSpace::Unmap(KPageTable* page_table, KProcessAddress process_address, | 116 | Result KDeviceAddressSpace::Unmap(KProcessPageTable* page_table, KProcessAddress process_address, |
| 117 | size_t size, u64 device_address) { | 117 | size_t size, u64 device_address) { |
| 118 | // Check that the address falls within the space. | 118 | // Check that the address falls within the space. |
| 119 | R_UNLESS((m_space_address <= device_address && | 119 | R_UNLESS((m_space_address <= device_address && |
diff --git a/src/core/hle/kernel/k_device_address_space.h b/src/core/hle/kernel/k_device_address_space.h index 18556e3cc..ff0ec8152 100644 --- a/src/core/hle/kernel/k_device_address_space.h +++ b/src/core/hle/kernel/k_device_address_space.h | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <string> | 6 | #include <string> |
| 7 | 7 | ||
| 8 | #include "core/hle/kernel/k_page_table.h" | 8 | #include "core/hle/kernel/k_process_page_table.h" |
| 9 | #include "core/hle/kernel/k_typed_address.h" | 9 | #include "core/hle/kernel/k_typed_address.h" |
| 10 | #include "core/hle/kernel/slab_helpers.h" | 10 | #include "core/hle/kernel/slab_helpers.h" |
| 11 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| @@ -31,23 +31,23 @@ public: | |||
| 31 | Result Attach(Svc::DeviceName device_name); | 31 | Result Attach(Svc::DeviceName device_name); |
| 32 | Result Detach(Svc::DeviceName device_name); | 32 | Result Detach(Svc::DeviceName device_name); |
| 33 | 33 | ||
| 34 | Result MapByForce(KPageTable* page_table, KProcessAddress process_address, size_t size, | 34 | Result MapByForce(KProcessPageTable* page_table, KProcessAddress process_address, size_t size, |
| 35 | u64 device_address, u32 option) { | 35 | u64 device_address, u32 option) { |
| 36 | R_RETURN(this->Map(page_table, process_address, size, device_address, option, false)); | 36 | R_RETURN(this->Map(page_table, process_address, size, device_address, option, false)); |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | Result MapAligned(KPageTable* page_table, KProcessAddress process_address, size_t size, | 39 | Result MapAligned(KProcessPageTable* page_table, KProcessAddress process_address, size_t size, |
| 40 | u64 device_address, u32 option) { | 40 | u64 device_address, u32 option) { |
| 41 | R_RETURN(this->Map(page_table, process_address, size, device_address, option, true)); | 41 | R_RETURN(this->Map(page_table, process_address, size, device_address, option, true)); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | Result Unmap(KPageTable* page_table, KProcessAddress process_address, size_t size, | 44 | Result Unmap(KProcessPageTable* page_table, KProcessAddress process_address, size_t size, |
| 45 | u64 device_address); | 45 | u64 device_address); |
| 46 | 46 | ||
| 47 | static void Initialize(); | 47 | static void Initialize(); |
| 48 | 48 | ||
| 49 | private: | 49 | private: |
| 50 | Result Map(KPageTable* page_table, KProcessAddress process_address, size_t size, | 50 | Result Map(KProcessPageTable* page_table, KProcessAddress process_address, size_t size, |
| 51 | u64 device_address, u32 option, bool is_aligned); | 51 | u64 device_address, u32 option, bool is_aligned); |
| 52 | 52 | ||
| 53 | private: | 53 | private: |
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h index c8122644f..d7adb3169 100644 --- a/src/core/hle/kernel/k_memory_layout.h +++ b/src/core/hle/kernel/k_memory_layout.h | |||
| @@ -394,6 +394,14 @@ private: | |||
| 394 | return region.GetEndAddress(); | 394 | return region.GetEndAddress(); |
| 395 | } | 395 | } |
| 396 | 396 | ||
| 397 | public: | ||
| 398 | static const KMemoryRegion* Find(const KMemoryLayout& layout, KVirtualAddress address) { | ||
| 399 | return Find(address, layout.GetVirtualMemoryRegionTree()); | ||
| 400 | } | ||
| 401 | static const KMemoryRegion* Find(const KMemoryLayout& layout, KPhysicalAddress address) { | ||
| 402 | return Find(address, layout.GetPhysicalMemoryRegionTree()); | ||
| 403 | } | ||
| 404 | |||
| 397 | private: | 405 | private: |
| 398 | u64 m_linear_phys_to_virt_diff{}; | 406 | u64 m_linear_phys_to_virt_diff{}; |
| 399 | u64 m_linear_virt_to_phys_diff{}; | 407 | u64 m_linear_virt_to_phys_diff{}; |
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index cdc5572d8..0a973ec8c 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp | |||
| @@ -456,8 +456,7 @@ size_t KMemoryManager::Impl::Initialize(KPhysicalAddress address, size_t size, | |||
| 456 | } | 456 | } |
| 457 | 457 | ||
| 458 | void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) { | 458 | void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) { |
| 459 | auto optimize_pa = | 459 | auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region); |
| 460 | KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); | ||
| 461 | auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); | 460 | auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); |
| 462 | 461 | ||
| 463 | std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize())); | 462 | std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize())); |
| @@ -465,8 +464,7 @@ void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) { | |||
| 465 | 464 | ||
| 466 | void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, | 465 | void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, |
| 467 | size_t num_pages) { | 466 | size_t num_pages) { |
| 468 | auto optimize_pa = | 467 | auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region); |
| 469 | KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); | ||
| 470 | auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); | 468 | auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); |
| 471 | 469 | ||
| 472 | // Get the range we're tracking. | 470 | // Get the range we're tracking. |
| @@ -485,8 +483,7 @@ void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysi | |||
| 485 | 483 | ||
| 486 | void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, | 484 | void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, |
| 487 | size_t num_pages) { | 485 | size_t num_pages) { |
| 488 | auto optimize_pa = | 486 | auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region); |
| 489 | KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); | ||
| 490 | auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); | 487 | auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa); |
| 491 | 488 | ||
| 492 | // Get the range we're tracking. | 489 | // Get the range we're tracking. |
| @@ -506,8 +503,7 @@ void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysica | |||
| 506 | bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, | 503 | bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, |
| 507 | size_t num_pages, u8 fill_pattern) { | 504 | size_t num_pages, u8 fill_pattern) { |
| 508 | auto& device_memory = kernel.System().DeviceMemory(); | 505 | auto& device_memory = kernel.System().DeviceMemory(); |
| 509 | auto optimize_pa = | 506 | auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region); |
| 510 | KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region); | ||
| 511 | auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa); | 507 | auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa); |
| 512 | 508 | ||
| 513 | // We want to return whether any pages were newly allocated. | 509 | // We want to return whether any pages were newly allocated. |
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp deleted file mode 100644 index 1d47bdf6b..000000000 --- a/src/core/hle/kernel/k_page_table.cpp +++ /dev/null | |||
| @@ -1,3519 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/alignment.h" | ||
| 5 | #include "common/assert.h" | ||
| 6 | #include "common/literals.h" | ||
| 7 | #include "common/scope_exit.h" | ||
| 8 | #include "common/settings.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "core/hle/kernel/k_address_space_info.h" | ||
| 11 | #include "core/hle/kernel/k_memory_block.h" | ||
| 12 | #include "core/hle/kernel/k_memory_block_manager.h" | ||
| 13 | #include "core/hle/kernel/k_page_group.h" | ||
| 14 | #include "core/hle/kernel/k_page_table.h" | ||
| 15 | #include "core/hle/kernel/k_process.h" | ||
| 16 | #include "core/hle/kernel/k_resource_limit.h" | ||
| 17 | #include "core/hle/kernel/k_scoped_resource_reservation.h" | ||
| 18 | #include "core/hle/kernel/k_system_control.h" | ||
| 19 | #include "core/hle/kernel/k_system_resource.h" | ||
| 20 | #include "core/hle/kernel/kernel.h" | ||
| 21 | #include "core/hle/kernel/svc_results.h" | ||
| 22 | #include "core/memory.h" | ||
| 23 | |||
| 24 | namespace Kernel { | ||
| 25 | |||
| 26 | namespace { | ||
| 27 | |||
| 28 | class KScopedLightLockPair { | ||
| 29 | YUZU_NON_COPYABLE(KScopedLightLockPair); | ||
| 30 | YUZU_NON_MOVEABLE(KScopedLightLockPair); | ||
| 31 | |||
| 32 | private: | ||
| 33 | KLightLock* m_lower; | ||
| 34 | KLightLock* m_upper; | ||
| 35 | |||
| 36 | public: | ||
| 37 | KScopedLightLockPair(KLightLock& lhs, KLightLock& rhs) { | ||
| 38 | // Ensure our locks are in a consistent order. | ||
| 39 | if (std::addressof(lhs) <= std::addressof(rhs)) { | ||
| 40 | m_lower = std::addressof(lhs); | ||
| 41 | m_upper = std::addressof(rhs); | ||
| 42 | } else { | ||
| 43 | m_lower = std::addressof(rhs); | ||
| 44 | m_upper = std::addressof(lhs); | ||
| 45 | } | ||
| 46 | |||
| 47 | // Acquire both locks. | ||
| 48 | m_lower->Lock(); | ||
| 49 | if (m_lower != m_upper) { | ||
| 50 | m_upper->Lock(); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | ~KScopedLightLockPair() { | ||
| 55 | // Unlock the upper lock. | ||
| 56 | if (m_upper != nullptr && m_upper != m_lower) { | ||
| 57 | m_upper->Unlock(); | ||
| 58 | } | ||
| 59 | |||
| 60 | // Unlock the lower lock. | ||
| 61 | if (m_lower != nullptr) { | ||
| 62 | m_lower->Unlock(); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | public: | ||
| 67 | // Utility. | ||
| 68 | void TryUnlockHalf(KLightLock& lock) { | ||
| 69 | // Only allow unlocking if the lock is half the pair. | ||
| 70 | if (m_lower != m_upper) { | ||
| 71 | // We want to be sure the lock is one we own. | ||
| 72 | if (m_lower == std::addressof(lock)) { | ||
| 73 | lock.Unlock(); | ||
| 74 | m_lower = nullptr; | ||
| 75 | } else if (m_upper == std::addressof(lock)) { | ||
| 76 | lock.Unlock(); | ||
| 77 | m_upper = nullptr; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | |||
| 83 | using namespace Common::Literals; | ||
| 84 | |||
| 85 | constexpr size_t GetAddressSpaceWidthFromType(Svc::CreateProcessFlag as_type) { | ||
| 86 | switch (as_type) { | ||
| 87 | case Svc::CreateProcessFlag::AddressSpace32Bit: | ||
| 88 | case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias: | ||
| 89 | return 32; | ||
| 90 | case Svc::CreateProcessFlag::AddressSpace64BitDeprecated: | ||
| 91 | return 36; | ||
| 92 | case Svc::CreateProcessFlag::AddressSpace64Bit: | ||
| 93 | return 39; | ||
| 94 | default: | ||
| 95 | ASSERT(false); | ||
| 96 | return {}; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | } // namespace | ||
| 101 | |||
| 102 | KPageTable::KPageTable(Core::System& system_) | ||
| 103 | : m_general_lock{system_.Kernel()}, | ||
| 104 | m_map_physical_memory_lock{system_.Kernel()}, m_system{system_}, m_kernel{system_.Kernel()} {} | ||
| 105 | |||
| 106 | KPageTable::~KPageTable() = default; | ||
| 107 | |||
| 108 | Result KPageTable::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, | ||
| 109 | bool enable_das_merge, bool from_back, | ||
| 110 | KMemoryManager::Pool pool, KProcessAddress code_addr, | ||
| 111 | size_t code_size, KSystemResource* system_resource, | ||
| 112 | KResourceLimit* resource_limit, | ||
| 113 | Core::Memory::Memory& memory) { | ||
| 114 | |||
| 115 | const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) { | ||
| 116 | return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type); | ||
| 117 | }; | ||
| 118 | const auto GetSpaceSize = [this](KAddressSpaceInfo::Type type) { | ||
| 119 | return KAddressSpaceInfo::GetAddressSpaceSize(m_address_space_width, type); | ||
| 120 | }; | ||
| 121 | |||
| 122 | // Set the tracking memory | ||
| 123 | m_memory = std::addressof(memory); | ||
| 124 | |||
| 125 | // Set our width and heap/alias sizes | ||
| 126 | m_address_space_width = GetAddressSpaceWidthFromType(as_type); | ||
| 127 | const KProcessAddress start = 0; | ||
| 128 | const KProcessAddress end{1ULL << m_address_space_width}; | ||
| 129 | size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)}; | ||
| 130 | size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)}; | ||
| 131 | |||
| 132 | ASSERT(code_addr < code_addr + code_size); | ||
| 133 | ASSERT(code_addr + code_size - 1 <= end - 1); | ||
| 134 | |||
| 135 | // Adjust heap/alias size if we don't have an alias region | ||
| 136 | if (as_type == Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias) { | ||
| 137 | heap_region_size += alias_region_size; | ||
| 138 | alias_region_size = 0; | ||
| 139 | } | ||
| 140 | |||
| 141 | // Set code regions and determine remaining | ||
| 142 | constexpr size_t RegionAlignment{2_MiB}; | ||
| 143 | KProcessAddress process_code_start{}; | ||
| 144 | KProcessAddress process_code_end{}; | ||
| 145 | size_t stack_region_size{}; | ||
| 146 | size_t kernel_map_region_size{}; | ||
| 147 | |||
| 148 | if (m_address_space_width == 39) { | ||
| 149 | alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias); | ||
| 150 | heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap); | ||
| 151 | stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack); | ||
| 152 | kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); | ||
| 153 | m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit); | ||
| 154 | m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit); | ||
| 155 | m_alias_code_region_start = m_code_region_start; | ||
| 156 | m_alias_code_region_end = m_code_region_end; | ||
| 157 | process_code_start = Common::AlignDown(GetInteger(code_addr), RegionAlignment); | ||
| 158 | process_code_end = Common::AlignUp(GetInteger(code_addr) + code_size, RegionAlignment); | ||
| 159 | } else { | ||
| 160 | stack_region_size = 0; | ||
| 161 | kernel_map_region_size = 0; | ||
| 162 | m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall); | ||
| 163 | m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); | ||
| 164 | m_stack_region_start = m_code_region_start; | ||
| 165 | m_alias_code_region_start = m_code_region_start; | ||
| 166 | m_alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) + | ||
| 167 | GetSpaceSize(KAddressSpaceInfo::Type::MapLarge); | ||
| 168 | m_stack_region_end = m_code_region_end; | ||
| 169 | m_kernel_map_region_start = m_code_region_start; | ||
| 170 | m_kernel_map_region_end = m_code_region_end; | ||
| 171 | process_code_start = m_code_region_start; | ||
| 172 | process_code_end = m_code_region_end; | ||
| 173 | } | ||
| 174 | |||
| 175 | // Set other basic fields | ||
| 176 | m_enable_aslr = enable_aslr; | ||
| 177 | m_enable_device_address_space_merge = enable_das_merge; | ||
| 178 | m_address_space_start = start; | ||
| 179 | m_address_space_end = end; | ||
| 180 | m_is_kernel = false; | ||
| 181 | m_memory_block_slab_manager = system_resource->GetMemoryBlockSlabManagerPointer(); | ||
| 182 | m_block_info_manager = system_resource->GetBlockInfoManagerPointer(); | ||
| 183 | m_resource_limit = resource_limit; | ||
| 184 | |||
| 185 | // Determine the region we can place our undetermineds in | ||
| 186 | KProcessAddress alloc_start{}; | ||
| 187 | size_t alloc_size{}; | ||
| 188 | if ((process_code_start - m_code_region_start) >= (end - process_code_end)) { | ||
| 189 | alloc_start = m_code_region_start; | ||
| 190 | alloc_size = process_code_start - m_code_region_start; | ||
| 191 | } else { | ||
| 192 | alloc_start = process_code_end; | ||
| 193 | alloc_size = end - process_code_end; | ||
| 194 | } | ||
| 195 | const size_t needed_size = | ||
| 196 | (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size); | ||
| 197 | R_UNLESS(alloc_size >= needed_size, ResultOutOfMemory); | ||
| 198 | |||
| 199 | const size_t remaining_size{alloc_size - needed_size}; | ||
| 200 | |||
| 201 | // Determine random placements for each region | ||
| 202 | size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{}; | ||
| 203 | if (enable_aslr) { | ||
| 204 | alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * | ||
| 205 | RegionAlignment; | ||
| 206 | heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * | ||
| 207 | RegionAlignment; | ||
| 208 | stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * | ||
| 209 | RegionAlignment; | ||
| 210 | kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * | ||
| 211 | RegionAlignment; | ||
| 212 | } | ||
| 213 | |||
| 214 | // Setup heap and alias regions | ||
| 215 | m_alias_region_start = alloc_start + alias_rnd; | ||
| 216 | m_alias_region_end = m_alias_region_start + alias_region_size; | ||
| 217 | m_heap_region_start = alloc_start + heap_rnd; | ||
| 218 | m_heap_region_end = m_heap_region_start + heap_region_size; | ||
| 219 | |||
| 220 | if (alias_rnd <= heap_rnd) { | ||
| 221 | m_heap_region_start += alias_region_size; | ||
| 222 | m_heap_region_end += alias_region_size; | ||
| 223 | } else { | ||
| 224 | m_alias_region_start += heap_region_size; | ||
| 225 | m_alias_region_end += heap_region_size; | ||
| 226 | } | ||
| 227 | |||
| 228 | // Setup stack region | ||
| 229 | if (stack_region_size) { | ||
| 230 | m_stack_region_start = alloc_start + stack_rnd; | ||
| 231 | m_stack_region_end = m_stack_region_start + stack_region_size; | ||
| 232 | |||
| 233 | if (alias_rnd < stack_rnd) { | ||
| 234 | m_stack_region_start += alias_region_size; | ||
| 235 | m_stack_region_end += alias_region_size; | ||
| 236 | } else { | ||
| 237 | m_alias_region_start += stack_region_size; | ||
| 238 | m_alias_region_end += stack_region_size; | ||
| 239 | } | ||
| 240 | |||
| 241 | if (heap_rnd < stack_rnd) { | ||
| 242 | m_stack_region_start += heap_region_size; | ||
| 243 | m_stack_region_end += heap_region_size; | ||
| 244 | } else { | ||
| 245 | m_heap_region_start += stack_region_size; | ||
| 246 | m_heap_region_end += stack_region_size; | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | // Setup kernel map region | ||
| 251 | if (kernel_map_region_size) { | ||
| 252 | m_kernel_map_region_start = alloc_start + kmap_rnd; | ||
| 253 | m_kernel_map_region_end = m_kernel_map_region_start + kernel_map_region_size; | ||
| 254 | |||
| 255 | if (alias_rnd < kmap_rnd) { | ||
| 256 | m_kernel_map_region_start += alias_region_size; | ||
| 257 | m_kernel_map_region_end += alias_region_size; | ||
| 258 | } else { | ||
| 259 | m_alias_region_start += kernel_map_region_size; | ||
| 260 | m_alias_region_end += kernel_map_region_size; | ||
| 261 | } | ||
| 262 | |||
| 263 | if (heap_rnd < kmap_rnd) { | ||
| 264 | m_kernel_map_region_start += heap_region_size; | ||
| 265 | m_kernel_map_region_end += heap_region_size; | ||
| 266 | } else { | ||
| 267 | m_heap_region_start += kernel_map_region_size; | ||
| 268 | m_heap_region_end += kernel_map_region_size; | ||
| 269 | } | ||
| 270 | |||
| 271 | if (stack_region_size) { | ||
| 272 | if (stack_rnd < kmap_rnd) { | ||
| 273 | m_kernel_map_region_start += stack_region_size; | ||
| 274 | m_kernel_map_region_end += stack_region_size; | ||
| 275 | } else { | ||
| 276 | m_stack_region_start += kernel_map_region_size; | ||
| 277 | m_stack_region_end += kernel_map_region_size; | ||
| 278 | } | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | // Set heap and fill members. | ||
| 283 | m_current_heap_end = m_heap_region_start; | ||
| 284 | m_max_heap_size = 0; | ||
| 285 | m_mapped_physical_memory_size = 0; | ||
| 286 | m_mapped_unsafe_physical_memory = 0; | ||
| 287 | m_mapped_insecure_memory = 0; | ||
| 288 | m_mapped_ipc_server_memory = 0; | ||
| 289 | |||
| 290 | m_heap_fill_value = 0; | ||
| 291 | m_ipc_fill_value = 0; | ||
| 292 | m_stack_fill_value = 0; | ||
| 293 | |||
| 294 | // Set allocation option. | ||
| 295 | m_allocate_option = | ||
| 296 | KMemoryManager::EncodeOption(pool, from_back ? KMemoryManager::Direction::FromBack | ||
| 297 | : KMemoryManager::Direction::FromFront); | ||
| 298 | |||
| 299 | // Ensure that we regions inside our address space | ||
| 300 | auto IsInAddressSpace = [&](KProcessAddress addr) { | ||
| 301 | return m_address_space_start <= addr && addr <= m_address_space_end; | ||
| 302 | }; | ||
| 303 | ASSERT(IsInAddressSpace(m_alias_region_start)); | ||
| 304 | ASSERT(IsInAddressSpace(m_alias_region_end)); | ||
| 305 | ASSERT(IsInAddressSpace(m_heap_region_start)); | ||
| 306 | ASSERT(IsInAddressSpace(m_heap_region_end)); | ||
| 307 | ASSERT(IsInAddressSpace(m_stack_region_start)); | ||
| 308 | ASSERT(IsInAddressSpace(m_stack_region_end)); | ||
| 309 | ASSERT(IsInAddressSpace(m_kernel_map_region_start)); | ||
| 310 | ASSERT(IsInAddressSpace(m_kernel_map_region_end)); | ||
| 311 | |||
| 312 | // Ensure that we selected regions that don't overlap | ||
| 313 | const KProcessAddress alias_start{m_alias_region_start}; | ||
| 314 | const KProcessAddress alias_last{m_alias_region_end - 1}; | ||
| 315 | const KProcessAddress heap_start{m_heap_region_start}; | ||
| 316 | const KProcessAddress heap_last{m_heap_region_end - 1}; | ||
| 317 | const KProcessAddress stack_start{m_stack_region_start}; | ||
| 318 | const KProcessAddress stack_last{m_stack_region_end - 1}; | ||
| 319 | const KProcessAddress kmap_start{m_kernel_map_region_start}; | ||
| 320 | const KProcessAddress kmap_last{m_kernel_map_region_end - 1}; | ||
| 321 | ASSERT(alias_last < heap_start || heap_last < alias_start); | ||
| 322 | ASSERT(alias_last < stack_start || stack_last < alias_start); | ||
| 323 | ASSERT(alias_last < kmap_start || kmap_last < alias_start); | ||
| 324 | ASSERT(heap_last < stack_start || stack_last < heap_start); | ||
| 325 | ASSERT(heap_last < kmap_start || kmap_last < heap_start); | ||
| 326 | |||
| 327 | m_current_heap_end = m_heap_region_start; | ||
| 328 | m_max_heap_size = 0; | ||
| 329 | m_mapped_physical_memory_size = 0; | ||
| 330 | m_memory_pool = pool; | ||
| 331 | |||
| 332 | m_page_table_impl = std::make_unique<Common::PageTable>(); | ||
| 333 | m_page_table_impl->Resize(m_address_space_width, PageBits); | ||
| 334 | |||
| 335 | // Initialize our memory block manager. | ||
| 336 | R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end, | ||
| 337 | m_memory_block_slab_manager)); | ||
| 338 | } | ||
| 339 | |||
| 340 | void KPageTable::Finalize() { | ||
| 341 | auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { | ||
| 342 | if (Settings::IsFastmemEnabled()) { | ||
| 343 | m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size); | ||
| 344 | } | ||
| 345 | }; | ||
| 346 | |||
| 347 | // Finalize memory blocks. | ||
| 348 | m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback)); | ||
| 349 | |||
| 350 | // Release any insecure mapped memory. | ||
| 351 | if (m_mapped_insecure_memory) { | ||
| 352 | UNIMPLEMENTED(); | ||
| 353 | } | ||
| 354 | |||
| 355 | // Release any ipc server memory. | ||
| 356 | if (m_mapped_ipc_server_memory) { | ||
| 357 | UNIMPLEMENTED(); | ||
| 358 | } | ||
| 359 | |||
| 360 | // Close the backing page table, as the destructor is not called for guest objects. | ||
| 361 | m_page_table_impl.reset(); | ||
| 362 | } | ||
| 363 | |||
| 364 | Result KPageTable::MapProcessCode(KProcessAddress addr, size_t num_pages, KMemoryState state, | ||
| 365 | KMemoryPermission perm) { | ||
| 366 | const u64 size{num_pages * PageSize}; | ||
| 367 | |||
| 368 | // Validate the mapping request. | ||
| 369 | R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); | ||
| 370 | |||
| 371 | // Lock the table. | ||
| 372 | KScopedLightLock lk(m_general_lock); | ||
| 373 | |||
| 374 | // Verify that the destination memory is unmapped. | ||
| 375 | R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, | ||
| 376 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 377 | KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 378 | |||
| 379 | // Create an update allocator. | ||
| 380 | Result allocator_result{ResultSuccess}; | ||
| 381 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 382 | m_memory_block_slab_manager); | ||
| 383 | |||
| 384 | // Allocate and open. | ||
| 385 | KPageGroup pg{m_kernel, m_block_info_manager}; | ||
| 386 | R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( | ||
| 387 | &pg, num_pages, | ||
| 388 | KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option))); | ||
| 389 | |||
| 390 | R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup)); | ||
| 391 | |||
| 392 | // Update the blocks. | ||
| 393 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, | ||
| 394 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 395 | KMemoryBlockDisableMergeAttribute::None); | ||
| 396 | |||
| 397 | R_SUCCEED(); | ||
| 398 | } | ||
| 399 | |||
| 400 | Result KPageTable::MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 401 | size_t size) { | ||
| 402 | // Validate the mapping request. | ||
| 403 | R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), | ||
| 404 | ResultInvalidMemoryRegion); | ||
| 405 | |||
| 406 | // Lock the table. | ||
| 407 | KScopedLightLock lk(m_general_lock); | ||
| 408 | |||
| 409 | // Verify that the source memory is normal heap. | ||
| 410 | KMemoryState src_state{}; | ||
| 411 | KMemoryPermission src_perm{}; | ||
| 412 | size_t num_src_allocator_blocks{}; | ||
| 413 | R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks, | ||
| 414 | src_address, size, KMemoryState::All, KMemoryState::Normal, | ||
| 415 | KMemoryPermission::All, KMemoryPermission::UserReadWrite, | ||
| 416 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 417 | |||
| 418 | // Verify that the destination memory is unmapped. | ||
| 419 | size_t num_dst_allocator_blocks{}; | ||
| 420 | R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All, | ||
| 421 | KMemoryState::Free, KMemoryPermission::None, | ||
| 422 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 423 | KMemoryAttribute::None)); | ||
| 424 | |||
| 425 | // Create an update allocator for the source. | ||
| 426 | Result src_allocator_result{ResultSuccess}; | ||
| 427 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), | ||
| 428 | m_memory_block_slab_manager, | ||
| 429 | num_src_allocator_blocks); | ||
| 430 | R_TRY(src_allocator_result); | ||
| 431 | |||
| 432 | // Create an update allocator for the destination. | ||
| 433 | Result dst_allocator_result{ResultSuccess}; | ||
| 434 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), | ||
| 435 | m_memory_block_slab_manager, | ||
| 436 | num_dst_allocator_blocks); | ||
| 437 | R_TRY(dst_allocator_result); | ||
| 438 | |||
| 439 | // Map the code memory. | ||
| 440 | { | ||
| 441 | // Determine the number of pages being operated on. | ||
| 442 | const size_t num_pages = size / PageSize; | ||
| 443 | |||
| 444 | // Create page groups for the memory being mapped. | ||
| 445 | KPageGroup pg{m_kernel, m_block_info_manager}; | ||
| 446 | AddRegionToPages(src_address, num_pages, pg); | ||
| 447 | |||
| 448 | // We're going to perform an update, so create a helper. | ||
| 449 | KScopedPageTableUpdater updater(this); | ||
| 450 | |||
| 451 | // Reprotect the source as kernel-read/not mapped. | ||
| 452 | const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | | ||
| 453 | KMemoryPermission::NotMapped); | ||
| 454 | R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions)); | ||
| 455 | |||
| 456 | // Ensure that we unprotect the source pages on failure. | ||
| 457 | auto unprot_guard = SCOPE_GUARD({ | ||
| 458 | ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions) | ||
| 459 | .IsSuccess()); | ||
| 460 | }); | ||
| 461 | |||
| 462 | // Map the alias pages. | ||
| 463 | const KPageProperties dst_properties = {new_perm, false, false, | ||
| 464 | DisableMergeAttribute::DisableHead}; | ||
| 465 | R_TRY( | ||
| 466 | this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false)); | ||
| 467 | |||
| 468 | // We successfully mapped the alias pages, so we don't need to unprotect the src pages on | ||
| 469 | // failure. | ||
| 470 | unprot_guard.Cancel(); | ||
| 471 | |||
| 472 | // Apply the memory block updates. | ||
| 473 | m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, | ||
| 474 | src_state, new_perm, KMemoryAttribute::Locked, | ||
| 475 | KMemoryBlockDisableMergeAttribute::Locked, | ||
| 476 | KMemoryBlockDisableMergeAttribute::None); | ||
| 477 | m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, | ||
| 478 | KMemoryState::AliasCode, new_perm, KMemoryAttribute::None, | ||
| 479 | KMemoryBlockDisableMergeAttribute::Normal, | ||
| 480 | KMemoryBlockDisableMergeAttribute::None); | ||
| 481 | } | ||
| 482 | |||
| 483 | R_SUCCEED(); | ||
| 484 | } | ||
| 485 | |||
| 486 | Result KPageTable::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 487 | size_t size, | ||
| 488 | ICacheInvalidationStrategy icache_invalidation_strategy) { | ||
| 489 | // Validate the mapping request. | ||
| 490 | R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), | ||
| 491 | ResultInvalidMemoryRegion); | ||
| 492 | |||
| 493 | // Lock the table. | ||
| 494 | KScopedLightLock lk(m_general_lock); | ||
| 495 | |||
| 496 | // Verify that the source memory is locked normal heap. | ||
| 497 | size_t num_src_allocator_blocks{}; | ||
| 498 | R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size, | ||
| 499 | KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, | ||
| 500 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 501 | KMemoryAttribute::Locked)); | ||
| 502 | |||
| 503 | // Verify that the destination memory is aliasable code. | ||
| 504 | size_t num_dst_allocator_blocks{}; | ||
| 505 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 506 | std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias, | ||
| 507 | KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, | ||
| 508 | KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None)); | ||
| 509 | |||
| 510 | // Determine whether any pages being unmapped are code. | ||
| 511 | bool any_code_pages = false; | ||
| 512 | { | ||
| 513 | KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(dst_address); | ||
| 514 | while (true) { | ||
| 515 | // Get the memory info. | ||
| 516 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 517 | |||
| 518 | // Check if the memory has code flag. | ||
| 519 | if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) { | ||
| 520 | any_code_pages = true; | ||
| 521 | break; | ||
| 522 | } | ||
| 523 | |||
| 524 | // Check if we're done. | ||
| 525 | if (dst_address + size - 1 <= info.GetLastAddress()) { | ||
| 526 | break; | ||
| 527 | } | ||
| 528 | |||
| 529 | // Advance. | ||
| 530 | ++it; | ||
| 531 | } | ||
| 532 | } | ||
| 533 | |||
| 534 | // Ensure that we maintain the instruction cache. | ||
| 535 | bool reprotected_pages = false; | ||
| 536 | SCOPE_EXIT({ | ||
| 537 | if (reprotected_pages && any_code_pages) { | ||
| 538 | if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) { | ||
| 539 | m_system.InvalidateCpuInstructionCacheRange(GetInteger(dst_address), size); | ||
| 540 | } else { | ||
| 541 | m_system.InvalidateCpuInstructionCaches(); | ||
| 542 | } | ||
| 543 | } | ||
| 544 | }); | ||
| 545 | |||
| 546 | // Unmap. | ||
| 547 | { | ||
| 548 | // Determine the number of pages being operated on. | ||
| 549 | const size_t num_pages = size / PageSize; | ||
| 550 | |||
| 551 | // Create an update allocator for the source. | ||
| 552 | Result src_allocator_result{ResultSuccess}; | ||
| 553 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), | ||
| 554 | m_memory_block_slab_manager, | ||
| 555 | num_src_allocator_blocks); | ||
| 556 | R_TRY(src_allocator_result); | ||
| 557 | |||
| 558 | // Create an update allocator for the destination. | ||
| 559 | Result dst_allocator_result{ResultSuccess}; | ||
| 560 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), | ||
| 561 | m_memory_block_slab_manager, | ||
| 562 | num_dst_allocator_blocks); | ||
| 563 | R_TRY(dst_allocator_result); | ||
| 564 | |||
| 565 | // Unmap the aliased copy of the pages. | ||
| 566 | R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); | ||
| 567 | |||
| 568 | // Try to set the permissions for the source pages back to what they should be. | ||
| 569 | R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, | ||
| 570 | OperationType::ChangePermissions)); | ||
| 571 | |||
| 572 | // Apply the memory block updates. | ||
| 573 | m_memory_block_manager.Update( | ||
| 574 | std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None, | ||
| 575 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 576 | KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); | ||
| 577 | m_memory_block_manager.Update( | ||
| 578 | std::addressof(src_allocator), src_address, num_pages, KMemoryState::Normal, | ||
| 579 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 580 | KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked); | ||
| 581 | |||
| 582 | // Note that we reprotected pages. | ||
| 583 | reprotected_pages = true; | ||
| 584 | } | ||
| 585 | |||
| 586 | R_SUCCEED(); | ||
| 587 | } | ||
| 588 | |||
| 589 | KProcessAddress KPageTable::FindFreeArea(KProcessAddress region_start, size_t region_num_pages, | ||
| 590 | size_t num_pages, size_t alignment, size_t offset, | ||
| 591 | size_t guard_pages) { | ||
| 592 | KProcessAddress address = 0; | ||
| 593 | |||
| 594 | if (num_pages <= region_num_pages) { | ||
| 595 | if (this->IsAslrEnabled()) { | ||
| 596 | UNIMPLEMENTED(); | ||
| 597 | } | ||
| 598 | // Find the first free area. | ||
| 599 | if (address == 0) { | ||
| 600 | address = m_memory_block_manager.FindFreeArea(region_start, region_num_pages, num_pages, | ||
| 601 | alignment, offset, guard_pages); | ||
| 602 | } | ||
| 603 | } | ||
| 604 | |||
| 605 | return address; | ||
| 606 | } | ||
| 607 | |||
| 608 | Result KPageTable::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages) { | ||
| 609 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 610 | |||
| 611 | const size_t size = num_pages * PageSize; | ||
| 612 | |||
| 613 | // We're making a new group, not adding to an existing one. | ||
| 614 | R_UNLESS(pg.empty(), ResultInvalidCurrentMemory); | ||
| 615 | |||
| 616 | // Begin traversal. | ||
| 617 | Common::PageTable::TraversalContext context; | ||
| 618 | Common::PageTable::TraversalEntry next_entry; | ||
| 619 | R_UNLESS(m_page_table_impl->BeginTraversal(next_entry, context, GetInteger(addr)), | ||
| 620 | ResultInvalidCurrentMemory); | ||
| 621 | |||
| 622 | // Prepare tracking variables. | ||
| 623 | KPhysicalAddress cur_addr = next_entry.phys_addr; | ||
| 624 | size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1)); | ||
| 625 | size_t tot_size = cur_size; | ||
| 626 | |||
| 627 | // Iterate, adding to group as we go. | ||
| 628 | const auto& memory_layout = m_system.Kernel().MemoryLayout(); | ||
| 629 | while (tot_size < size) { | ||
| 630 | R_UNLESS(m_page_table_impl->ContinueTraversal(next_entry, context), | ||
| 631 | ResultInvalidCurrentMemory); | ||
| 632 | |||
| 633 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 634 | const size_t cur_pages = cur_size / PageSize; | ||
| 635 | |||
| 636 | R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory); | ||
| 637 | R_TRY(pg.AddBlock(cur_addr, cur_pages)); | ||
| 638 | |||
| 639 | cur_addr = next_entry.phys_addr; | ||
| 640 | cur_size = next_entry.block_size; | ||
| 641 | } else { | ||
| 642 | cur_size += next_entry.block_size; | ||
| 643 | } | ||
| 644 | |||
| 645 | tot_size += next_entry.block_size; | ||
| 646 | } | ||
| 647 | |||
| 648 | // Ensure we add the right amount for the last block. | ||
| 649 | if (tot_size > size) { | ||
| 650 | cur_size -= (tot_size - size); | ||
| 651 | } | ||
| 652 | |||
| 653 | // Add the last block. | ||
| 654 | const size_t cur_pages = cur_size / PageSize; | ||
| 655 | R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory); | ||
| 656 | R_TRY(pg.AddBlock(cur_addr, cur_pages)); | ||
| 657 | |||
| 658 | R_SUCCEED(); | ||
| 659 | } | ||
| 660 | |||
| 661 | bool KPageTable::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages) { | ||
| 662 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 663 | |||
| 664 | const size_t size = num_pages * PageSize; | ||
| 665 | const auto& memory_layout = m_system.Kernel().MemoryLayout(); | ||
| 666 | |||
| 667 | // Empty groups are necessarily invalid. | ||
| 668 | if (pg.empty()) { | ||
| 669 | return false; | ||
| 670 | } | ||
| 671 | |||
| 672 | // We're going to validate that the group we'd expect is the group we see. | ||
| 673 | auto cur_it = pg.begin(); | ||
| 674 | KPhysicalAddress cur_block_address = cur_it->GetAddress(); | ||
| 675 | size_t cur_block_pages = cur_it->GetNumPages(); | ||
| 676 | |||
| 677 | auto UpdateCurrentIterator = [&]() { | ||
| 678 | if (cur_block_pages == 0) { | ||
| 679 | if ((++cur_it) == pg.end()) { | ||
| 680 | return false; | ||
| 681 | } | ||
| 682 | |||
| 683 | cur_block_address = cur_it->GetAddress(); | ||
| 684 | cur_block_pages = cur_it->GetNumPages(); | ||
| 685 | } | ||
| 686 | return true; | ||
| 687 | }; | ||
| 688 | |||
| 689 | // Begin traversal. | ||
| 690 | Common::PageTable::TraversalContext context; | ||
| 691 | Common::PageTable::TraversalEntry next_entry; | ||
| 692 | if (!m_page_table_impl->BeginTraversal(next_entry, context, GetInteger(addr))) { | ||
| 693 | return false; | ||
| 694 | } | ||
| 695 | |||
| 696 | // Prepare tracking variables. | ||
| 697 | KPhysicalAddress cur_addr = next_entry.phys_addr; | ||
| 698 | size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1)); | ||
| 699 | size_t tot_size = cur_size; | ||
| 700 | |||
| 701 | // Iterate, comparing expected to actual. | ||
| 702 | while (tot_size < size) { | ||
| 703 | if (!m_page_table_impl->ContinueTraversal(next_entry, context)) { | ||
| 704 | return false; | ||
| 705 | } | ||
| 706 | |||
| 707 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 708 | const size_t cur_pages = cur_size / PageSize; | ||
| 709 | |||
| 710 | if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) { | ||
| 711 | return false; | ||
| 712 | } | ||
| 713 | |||
| 714 | if (!UpdateCurrentIterator()) { | ||
| 715 | return false; | ||
| 716 | } | ||
| 717 | |||
| 718 | if (cur_block_address != cur_addr || cur_block_pages < cur_pages) { | ||
| 719 | return false; | ||
| 720 | } | ||
| 721 | |||
| 722 | cur_block_address += cur_size; | ||
| 723 | cur_block_pages -= cur_pages; | ||
| 724 | cur_addr = next_entry.phys_addr; | ||
| 725 | cur_size = next_entry.block_size; | ||
| 726 | } else { | ||
| 727 | cur_size += next_entry.block_size; | ||
| 728 | } | ||
| 729 | |||
| 730 | tot_size += next_entry.block_size; | ||
| 731 | } | ||
| 732 | |||
| 733 | // Ensure we compare the right amount for the last block. | ||
| 734 | if (tot_size > size) { | ||
| 735 | cur_size -= (tot_size - size); | ||
| 736 | } | ||
| 737 | |||
| 738 | if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) { | ||
| 739 | return false; | ||
| 740 | } | ||
| 741 | |||
| 742 | if (!UpdateCurrentIterator()) { | ||
| 743 | return false; | ||
| 744 | } | ||
| 745 | |||
| 746 | return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize); | ||
| 747 | } | ||
| 748 | |||
| 749 | Result KPageTable::UnmapProcessMemory(KProcessAddress dst_addr, size_t size, | ||
| 750 | KPageTable& src_page_table, KProcessAddress src_addr) { | ||
| 751 | // Acquire the table locks. | ||
| 752 | KScopedLightLockPair lk(src_page_table.m_general_lock, m_general_lock); | ||
| 753 | |||
| 754 | const size_t num_pages{size / PageSize}; | ||
| 755 | |||
| 756 | // Check that the memory is mapped in the destination process. | ||
| 757 | size_t num_allocator_blocks; | ||
| 758 | R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All, | ||
| 759 | KMemoryState::SharedCode, KMemoryPermission::UserReadWrite, | ||
| 760 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, | ||
| 761 | KMemoryAttribute::None)); | ||
| 762 | |||
| 763 | // Check that the memory is mapped in the source process. | ||
| 764 | R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess, | ||
| 765 | KMemoryState::FlagCanMapProcess, KMemoryPermission::None, | ||
| 766 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 767 | KMemoryAttribute::None)); | ||
| 768 | |||
| 769 | // Create an update allocator. | ||
| 770 | Result allocator_result{ResultSuccess}; | ||
| 771 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 772 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 773 | R_TRY(allocator_result); | ||
| 774 | |||
| 775 | R_TRY(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); | ||
| 776 | |||
| 777 | // Apply the memory block update. | ||
| 778 | m_memory_block_manager.Update(std::addressof(allocator), dst_addr, num_pages, | ||
| 779 | KMemoryState::Free, KMemoryPermission::None, | ||
| 780 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | ||
| 781 | KMemoryBlockDisableMergeAttribute::Normal); | ||
| 782 | |||
| 783 | m_system.InvalidateCpuInstructionCaches(); | ||
| 784 | |||
| 785 | R_SUCCEED(); | ||
| 786 | } | ||
| 787 | |||
| 788 | Result KPageTable::SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed, | ||
| 789 | KProcessAddress address, size_t size, | ||
| 790 | KMemoryPermission test_perm, KMemoryState dst_state) { | ||
| 791 | // Validate pre-conditions. | ||
| 792 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 793 | ASSERT(test_perm == KMemoryPermission::UserReadWrite || | ||
| 794 | test_perm == KMemoryPermission::UserRead); | ||
| 795 | |||
| 796 | // Check that the address is in range. | ||
| 797 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 798 | |||
| 799 | // Get the source permission. | ||
| 800 | const auto src_perm = (test_perm == KMemoryPermission::UserReadWrite) | ||
| 801 | ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped | ||
| 802 | : KMemoryPermission::UserRead; | ||
| 803 | |||
| 804 | // Get aligned extents. | ||
| 805 | const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(address), PageSize); | ||
| 806 | const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(address) + size, PageSize); | ||
| 807 | const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(address), PageSize); | ||
| 808 | const KProcessAddress mapping_src_end = Common::AlignDown(GetInteger(address) + size, PageSize); | ||
| 809 | |||
| 810 | const auto aligned_src_last = (aligned_src_end)-1; | ||
| 811 | const auto mapping_src_last = (mapping_src_end)-1; | ||
| 812 | |||
| 813 | // Get the test state and attribute mask. | ||
| 814 | KMemoryState test_state; | ||
| 815 | KMemoryAttribute test_attr_mask; | ||
| 816 | switch (dst_state) { | ||
| 817 | case KMemoryState::Ipc: | ||
| 818 | test_state = KMemoryState::FlagCanUseIpc; | ||
| 819 | test_attr_mask = | ||
| 820 | KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked; | ||
| 821 | break; | ||
| 822 | case KMemoryState::NonSecureIpc: | ||
| 823 | test_state = KMemoryState::FlagCanUseNonSecureIpc; | ||
| 824 | test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; | ||
| 825 | break; | ||
| 826 | case KMemoryState::NonDeviceIpc: | ||
| 827 | test_state = KMemoryState::FlagCanUseNonDeviceIpc; | ||
| 828 | test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; | ||
| 829 | break; | ||
| 830 | default: | ||
| 831 | R_THROW(ResultInvalidCombination); | ||
| 832 | } | ||
| 833 | |||
| 834 | // Ensure that on failure, we roll back appropriately. | ||
| 835 | size_t mapped_size = 0; | ||
| 836 | ON_RESULT_FAILURE { | ||
| 837 | if (mapped_size > 0) { | ||
| 838 | this->CleanupForIpcClientOnServerSetupFailure(page_list, mapping_src_start, mapped_size, | ||
| 839 | src_perm); | ||
| 840 | } | ||
| 841 | }; | ||
| 842 | |||
| 843 | size_t blocks_needed = 0; | ||
| 844 | |||
| 845 | // Iterate, mapping as needed. | ||
| 846 | KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(aligned_src_start); | ||
| 847 | while (true) { | ||
| 848 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 849 | |||
| 850 | // Validate the current block. | ||
| 851 | R_TRY(this->CheckMemoryState(info, test_state, test_state, test_perm, test_perm, | ||
| 852 | test_attr_mask, KMemoryAttribute::None)); | ||
| 853 | |||
| 854 | if (mapping_src_start < mapping_src_end && (mapping_src_start) < info.GetEndAddress() && | ||
| 855 | info.GetAddress() < GetInteger(mapping_src_end)) { | ||
| 856 | const auto cur_start = info.GetAddress() >= GetInteger(mapping_src_start) | ||
| 857 | ? info.GetAddress() | ||
| 858 | : (mapping_src_start); | ||
| 859 | const auto cur_end = mapping_src_last >= info.GetLastAddress() ? info.GetEndAddress() | ||
| 860 | : (mapping_src_end); | ||
| 861 | const size_t cur_size = cur_end - cur_start; | ||
| 862 | |||
| 863 | if (info.GetAddress() < GetInteger(mapping_src_start)) { | ||
| 864 | ++blocks_needed; | ||
| 865 | } | ||
| 866 | if (mapping_src_last < info.GetLastAddress()) { | ||
| 867 | ++blocks_needed; | ||
| 868 | } | ||
| 869 | |||
| 870 | // Set the permissions on the block, if we need to. | ||
| 871 | if ((info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != src_perm) { | ||
| 872 | R_TRY(Operate(cur_start, cur_size / PageSize, src_perm, | ||
| 873 | OperationType::ChangePermissions)); | ||
| 874 | } | ||
| 875 | |||
| 876 | // Note that we mapped this part. | ||
| 877 | mapped_size += cur_size; | ||
| 878 | } | ||
| 879 | |||
| 880 | // If the block is at the end, we're done. | ||
| 881 | if (aligned_src_last <= info.GetLastAddress()) { | ||
| 882 | break; | ||
| 883 | } | ||
| 884 | |||
| 885 | // Advance. | ||
| 886 | ++it; | ||
| 887 | ASSERT(it != m_memory_block_manager.end()); | ||
| 888 | } | ||
| 889 | |||
| 890 | if (out_blocks_needed != nullptr) { | ||
| 891 | ASSERT(blocks_needed <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); | ||
| 892 | *out_blocks_needed = blocks_needed; | ||
| 893 | } | ||
| 894 | |||
| 895 | R_SUCCEED(); | ||
| 896 | } | ||
| 897 | |||
| 898 | Result KPageTable::SetupForIpcServer(KProcessAddress* out_addr, size_t size, | ||
| 899 | KProcessAddress src_addr, KMemoryPermission test_perm, | ||
| 900 | KMemoryState dst_state, KPageTable& src_page_table, | ||
| 901 | bool send) { | ||
| 902 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 903 | ASSERT(src_page_table.IsLockedByCurrentThread()); | ||
| 904 | |||
| 905 | // Check that we can theoretically map. | ||
| 906 | const KProcessAddress region_start = m_alias_region_start; | ||
| 907 | const size_t region_size = m_alias_region_end - m_alias_region_start; | ||
| 908 | R_UNLESS(size < region_size, ResultOutOfAddressSpace); | ||
| 909 | |||
| 910 | // Get aligned source extents. | ||
| 911 | const KProcessAddress src_start = src_addr; | ||
| 912 | const KProcessAddress src_end = src_addr + size; | ||
| 913 | const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(src_start), PageSize); | ||
| 914 | const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(src_start) + size, PageSize); | ||
| 915 | const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(src_start), PageSize); | ||
| 916 | const KProcessAddress mapping_src_end = | ||
| 917 | Common::AlignDown(GetInteger(src_start) + size, PageSize); | ||
| 918 | const size_t aligned_src_size = aligned_src_end - aligned_src_start; | ||
| 919 | const size_t mapping_src_size = | ||
| 920 | (mapping_src_start < mapping_src_end) ? (mapping_src_end - mapping_src_start) : 0; | ||
| 921 | |||
| 922 | // Select a random address to map at. | ||
| 923 | KProcessAddress dst_addr = | ||
| 924 | this->FindFreeArea(region_start, region_size / PageSize, aligned_src_size / PageSize, | ||
| 925 | PageSize, 0, this->GetNumGuardPages()); | ||
| 926 | |||
| 927 | R_UNLESS(dst_addr != 0, ResultOutOfAddressSpace); | ||
| 928 | |||
| 929 | // Check that we can perform the operation we're about to perform. | ||
| 930 | ASSERT(this->CanContain(dst_addr, aligned_src_size, dst_state)); | ||
| 931 | |||
| 932 | // Create an update allocator. | ||
| 933 | Result allocator_result; | ||
| 934 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 935 | m_memory_block_slab_manager); | ||
| 936 | R_TRY(allocator_result); | ||
| 937 | |||
| 938 | // We're going to perform an update, so create a helper. | ||
| 939 | KScopedPageTableUpdater updater(this); | ||
| 940 | |||
| 941 | // Reserve space for any partial pages we allocate. | ||
| 942 | const size_t unmapped_size = aligned_src_size - mapping_src_size; | ||
| 943 | KScopedResourceReservation memory_reservation( | ||
| 944 | m_resource_limit, LimitableResource::PhysicalMemoryMax, unmapped_size); | ||
| 945 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | ||
| 946 | |||
| 947 | // Ensure that we manage page references correctly. | ||
| 948 | KPhysicalAddress start_partial_page = 0; | ||
| 949 | KPhysicalAddress end_partial_page = 0; | ||
| 950 | KProcessAddress cur_mapped_addr = dst_addr; | ||
| 951 | |||
| 952 | // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll | ||
| 953 | // free on scope exit. | ||
| 954 | SCOPE_EXIT({ | ||
| 955 | if (start_partial_page != 0) { | ||
| 956 | m_system.Kernel().MemoryManager().Close(start_partial_page, 1); | ||
| 957 | } | ||
| 958 | if (end_partial_page != 0) { | ||
| 959 | m_system.Kernel().MemoryManager().Close(end_partial_page, 1); | ||
| 960 | } | ||
| 961 | }); | ||
| 962 | |||
| 963 | ON_RESULT_FAILURE { | ||
| 964 | if (cur_mapped_addr != dst_addr) { | ||
| 965 | ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize, | ||
| 966 | KMemoryPermission::None, OperationType::Unmap) | ||
| 967 | .IsSuccess()); | ||
| 968 | } | ||
| 969 | }; | ||
| 970 | |||
| 971 | // Allocate the start page as needed. | ||
| 972 | if (aligned_src_start < mapping_src_start) { | ||
| 973 | start_partial_page = | ||
| 974 | m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option); | ||
| 975 | R_UNLESS(start_partial_page != 0, ResultOutOfMemory); | ||
| 976 | } | ||
| 977 | |||
| 978 | // Allocate the end page as needed. | ||
| 979 | if (mapping_src_end < aligned_src_end && | ||
| 980 | (aligned_src_start < mapping_src_end || aligned_src_start == mapping_src_start)) { | ||
| 981 | end_partial_page = | ||
| 982 | m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option); | ||
| 983 | R_UNLESS(end_partial_page != 0, ResultOutOfMemory); | ||
| 984 | } | ||
| 985 | |||
| 986 | // Get the implementation. | ||
| 987 | auto& src_impl = src_page_table.PageTableImpl(); | ||
| 988 | |||
| 989 | // Get the fill value for partial pages. | ||
| 990 | const auto fill_val = m_ipc_fill_value; | ||
| 991 | |||
| 992 | // Begin traversal. | ||
| 993 | Common::PageTable::TraversalContext context; | ||
| 994 | Common::PageTable::TraversalEntry next_entry; | ||
| 995 | bool traverse_valid = | ||
| 996 | src_impl.BeginTraversal(next_entry, context, GetInteger(aligned_src_start)); | ||
| 997 | ASSERT(traverse_valid); | ||
| 998 | |||
| 999 | // Prepare tracking variables. | ||
| 1000 | KPhysicalAddress cur_block_addr = next_entry.phys_addr; | ||
| 1001 | size_t cur_block_size = | ||
| 1002 | next_entry.block_size - ((cur_block_addr) & (next_entry.block_size - 1)); | ||
| 1003 | size_t tot_block_size = cur_block_size; | ||
| 1004 | |||
| 1005 | // Map the start page, if we have one. | ||
| 1006 | if (start_partial_page != 0) { | ||
| 1007 | // Ensure the page holds correct data. | ||
| 1008 | const KVirtualAddress start_partial_virt = | ||
| 1009 | GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), start_partial_page); | ||
| 1010 | if (send) { | ||
| 1011 | const size_t partial_offset = src_start - aligned_src_start; | ||
| 1012 | size_t copy_size, clear_size; | ||
| 1013 | if (src_end < mapping_src_start) { | ||
| 1014 | copy_size = size; | ||
| 1015 | clear_size = mapping_src_start - src_end; | ||
| 1016 | } else { | ||
| 1017 | copy_size = mapping_src_start - src_start; | ||
| 1018 | clear_size = 0; | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt)), fill_val, | ||
| 1022 | partial_offset); | ||
| 1023 | std::memcpy( | ||
| 1024 | m_memory->GetPointer<void>(GetInteger(start_partial_virt) + partial_offset), | ||
| 1025 | m_memory->GetPointer<void>(GetInteger(GetHeapVirtualAddress( | ||
| 1026 | m_system.Kernel().MemoryLayout(), cur_block_addr)) + | ||
| 1027 | partial_offset), | ||
| 1028 | copy_size); | ||
| 1029 | if (clear_size > 0) { | ||
| 1030 | std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt) + | ||
| 1031 | partial_offset + copy_size), | ||
| 1032 | fill_val, clear_size); | ||
| 1033 | } | ||
| 1034 | } else { | ||
| 1035 | std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt)), fill_val, | ||
| 1036 | PageSize); | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | // Map the page. | ||
| 1040 | R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page)); | ||
| 1041 | |||
| 1042 | // Update tracking extents. | ||
| 1043 | cur_mapped_addr += PageSize; | ||
| 1044 | cur_block_addr += PageSize; | ||
| 1045 | cur_block_size -= PageSize; | ||
| 1046 | |||
| 1047 | // If the block's size was one page, we may need to continue traversal. | ||
| 1048 | if (cur_block_size == 0 && aligned_src_size > PageSize) { | ||
| 1049 | traverse_valid = src_impl.ContinueTraversal(next_entry, context); | ||
| 1050 | ASSERT(traverse_valid); | ||
| 1051 | |||
| 1052 | cur_block_addr = next_entry.phys_addr; | ||
| 1053 | cur_block_size = next_entry.block_size; | ||
| 1054 | tot_block_size += next_entry.block_size; | ||
| 1055 | } | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | // Map the remaining pages. | ||
| 1059 | while (aligned_src_start + tot_block_size < mapping_src_end) { | ||
| 1060 | // Continue the traversal. | ||
| 1061 | traverse_valid = src_impl.ContinueTraversal(next_entry, context); | ||
| 1062 | ASSERT(traverse_valid); | ||
| 1063 | |||
| 1064 | // Process the block. | ||
| 1065 | if (next_entry.phys_addr != cur_block_addr + cur_block_size) { | ||
| 1066 | // Map the block we've been processing so far. | ||
| 1067 | R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map, | ||
| 1068 | cur_block_addr)); | ||
| 1069 | |||
| 1070 | // Update tracking extents. | ||
| 1071 | cur_mapped_addr += cur_block_size; | ||
| 1072 | cur_block_addr = next_entry.phys_addr; | ||
| 1073 | cur_block_size = next_entry.block_size; | ||
| 1074 | } else { | ||
| 1075 | cur_block_size += next_entry.block_size; | ||
| 1076 | } | ||
| 1077 | tot_block_size += next_entry.block_size; | ||
| 1078 | } | ||
| 1079 | |||
| 1080 | // Handle the last direct-mapped page. | ||
| 1081 | if (const KProcessAddress mapped_block_end = | ||
| 1082 | aligned_src_start + tot_block_size - cur_block_size; | ||
| 1083 | mapped_block_end < mapping_src_end) { | ||
| 1084 | const size_t last_block_size = mapping_src_end - mapped_block_end; | ||
| 1085 | |||
| 1086 | // Map the last block. | ||
| 1087 | R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map, | ||
| 1088 | cur_block_addr)); | ||
| 1089 | |||
| 1090 | // Update tracking extents. | ||
| 1091 | cur_mapped_addr += last_block_size; | ||
| 1092 | cur_block_addr += last_block_size; | ||
| 1093 | if (mapped_block_end + cur_block_size < aligned_src_end && | ||
| 1094 | cur_block_size == last_block_size) { | ||
| 1095 | traverse_valid = src_impl.ContinueTraversal(next_entry, context); | ||
| 1096 | ASSERT(traverse_valid); | ||
| 1097 | |||
| 1098 | cur_block_addr = next_entry.phys_addr; | ||
| 1099 | } | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | // Map the end page, if we have one. | ||
| 1103 | if (end_partial_page != 0) { | ||
| 1104 | // Ensure the page holds correct data. | ||
| 1105 | const KVirtualAddress end_partial_virt = | ||
| 1106 | GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), end_partial_page); | ||
| 1107 | if (send) { | ||
| 1108 | const size_t copy_size = src_end - mapping_src_end; | ||
| 1109 | std::memcpy(m_memory->GetPointer<void>(GetInteger(end_partial_virt)), | ||
| 1110 | m_memory->GetPointer<void>(GetInteger(GetHeapVirtualAddress( | ||
| 1111 | m_system.Kernel().MemoryLayout(), cur_block_addr))), | ||
| 1112 | copy_size); | ||
| 1113 | std::memset(m_memory->GetPointer<void>(GetInteger(end_partial_virt) + copy_size), | ||
| 1114 | fill_val, PageSize - copy_size); | ||
| 1115 | } else { | ||
| 1116 | std::memset(m_memory->GetPointer<void>(GetInteger(end_partial_virt)), fill_val, | ||
| 1117 | PageSize); | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | // Map the page. | ||
| 1121 | R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page)); | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | // Update memory blocks to reflect our changes | ||
| 1125 | m_memory_block_manager.Update(std::addressof(allocator), dst_addr, aligned_src_size / PageSize, | ||
| 1126 | dst_state, test_perm, KMemoryAttribute::None, | ||
| 1127 | KMemoryBlockDisableMergeAttribute::Normal, | ||
| 1128 | KMemoryBlockDisableMergeAttribute::None); | ||
| 1129 | |||
| 1130 | // Set the output address. | ||
| 1131 | *out_addr = dst_addr + (src_start - aligned_src_start); | ||
| 1132 | |||
| 1133 | // We succeeded. | ||
| 1134 | memory_reservation.Commit(); | ||
| 1135 | R_SUCCEED(); | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | Result KPageTable::SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr, | ||
| 1139 | KPageTable& src_page_table, KMemoryPermission test_perm, | ||
| 1140 | KMemoryState dst_state, bool send) { | ||
| 1141 | // For convenience, alias this. | ||
| 1142 | KPageTable& dst_page_table = *this; | ||
| 1143 | |||
| 1144 | // Acquire the table locks. | ||
| 1145 | KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); | ||
| 1146 | |||
| 1147 | // We're going to perform an update, so create a helper. | ||
| 1148 | KScopedPageTableUpdater updater(std::addressof(src_page_table)); | ||
| 1149 | |||
| 1150 | // Perform client setup. | ||
| 1151 | size_t num_allocator_blocks; | ||
| 1152 | R_TRY(src_page_table.SetupForIpcClient(updater.GetPageList(), | ||
| 1153 | std::addressof(num_allocator_blocks), src_addr, size, | ||
| 1154 | test_perm, dst_state)); | ||
| 1155 | |||
| 1156 | // Create an update allocator. | ||
| 1157 | Result allocator_result; | ||
| 1158 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 1159 | src_page_table.m_memory_block_slab_manager, | ||
| 1160 | num_allocator_blocks); | ||
| 1161 | R_TRY(allocator_result); | ||
| 1162 | |||
| 1163 | // Get the mapped extents. | ||
| 1164 | const KProcessAddress src_map_start = Common::AlignUp(GetInteger(src_addr), PageSize); | ||
| 1165 | const KProcessAddress src_map_end = Common::AlignDown(GetInteger(src_addr) + size, PageSize); | ||
| 1166 | const size_t src_map_size = src_map_end - src_map_start; | ||
| 1167 | |||
| 1168 | // Ensure that we clean up appropriately if we fail after this. | ||
| 1169 | const auto src_perm = (test_perm == KMemoryPermission::UserReadWrite) | ||
| 1170 | ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped | ||
| 1171 | : KMemoryPermission::UserRead; | ||
| 1172 | ON_RESULT_FAILURE { | ||
| 1173 | if (src_map_end > src_map_start) { | ||
| 1174 | src_page_table.CleanupForIpcClientOnServerSetupFailure( | ||
| 1175 | updater.GetPageList(), src_map_start, src_map_size, src_perm); | ||
| 1176 | } | ||
| 1177 | }; | ||
| 1178 | |||
| 1179 | // Perform server setup. | ||
| 1180 | R_TRY(dst_page_table.SetupForIpcServer(out_dst_addr, size, src_addr, test_perm, dst_state, | ||
| 1181 | src_page_table, send)); | ||
| 1182 | |||
| 1183 | // If anything was mapped, ipc-lock the pages. | ||
| 1184 | if (src_map_start < src_map_end) { | ||
| 1185 | // Get the source permission. | ||
| 1186 | src_page_table.m_memory_block_manager.UpdateLock(std::addressof(allocator), src_map_start, | ||
| 1187 | (src_map_end - src_map_start) / PageSize, | ||
| 1188 | &KMemoryBlock::LockForIpc, src_perm); | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | R_SUCCEED(); | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | Result KPageTable::CleanupForIpcServer(KProcessAddress address, size_t size, | ||
| 1195 | KMemoryState dst_state) { | ||
| 1196 | // Validate the address. | ||
| 1197 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 1198 | |||
| 1199 | // Lock the table. | ||
| 1200 | KScopedLightLock lk(m_general_lock); | ||
| 1201 | |||
| 1202 | // Validate the memory state. | ||
| 1203 | size_t num_allocator_blocks; | ||
| 1204 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | ||
| 1205 | KMemoryState::All, dst_state, KMemoryPermission::UserRead, | ||
| 1206 | KMemoryPermission::UserRead, KMemoryAttribute::All, | ||
| 1207 | KMemoryAttribute::None)); | ||
| 1208 | |||
| 1209 | // Create an update allocator. | ||
| 1210 | Result allocator_result; | ||
| 1211 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 1212 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 1213 | R_TRY(allocator_result); | ||
| 1214 | |||
| 1215 | // We're going to perform an update, so create a helper. | ||
| 1216 | KScopedPageTableUpdater updater(this); | ||
| 1217 | |||
| 1218 | // Get aligned extents. | ||
| 1219 | const KProcessAddress aligned_start = Common::AlignDown(GetInteger(address), PageSize); | ||
| 1220 | const KProcessAddress aligned_end = Common::AlignUp(GetInteger(address) + size, PageSize); | ||
| 1221 | const size_t aligned_size = aligned_end - aligned_start; | ||
| 1222 | const size_t aligned_num_pages = aligned_size / PageSize; | ||
| 1223 | |||
| 1224 | // Unmap the pages. | ||
| 1225 | R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap)); | ||
| 1226 | |||
| 1227 | // Update memory blocks. | ||
| 1228 | m_memory_block_manager.Update(std::addressof(allocator), aligned_start, aligned_num_pages, | ||
| 1229 | KMemoryState::None, KMemoryPermission::None, | ||
| 1230 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | ||
| 1231 | KMemoryBlockDisableMergeAttribute::Normal); | ||
| 1232 | |||
| 1233 | // Release from the resource limit as relevant. | ||
| 1234 | const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize); | ||
| 1235 | const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize); | ||
| 1236 | const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; | ||
| 1237 | m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size); | ||
| 1238 | |||
| 1239 | R_SUCCEED(); | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | Result KPageTable::CleanupForIpcClient(KProcessAddress address, size_t size, | ||
| 1243 | KMemoryState dst_state) { | ||
| 1244 | // Validate the address. | ||
| 1245 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 1246 | |||
| 1247 | // Get aligned source extents. | ||
| 1248 | const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize); | ||
| 1249 | const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize); | ||
| 1250 | const KProcessAddress mapping_last = mapping_end - 1; | ||
| 1251 | const size_t mapping_size = (mapping_start < mapping_end) ? (mapping_end - mapping_start) : 0; | ||
| 1252 | |||
| 1253 | // If nothing was mapped, we're actually done immediately. | ||
| 1254 | R_SUCCEED_IF(mapping_size == 0); | ||
| 1255 | |||
| 1256 | // Get the test state and attribute mask. | ||
| 1257 | KMemoryState test_state; | ||
| 1258 | KMemoryAttribute test_attr_mask; | ||
| 1259 | switch (dst_state) { | ||
| 1260 | case KMemoryState::Ipc: | ||
| 1261 | test_state = KMemoryState::FlagCanUseIpc; | ||
| 1262 | test_attr_mask = | ||
| 1263 | KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked; | ||
| 1264 | break; | ||
| 1265 | case KMemoryState::NonSecureIpc: | ||
| 1266 | test_state = KMemoryState::FlagCanUseNonSecureIpc; | ||
| 1267 | test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; | ||
| 1268 | break; | ||
| 1269 | case KMemoryState::NonDeviceIpc: | ||
| 1270 | test_state = KMemoryState::FlagCanUseNonDeviceIpc; | ||
| 1271 | test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; | ||
| 1272 | break; | ||
| 1273 | default: | ||
| 1274 | R_THROW(ResultInvalidCombination); | ||
| 1275 | } | ||
| 1276 | |||
| 1277 | // Lock the table. | ||
| 1278 | // NOTE: Nintendo does this *after* creating the updater below, but this does not follow | ||
| 1279 | // convention elsewhere in KPageTable. | ||
| 1280 | KScopedLightLock lk(m_general_lock); | ||
| 1281 | |||
| 1282 | // We're going to perform an update, so create a helper. | ||
| 1283 | KScopedPageTableUpdater updater(this); | ||
| 1284 | |||
| 1285 | // Ensure that on failure, we roll back appropriately. | ||
| 1286 | size_t mapped_size = 0; | ||
| 1287 | ON_RESULT_FAILURE { | ||
| 1288 | if (mapped_size > 0) { | ||
| 1289 | // Determine where the mapping ends. | ||
| 1290 | const auto mapped_end = (mapping_start) + mapped_size; | ||
| 1291 | const auto mapped_last = mapped_end - 1; | ||
| 1292 | |||
| 1293 | // Get current and next iterators. | ||
| 1294 | KMemoryBlockManager::const_iterator start_it = | ||
| 1295 | m_memory_block_manager.FindIterator(mapping_start); | ||
| 1296 | KMemoryBlockManager::const_iterator next_it = start_it; | ||
| 1297 | ++next_it; | ||
| 1298 | |||
| 1299 | // Get the current block info. | ||
| 1300 | KMemoryInfo cur_info = start_it->GetMemoryInfo(); | ||
| 1301 | |||
| 1302 | // Create tracking variables. | ||
| 1303 | KProcessAddress cur_address = cur_info.GetAddress(); | ||
| 1304 | size_t cur_size = cur_info.GetSize(); | ||
| 1305 | bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission(); | ||
| 1306 | bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1; | ||
| 1307 | bool first = | ||
| 1308 | cur_info.GetIpcDisableMergeCount() == 1 && | ||
| 1309 | (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked) == | ||
| 1310 | KMemoryBlockDisableMergeAttribute::None; | ||
| 1311 | |||
| 1312 | while (((cur_address) + cur_size - 1) < mapped_last) { | ||
| 1313 | // Check that we have a next block. | ||
| 1314 | ASSERT(next_it != m_memory_block_manager.end()); | ||
| 1315 | |||
| 1316 | // Get the next info. | ||
| 1317 | const KMemoryInfo next_info = next_it->GetMemoryInfo(); | ||
| 1318 | |||
| 1319 | // Check if we can consolidate the next block's permission set with the current one. | ||
| 1320 | |||
| 1321 | const bool next_perm_eq = | ||
| 1322 | next_info.GetPermission() == next_info.GetOriginalPermission(); | ||
| 1323 | const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1; | ||
| 1324 | if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm && | ||
| 1325 | cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) { | ||
| 1326 | // We can consolidate the reprotection for the current and next block into a | ||
| 1327 | // single call. | ||
| 1328 | cur_size += next_info.GetSize(); | ||
| 1329 | } else { | ||
| 1330 | // We have to operate on the current block. | ||
| 1331 | if ((cur_needs_set_perm || first) && !cur_perm_eq) { | ||
| 1332 | ASSERT(Operate(cur_address, cur_size / PageSize, cur_info.GetPermission(), | ||
| 1333 | OperationType::ChangePermissions) | ||
| 1334 | .IsSuccess()); | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | // Advance. | ||
| 1338 | cur_address = next_info.GetAddress(); | ||
| 1339 | cur_size = next_info.GetSize(); | ||
| 1340 | first = false; | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | // Advance. | ||
| 1344 | cur_info = next_info; | ||
| 1345 | cur_perm_eq = next_perm_eq; | ||
| 1346 | cur_needs_set_perm = next_needs_set_perm; | ||
| 1347 | ++next_it; | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | // Process the last block. | ||
| 1351 | if ((first || cur_needs_set_perm) && !cur_perm_eq) { | ||
| 1352 | ASSERT(Operate(cur_address, cur_size / PageSize, cur_info.GetPermission(), | ||
| 1353 | OperationType::ChangePermissions) | ||
| 1354 | .IsSuccess()); | ||
| 1355 | } | ||
| 1356 | } | ||
| 1357 | }; | ||
| 1358 | |||
| 1359 | // Iterate, reprotecting as needed. | ||
| 1360 | { | ||
| 1361 | // Get current and next iterators. | ||
| 1362 | KMemoryBlockManager::const_iterator start_it = | ||
| 1363 | m_memory_block_manager.FindIterator(mapping_start); | ||
| 1364 | KMemoryBlockManager::const_iterator next_it = start_it; | ||
| 1365 | ++next_it; | ||
| 1366 | |||
| 1367 | // Validate the current block. | ||
| 1368 | KMemoryInfo cur_info = start_it->GetMemoryInfo(); | ||
| 1369 | ASSERT(this->CheckMemoryState(cur_info, test_state, test_state, KMemoryPermission::None, | ||
| 1370 | KMemoryPermission::None, | ||
| 1371 | test_attr_mask | KMemoryAttribute::IpcLocked, | ||
| 1372 | KMemoryAttribute::IpcLocked) | ||
| 1373 | .IsSuccess()); | ||
| 1374 | |||
| 1375 | // Create tracking variables. | ||
| 1376 | KProcessAddress cur_address = cur_info.GetAddress(); | ||
| 1377 | size_t cur_size = cur_info.GetSize(); | ||
| 1378 | bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission(); | ||
| 1379 | bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1; | ||
| 1380 | bool first = | ||
| 1381 | cur_info.GetIpcDisableMergeCount() == 1 && | ||
| 1382 | (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked) == | ||
| 1383 | KMemoryBlockDisableMergeAttribute::None; | ||
| 1384 | |||
| 1385 | while ((cur_address + cur_size - 1) < mapping_last) { | ||
| 1386 | // Check that we have a next block. | ||
| 1387 | ASSERT(next_it != m_memory_block_manager.end()); | ||
| 1388 | |||
| 1389 | // Get the next info. | ||
| 1390 | const KMemoryInfo next_info = next_it->GetMemoryInfo(); | ||
| 1391 | |||
| 1392 | // Validate the next block. | ||
| 1393 | ASSERT(this->CheckMemoryState(next_info, test_state, test_state, | ||
| 1394 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 1395 | test_attr_mask | KMemoryAttribute::IpcLocked, | ||
| 1396 | KMemoryAttribute::IpcLocked) | ||
| 1397 | .IsSuccess()); | ||
| 1398 | |||
| 1399 | // Check if we can consolidate the next block's permission set with the current one. | ||
| 1400 | const bool next_perm_eq = | ||
| 1401 | next_info.GetPermission() == next_info.GetOriginalPermission(); | ||
| 1402 | const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1; | ||
| 1403 | if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm && | ||
| 1404 | cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) { | ||
| 1405 | // We can consolidate the reprotection for the current and next block into a single | ||
| 1406 | // call. | ||
| 1407 | cur_size += next_info.GetSize(); | ||
| 1408 | } else { | ||
| 1409 | // We have to operate on the current block. | ||
| 1410 | if ((cur_needs_set_perm || first) && !cur_perm_eq) { | ||
| 1411 | R_TRY(Operate(cur_address, cur_size / PageSize, | ||
| 1412 | cur_needs_set_perm ? cur_info.GetOriginalPermission() | ||
| 1413 | : cur_info.GetPermission(), | ||
| 1414 | OperationType::ChangePermissions)); | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | // Mark that we mapped the block. | ||
| 1418 | mapped_size += cur_size; | ||
| 1419 | |||
| 1420 | // Advance. | ||
| 1421 | cur_address = next_info.GetAddress(); | ||
| 1422 | cur_size = next_info.GetSize(); | ||
| 1423 | first = false; | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | // Advance. | ||
| 1427 | cur_info = next_info; | ||
| 1428 | cur_perm_eq = next_perm_eq; | ||
| 1429 | cur_needs_set_perm = next_needs_set_perm; | ||
| 1430 | ++next_it; | ||
| 1431 | } | ||
| 1432 | |||
| 1433 | // Process the last block. | ||
| 1434 | const auto lock_count = | ||
| 1435 | cur_info.GetIpcLockCount() + | ||
| 1436 | (next_it != m_memory_block_manager.end() | ||
| 1437 | ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount()) | ||
| 1438 | : 0); | ||
| 1439 | if ((first || cur_needs_set_perm || (lock_count == 1)) && !cur_perm_eq) { | ||
| 1440 | R_TRY(Operate(cur_address, cur_size / PageSize, | ||
| 1441 | cur_needs_set_perm ? cur_info.GetOriginalPermission() | ||
| 1442 | : cur_info.GetPermission(), | ||
| 1443 | OperationType::ChangePermissions)); | ||
| 1444 | } | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | // Create an update allocator. | ||
| 1448 | // NOTE: Guaranteed zero blocks needed here. | ||
| 1449 | Result allocator_result; | ||
| 1450 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 1451 | m_memory_block_slab_manager, 0); | ||
| 1452 | R_TRY(allocator_result); | ||
| 1453 | |||
| 1454 | // Unlock the pages. | ||
| 1455 | m_memory_block_manager.UpdateLock(std::addressof(allocator), mapping_start, | ||
| 1456 | mapping_size / PageSize, &KMemoryBlock::UnlockForIpc, | ||
| 1457 | KMemoryPermission::None); | ||
| 1458 | |||
| 1459 | R_SUCCEED(); | ||
| 1460 | } | ||
| 1461 | |||
| 1462 | void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLinkedList* page_list, | ||
| 1463 | KProcessAddress address, size_t size, | ||
| 1464 | KMemoryPermission prot_perm) { | ||
| 1465 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 1466 | ASSERT(Common::IsAligned(GetInteger(address), PageSize)); | ||
| 1467 | ASSERT(Common::IsAligned(size, PageSize)); | ||
| 1468 | |||
| 1469 | // Get the mapped extents. | ||
| 1470 | const KProcessAddress src_map_start = address; | ||
| 1471 | const KProcessAddress src_map_end = address + size; | ||
| 1472 | const KProcessAddress src_map_last = src_map_end - 1; | ||
| 1473 | |||
| 1474 | // This function is only invoked when there's something to do. | ||
| 1475 | ASSERT(src_map_end > src_map_start); | ||
| 1476 | |||
| 1477 | // Iterate over blocks, fixing permissions. | ||
| 1478 | KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(address); | ||
| 1479 | while (true) { | ||
| 1480 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1481 | |||
| 1482 | const auto cur_start = info.GetAddress() >= GetInteger(src_map_start) | ||
| 1483 | ? info.GetAddress() | ||
| 1484 | : GetInteger(src_map_start); | ||
| 1485 | const auto cur_end = | ||
| 1486 | src_map_last <= info.GetLastAddress() ? src_map_end : info.GetEndAddress(); | ||
| 1487 | |||
| 1488 | // If we can, fix the protections on the block. | ||
| 1489 | if ((info.GetIpcLockCount() == 0 && | ||
| 1490 | (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) || | ||
| 1491 | (info.GetIpcLockCount() != 0 && | ||
| 1492 | (info.GetOriginalPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm)) { | ||
| 1493 | // Check if we actually need to fix the protections on the block. | ||
| 1494 | if (cur_end == src_map_end || info.GetAddress() <= GetInteger(src_map_start) || | ||
| 1495 | (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) { | ||
| 1496 | ASSERT(Operate(cur_start, (cur_end - cur_start) / PageSize, info.GetPermission(), | ||
| 1497 | OperationType::ChangePermissions) | ||
| 1498 | .IsSuccess()); | ||
| 1499 | } | ||
| 1500 | } | ||
| 1501 | |||
| 1502 | // If we're past the end of the region, we're done. | ||
| 1503 | if (src_map_last <= info.GetLastAddress()) { | ||
| 1504 | break; | ||
| 1505 | } | ||
| 1506 | |||
| 1507 | // Advance. | ||
| 1508 | ++it; | ||
| 1509 | ASSERT(it != m_memory_block_manager.end()); | ||
| 1510 | } | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) { | ||
| 1514 | // Lock the physical memory lock. | ||
| 1515 | KScopedLightLock phys_lk(m_map_physical_memory_lock); | ||
| 1516 | |||
| 1517 | // Calculate the last address for convenience. | ||
| 1518 | const KProcessAddress last_address = address + size - 1; | ||
| 1519 | |||
| 1520 | // Define iteration variables. | ||
| 1521 | KProcessAddress cur_address; | ||
| 1522 | size_t mapped_size; | ||
| 1523 | |||
| 1524 | // The entire mapping process can be retried. | ||
| 1525 | while (true) { | ||
| 1526 | // Check if the memory is already mapped. | ||
| 1527 | { | ||
| 1528 | // Lock the table. | ||
| 1529 | KScopedLightLock lk(m_general_lock); | ||
| 1530 | |||
| 1531 | // Iterate over the memory. | ||
| 1532 | cur_address = address; | ||
| 1533 | mapped_size = 0; | ||
| 1534 | |||
| 1535 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 1536 | while (true) { | ||
| 1537 | // Check that the iterator is valid. | ||
| 1538 | ASSERT(it != m_memory_block_manager.end()); | ||
| 1539 | |||
| 1540 | // Get the memory info. | ||
| 1541 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1542 | |||
| 1543 | // Check if we're done. | ||
| 1544 | if (last_address <= info.GetLastAddress()) { | ||
| 1545 | if (info.GetState() != KMemoryState::Free) { | ||
| 1546 | mapped_size += (last_address + 1 - cur_address); | ||
| 1547 | } | ||
| 1548 | break; | ||
| 1549 | } | ||
| 1550 | |||
| 1551 | // Track the memory if it's mapped. | ||
| 1552 | if (info.GetState() != KMemoryState::Free) { | ||
| 1553 | mapped_size += KProcessAddress(info.GetEndAddress()) - cur_address; | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | // Advance. | ||
| 1557 | cur_address = info.GetEndAddress(); | ||
| 1558 | ++it; | ||
| 1559 | } | ||
| 1560 | |||
| 1561 | // If the size mapped is the size requested, we've nothing to do. | ||
| 1562 | R_SUCCEED_IF(size == mapped_size); | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | // Allocate and map the memory. | ||
| 1566 | { | ||
| 1567 | // Reserve the memory from the process resource limit. | ||
| 1568 | KScopedResourceReservation memory_reservation( | ||
| 1569 | m_resource_limit, LimitableResource::PhysicalMemoryMax, size - mapped_size); | ||
| 1570 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | ||
| 1571 | |||
| 1572 | // Allocate pages for the new memory. | ||
| 1573 | KPageGroup pg{m_kernel, m_block_info_manager}; | ||
| 1574 | R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess( | ||
| 1575 | &pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0)); | ||
| 1576 | |||
| 1577 | // If we fail in the next bit (or retry), we need to cleanup the pages. | ||
| 1578 | // auto pg_guard = SCOPE_GUARD { | ||
| 1579 | // pg.OpenFirst(); | ||
| 1580 | // pg.Close(); | ||
| 1581 | //}; | ||
| 1582 | |||
| 1583 | // Map the memory. | ||
| 1584 | { | ||
| 1585 | // Lock the table. | ||
| 1586 | KScopedLightLock lk(m_general_lock); | ||
| 1587 | |||
| 1588 | size_t num_allocator_blocks = 0; | ||
| 1589 | |||
| 1590 | // Verify that nobody has mapped memory since we first checked. | ||
| 1591 | { | ||
| 1592 | // Iterate over the memory. | ||
| 1593 | size_t checked_mapped_size = 0; | ||
| 1594 | cur_address = address; | ||
| 1595 | |||
| 1596 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 1597 | while (true) { | ||
| 1598 | // Check that the iterator is valid. | ||
| 1599 | ASSERT(it != m_memory_block_manager.end()); | ||
| 1600 | |||
| 1601 | // Get the memory info. | ||
| 1602 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1603 | |||
| 1604 | const bool is_free = info.GetState() == KMemoryState::Free; | ||
| 1605 | if (is_free) { | ||
| 1606 | if (info.GetAddress() < GetInteger(address)) { | ||
| 1607 | ++num_allocator_blocks; | ||
| 1608 | } | ||
| 1609 | if (last_address < info.GetLastAddress()) { | ||
| 1610 | ++num_allocator_blocks; | ||
| 1611 | } | ||
| 1612 | } | ||
| 1613 | |||
| 1614 | // Check if we're done. | ||
| 1615 | if (last_address <= info.GetLastAddress()) { | ||
| 1616 | if (!is_free) { | ||
| 1617 | checked_mapped_size += (last_address + 1 - cur_address); | ||
| 1618 | } | ||
| 1619 | break; | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | // Track the memory if it's mapped. | ||
| 1623 | if (!is_free) { | ||
| 1624 | checked_mapped_size += | ||
| 1625 | KProcessAddress(info.GetEndAddress()) - cur_address; | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | // Advance. | ||
| 1629 | cur_address = info.GetEndAddress(); | ||
| 1630 | ++it; | ||
| 1631 | } | ||
| 1632 | |||
| 1633 | // If the size now isn't what it was before, somebody mapped or unmapped | ||
| 1634 | // concurrently. If this happened, retry. | ||
| 1635 | if (mapped_size != checked_mapped_size) { | ||
| 1636 | continue; | ||
| 1637 | } | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | // Create an update allocator. | ||
| 1641 | ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); | ||
| 1642 | Result allocator_result; | ||
| 1643 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 1644 | m_memory_block_slab_manager, | ||
| 1645 | num_allocator_blocks); | ||
| 1646 | R_TRY(allocator_result); | ||
| 1647 | |||
| 1648 | // We're going to perform an update, so create a helper. | ||
| 1649 | KScopedPageTableUpdater updater(this); | ||
| 1650 | |||
| 1651 | // Prepare to iterate over the memory. | ||
| 1652 | auto pg_it = pg.begin(); | ||
| 1653 | KPhysicalAddress pg_phys_addr = pg_it->GetAddress(); | ||
| 1654 | size_t pg_pages = pg_it->GetNumPages(); | ||
| 1655 | |||
| 1656 | // Reset the current tracking address, and make sure we clean up on failure. | ||
| 1657 | // pg_guard.Cancel(); | ||
| 1658 | cur_address = address; | ||
| 1659 | ON_RESULT_FAILURE { | ||
| 1660 | if (cur_address > address) { | ||
| 1661 | const KProcessAddress last_unmap_address = cur_address - 1; | ||
| 1662 | |||
| 1663 | // Iterate, unmapping the pages. | ||
| 1664 | cur_address = address; | ||
| 1665 | |||
| 1666 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 1667 | while (true) { | ||
| 1668 | // Check that the iterator is valid. | ||
| 1669 | ASSERT(it != m_memory_block_manager.end()); | ||
| 1670 | |||
| 1671 | // Get the memory info. | ||
| 1672 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1673 | |||
| 1674 | // If the memory state is free, we mapped it and need to unmap it. | ||
| 1675 | if (info.GetState() == KMemoryState::Free) { | ||
| 1676 | // Determine the range to unmap. | ||
| 1677 | const size_t cur_pages = | ||
| 1678 | std::min(KProcessAddress(info.GetEndAddress()) - cur_address, | ||
| 1679 | last_unmap_address + 1 - cur_address) / | ||
| 1680 | PageSize; | ||
| 1681 | |||
| 1682 | // Unmap. | ||
| 1683 | ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, | ||
| 1684 | OperationType::Unmap) | ||
| 1685 | .IsSuccess()); | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | // Check if we're done. | ||
| 1689 | if (last_unmap_address <= info.GetLastAddress()) { | ||
| 1690 | break; | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | // Advance. | ||
| 1694 | cur_address = info.GetEndAddress(); | ||
| 1695 | ++it; | ||
| 1696 | } | ||
| 1697 | } | ||
| 1698 | |||
| 1699 | // Release any remaining unmapped memory. | ||
| 1700 | m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages); | ||
| 1701 | m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages); | ||
| 1702 | for (++pg_it; pg_it != pg.end(); ++pg_it) { | ||
| 1703 | m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(), | ||
| 1704 | pg_it->GetNumPages()); | ||
| 1705 | m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(), | ||
| 1706 | pg_it->GetNumPages()); | ||
| 1707 | } | ||
| 1708 | }; | ||
| 1709 | |||
| 1710 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 1711 | while (true) { | ||
| 1712 | // Check that the iterator is valid. | ||
| 1713 | ASSERT(it != m_memory_block_manager.end()); | ||
| 1714 | |||
| 1715 | // Get the memory info. | ||
| 1716 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1717 | |||
| 1718 | // If it's unmapped, we need to map it. | ||
| 1719 | if (info.GetState() == KMemoryState::Free) { | ||
| 1720 | // Determine the range to map. | ||
| 1721 | size_t map_pages = | ||
| 1722 | std::min(KProcessAddress(info.GetEndAddress()) - cur_address, | ||
| 1723 | last_address + 1 - cur_address) / | ||
| 1724 | PageSize; | ||
| 1725 | |||
| 1726 | // While we have pages to map, map them. | ||
| 1727 | { | ||
| 1728 | // Create a page group for the current mapping range. | ||
| 1729 | KPageGroup cur_pg(m_kernel, m_block_info_manager); | ||
| 1730 | { | ||
| 1731 | ON_RESULT_FAILURE_2 { | ||
| 1732 | cur_pg.OpenFirst(); | ||
| 1733 | cur_pg.Close(); | ||
| 1734 | }; | ||
| 1735 | |||
| 1736 | size_t remain_pages = map_pages; | ||
| 1737 | while (remain_pages > 0) { | ||
| 1738 | // Check if we're at the end of the physical block. | ||
| 1739 | if (pg_pages == 0) { | ||
| 1740 | // Ensure there are more pages to map. | ||
| 1741 | ASSERT(pg_it != pg.end()); | ||
| 1742 | |||
| 1743 | // Advance our physical block. | ||
| 1744 | ++pg_it; | ||
| 1745 | pg_phys_addr = pg_it->GetAddress(); | ||
| 1746 | pg_pages = pg_it->GetNumPages(); | ||
| 1747 | } | ||
| 1748 | |||
| 1749 | // Add whatever we can to the current block. | ||
| 1750 | const size_t cur_pages = std::min(pg_pages, remain_pages); | ||
| 1751 | R_TRY(cur_pg.AddBlock(pg_phys_addr + | ||
| 1752 | ((pg_pages - cur_pages) * PageSize), | ||
| 1753 | cur_pages)); | ||
| 1754 | |||
| 1755 | // Advance. | ||
| 1756 | remain_pages -= cur_pages; | ||
| 1757 | pg_pages -= cur_pages; | ||
| 1758 | } | ||
| 1759 | } | ||
| 1760 | |||
| 1761 | // Map the pages. | ||
| 1762 | R_TRY(this->Operate(cur_address, map_pages, cur_pg, | ||
| 1763 | OperationType::MapFirstGroup)); | ||
| 1764 | } | ||
| 1765 | } | ||
| 1766 | |||
| 1767 | // Check if we're done. | ||
| 1768 | if (last_address <= info.GetLastAddress()) { | ||
| 1769 | break; | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | // Advance. | ||
| 1773 | cur_address = info.GetEndAddress(); | ||
| 1774 | ++it; | ||
| 1775 | } | ||
| 1776 | |||
| 1777 | // We succeeded, so commit the memory reservation. | ||
| 1778 | memory_reservation.Commit(); | ||
| 1779 | |||
| 1780 | // Increase our tracked mapped size. | ||
| 1781 | m_mapped_physical_memory_size += (size - mapped_size); | ||
| 1782 | |||
| 1783 | // Update the relevant memory blocks. | ||
| 1784 | m_memory_block_manager.UpdateIfMatch( | ||
| 1785 | std::addressof(allocator), address, size / PageSize, KMemoryState::Free, | ||
| 1786 | KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal, | ||
| 1787 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 1788 | address == this->GetAliasRegionStart() | ||
| 1789 | ? KMemoryBlockDisableMergeAttribute::Normal | ||
| 1790 | : KMemoryBlockDisableMergeAttribute::None, | ||
| 1791 | KMemoryBlockDisableMergeAttribute::None); | ||
| 1792 | |||
| 1793 | R_SUCCEED(); | ||
| 1794 | } | ||
| 1795 | } | ||
| 1796 | } | ||
| 1797 | } | ||
| 1798 | |||
| 1799 | Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) { | ||
| 1800 | // Lock the physical memory lock. | ||
| 1801 | KScopedLightLock phys_lk(m_map_physical_memory_lock); | ||
| 1802 | |||
| 1803 | // Lock the table. | ||
| 1804 | KScopedLightLock lk(m_general_lock); | ||
| 1805 | |||
| 1806 | // Calculate the last address for convenience. | ||
| 1807 | const KProcessAddress last_address = address + size - 1; | ||
| 1808 | |||
| 1809 | // Define iteration variables. | ||
| 1810 | KProcessAddress map_start_address = 0; | ||
| 1811 | KProcessAddress map_last_address = 0; | ||
| 1812 | |||
| 1813 | KProcessAddress cur_address; | ||
| 1814 | size_t mapped_size; | ||
| 1815 | size_t num_allocator_blocks = 0; | ||
| 1816 | |||
| 1817 | // Check if the memory is mapped. | ||
| 1818 | { | ||
| 1819 | // Iterate over the memory. | ||
| 1820 | cur_address = address; | ||
| 1821 | mapped_size = 0; | ||
| 1822 | |||
| 1823 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 1824 | while (true) { | ||
| 1825 | // Check that the iterator is valid. | ||
| 1826 | ASSERT(it != m_memory_block_manager.end()); | ||
| 1827 | |||
| 1828 | // Get the memory info. | ||
| 1829 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1830 | |||
| 1831 | // Verify the memory's state. | ||
| 1832 | const bool is_normal = info.GetState() == KMemoryState::Normal && | ||
| 1833 | info.GetAttribute() == KMemoryAttribute::None; | ||
| 1834 | const bool is_free = info.GetState() == KMemoryState::Free; | ||
| 1835 | R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory); | ||
| 1836 | |||
| 1837 | if (is_normal) { | ||
| 1838 | R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory); | ||
| 1839 | |||
| 1840 | if (map_start_address == 0) { | ||
| 1841 | map_start_address = cur_address; | ||
| 1842 | } | ||
| 1843 | map_last_address = | ||
| 1844 | (last_address >= info.GetLastAddress()) ? info.GetLastAddress() : last_address; | ||
| 1845 | |||
| 1846 | if (info.GetAddress() < GetInteger(address)) { | ||
| 1847 | ++num_allocator_blocks; | ||
| 1848 | } | ||
| 1849 | if (last_address < info.GetLastAddress()) { | ||
| 1850 | ++num_allocator_blocks; | ||
| 1851 | } | ||
| 1852 | |||
| 1853 | mapped_size += (map_last_address + 1 - cur_address); | ||
| 1854 | } | ||
| 1855 | |||
| 1856 | // Check if we're done. | ||
| 1857 | if (last_address <= info.GetLastAddress()) { | ||
| 1858 | break; | ||
| 1859 | } | ||
| 1860 | |||
| 1861 | // Advance. | ||
| 1862 | cur_address = info.GetEndAddress(); | ||
| 1863 | ++it; | ||
| 1864 | } | ||
| 1865 | |||
| 1866 | // If there's nothing mapped, we've nothing to do. | ||
| 1867 | R_SUCCEED_IF(mapped_size == 0); | ||
| 1868 | } | ||
| 1869 | |||
| 1870 | // Create an update allocator. | ||
| 1871 | ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); | ||
| 1872 | Result allocator_result; | ||
| 1873 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 1874 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 1875 | R_TRY(allocator_result); | ||
| 1876 | |||
| 1877 | // We're going to perform an update, so create a helper. | ||
| 1878 | KScopedPageTableUpdater updater(this); | ||
| 1879 | |||
| 1880 | // Separate the mapping. | ||
| 1881 | R_TRY(Operate(map_start_address, (map_last_address + 1 - map_start_address) / PageSize, | ||
| 1882 | KMemoryPermission::None, OperationType::Separate)); | ||
| 1883 | |||
| 1884 | // Reset the current tracking address, and make sure we clean up on failure. | ||
| 1885 | cur_address = address; | ||
| 1886 | |||
| 1887 | // Iterate over the memory, unmapping as we go. | ||
| 1888 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 1889 | |||
| 1890 | const auto clear_merge_attr = | ||
| 1891 | (it->GetState() == KMemoryState::Normal && | ||
| 1892 | it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address) | ||
| 1893 | ? KMemoryBlockDisableMergeAttribute::Normal | ||
| 1894 | : KMemoryBlockDisableMergeAttribute::None; | ||
| 1895 | |||
| 1896 | while (true) { | ||
| 1897 | // Check that the iterator is valid. | ||
| 1898 | ASSERT(it != m_memory_block_manager.end()); | ||
| 1899 | |||
| 1900 | // Get the memory info. | ||
| 1901 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1902 | |||
| 1903 | // If the memory state is normal, we need to unmap it. | ||
| 1904 | if (info.GetState() == KMemoryState::Normal) { | ||
| 1905 | // Determine the range to unmap. | ||
| 1906 | const size_t cur_pages = std::min(KProcessAddress(info.GetEndAddress()) - cur_address, | ||
| 1907 | last_address + 1 - cur_address) / | ||
| 1908 | PageSize; | ||
| 1909 | |||
| 1910 | // Unmap. | ||
| 1911 | ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap) | ||
| 1912 | .IsSuccess()); | ||
| 1913 | } | ||
| 1914 | |||
| 1915 | // Check if we're done. | ||
| 1916 | if (last_address <= info.GetLastAddress()) { | ||
| 1917 | break; | ||
| 1918 | } | ||
| 1919 | |||
| 1920 | // Advance. | ||
| 1921 | cur_address = info.GetEndAddress(); | ||
| 1922 | ++it; | ||
| 1923 | } | ||
| 1924 | |||
| 1925 | // Release the memory resource. | ||
| 1926 | m_mapped_physical_memory_size -= mapped_size; | ||
| 1927 | m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, mapped_size); | ||
| 1928 | |||
| 1929 | // Update memory blocks. | ||
| 1930 | m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, | ||
| 1931 | KMemoryState::Free, KMemoryPermission::None, | ||
| 1932 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | ||
| 1933 | clear_merge_attr); | ||
| 1934 | |||
| 1935 | // We succeeded. | ||
| 1936 | R_SUCCEED(); | ||
| 1937 | } | ||
| 1938 | |||
| 1939 | Result KPageTable::MapMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 1940 | size_t size) { | ||
| 1941 | // Lock the table. | ||
| 1942 | KScopedLightLock lk(m_general_lock); | ||
| 1943 | |||
| 1944 | // Validate that the source address's state is valid. | ||
| 1945 | KMemoryState src_state; | ||
| 1946 | size_t num_src_allocator_blocks; | ||
| 1947 | R_TRY(this->CheckMemoryState(std::addressof(src_state), nullptr, nullptr, | ||
| 1948 | std::addressof(num_src_allocator_blocks), src_address, size, | ||
| 1949 | KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias, | ||
| 1950 | KMemoryPermission::All, KMemoryPermission::UserReadWrite, | ||
| 1951 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 1952 | |||
| 1953 | // Validate that the dst address's state is valid. | ||
| 1954 | size_t num_dst_allocator_blocks; | ||
| 1955 | R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size, | ||
| 1956 | KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, | ||
| 1957 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 1958 | KMemoryAttribute::None)); | ||
| 1959 | |||
| 1960 | // Create an update allocator for the source. | ||
| 1961 | Result src_allocator_result; | ||
| 1962 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), | ||
| 1963 | m_memory_block_slab_manager, | ||
| 1964 | num_src_allocator_blocks); | ||
| 1965 | R_TRY(src_allocator_result); | ||
| 1966 | |||
| 1967 | // Create an update allocator for the destination. | ||
| 1968 | Result dst_allocator_result; | ||
| 1969 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), | ||
| 1970 | m_memory_block_slab_manager, | ||
| 1971 | num_dst_allocator_blocks); | ||
| 1972 | R_TRY(dst_allocator_result); | ||
| 1973 | |||
| 1974 | // Map the memory. | ||
| 1975 | { | ||
| 1976 | // Determine the number of pages being operated on. | ||
| 1977 | const size_t num_pages = size / PageSize; | ||
| 1978 | |||
| 1979 | // Create page groups for the memory being unmapped. | ||
| 1980 | KPageGroup pg{m_kernel, m_block_info_manager}; | ||
| 1981 | |||
| 1982 | // Create the page group representing the source. | ||
| 1983 | R_TRY(this->MakePageGroup(pg, src_address, num_pages)); | ||
| 1984 | |||
| 1985 | // We're going to perform an update, so create a helper. | ||
| 1986 | KScopedPageTableUpdater updater(this); | ||
| 1987 | |||
| 1988 | // Reprotect the source as kernel-read/not mapped. | ||
| 1989 | const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>( | ||
| 1990 | KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); | ||
| 1991 | const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked; | ||
| 1992 | const KPageProperties src_properties = {new_src_perm, false, false, | ||
| 1993 | DisableMergeAttribute::DisableHeadBodyTail}; | ||
| 1994 | R_TRY(this->Operate(src_address, num_pages, src_properties.perm, | ||
| 1995 | OperationType::ChangePermissions)); | ||
| 1996 | |||
| 1997 | // Ensure that we unprotect the source pages on failure. | ||
| 1998 | ON_RESULT_FAILURE { | ||
| 1999 | const KPageProperties unprotect_properties = { | ||
| 2000 | KMemoryPermission::UserReadWrite, false, false, | ||
| 2001 | DisableMergeAttribute::EnableHeadBodyTail}; | ||
| 2002 | ASSERT(this->Operate(src_address, num_pages, unprotect_properties.perm, | ||
| 2003 | OperationType::ChangePermissions) == ResultSuccess); | ||
| 2004 | }; | ||
| 2005 | |||
| 2006 | // Map the alias pages. | ||
| 2007 | const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false, | ||
| 2008 | DisableMergeAttribute::DisableHead}; | ||
| 2009 | R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties, | ||
| 2010 | false)); | ||
| 2011 | |||
| 2012 | // Apply the memory block updates. | ||
| 2013 | m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, | ||
| 2014 | src_state, new_src_perm, new_src_attr, | ||
| 2015 | KMemoryBlockDisableMergeAttribute::Locked, | ||
| 2016 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2017 | m_memory_block_manager.Update( | ||
| 2018 | std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack, | ||
| 2019 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 2020 | KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None); | ||
| 2021 | } | ||
| 2022 | |||
| 2023 | R_SUCCEED(); | ||
| 2024 | } | ||
| 2025 | |||
| 2026 | Result KPageTable::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 2027 | size_t size) { | ||
| 2028 | // Lock the table. | ||
| 2029 | KScopedLightLock lk(m_general_lock); | ||
| 2030 | |||
| 2031 | // Validate that the source address's state is valid. | ||
| 2032 | KMemoryState src_state; | ||
| 2033 | size_t num_src_allocator_blocks; | ||
| 2034 | R_TRY(this->CheckMemoryState( | ||
| 2035 | std::addressof(src_state), nullptr, nullptr, std::addressof(num_src_allocator_blocks), | ||
| 2036 | src_address, size, KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias, | ||
| 2037 | KMemoryPermission::All, KMemoryPermission::NotMapped | KMemoryPermission::KernelRead, | ||
| 2038 | KMemoryAttribute::All, KMemoryAttribute::Locked)); | ||
| 2039 | |||
| 2040 | // Validate that the dst address's state is valid. | ||
| 2041 | KMemoryPermission dst_perm; | ||
| 2042 | size_t num_dst_allocator_blocks; | ||
| 2043 | R_TRY(this->CheckMemoryState( | ||
| 2044 | nullptr, std::addressof(dst_perm), nullptr, std::addressof(num_dst_allocator_blocks), | ||
| 2045 | dst_address, size, KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None, | ||
| 2046 | KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 2047 | |||
| 2048 | // Create an update allocator for the source. | ||
| 2049 | Result src_allocator_result; | ||
| 2050 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), | ||
| 2051 | m_memory_block_slab_manager, | ||
| 2052 | num_src_allocator_blocks); | ||
| 2053 | R_TRY(src_allocator_result); | ||
| 2054 | |||
| 2055 | // Create an update allocator for the destination. | ||
| 2056 | Result dst_allocator_result; | ||
| 2057 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), | ||
| 2058 | m_memory_block_slab_manager, | ||
| 2059 | num_dst_allocator_blocks); | ||
| 2060 | R_TRY(dst_allocator_result); | ||
| 2061 | |||
| 2062 | // Unmap the memory. | ||
| 2063 | { | ||
| 2064 | // Determine the number of pages being operated on. | ||
| 2065 | const size_t num_pages = size / PageSize; | ||
| 2066 | |||
| 2067 | // Create page groups for the memory being unmapped. | ||
| 2068 | KPageGroup pg{m_kernel, m_block_info_manager}; | ||
| 2069 | |||
| 2070 | // Create the page group representing the destination. | ||
| 2071 | R_TRY(this->MakePageGroup(pg, dst_address, num_pages)); | ||
| 2072 | |||
| 2073 | // Ensure the page group is the valid for the source. | ||
| 2074 | R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion); | ||
| 2075 | |||
| 2076 | // We're going to perform an update, so create a helper. | ||
| 2077 | KScopedPageTableUpdater updater(this); | ||
| 2078 | |||
| 2079 | // Unmap the aliased copy of the pages. | ||
| 2080 | const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false, | ||
| 2081 | DisableMergeAttribute::None}; | ||
| 2082 | R_TRY( | ||
| 2083 | this->Operate(dst_address, num_pages, dst_unmap_properties.perm, OperationType::Unmap)); | ||
| 2084 | |||
| 2085 | // Ensure that we re-map the aliased pages on failure. | ||
| 2086 | ON_RESULT_FAILURE { | ||
| 2087 | this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg); | ||
| 2088 | }; | ||
| 2089 | |||
| 2090 | // Try to set the permissions for the source pages back to what they should be. | ||
| 2091 | const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false, | ||
| 2092 | DisableMergeAttribute::EnableAndMergeHeadBodyTail}; | ||
| 2093 | R_TRY(this->Operate(src_address, num_pages, src_properties.perm, | ||
| 2094 | OperationType::ChangePermissions)); | ||
| 2095 | |||
| 2096 | // Apply the memory block updates. | ||
| 2097 | m_memory_block_manager.Update( | ||
| 2098 | std::addressof(src_allocator), src_address, num_pages, src_state, | ||
| 2099 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 2100 | KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked); | ||
| 2101 | m_memory_block_manager.Update( | ||
| 2102 | std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None, | ||
| 2103 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2104 | KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); | ||
| 2105 | } | ||
| 2106 | |||
| 2107 | R_SUCCEED(); | ||
| 2108 | } | ||
| 2109 | |||
| 2110 | Result KPageTable::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address, | ||
| 2111 | size_t num_pages, KMemoryPermission perm) { | ||
| 2112 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 2113 | |||
| 2114 | // Create a page group to hold the pages we allocate. | ||
| 2115 | KPageGroup pg{m_kernel, m_block_info_manager}; | ||
| 2116 | |||
| 2117 | // Allocate the pages. | ||
| 2118 | R_TRY( | ||
| 2119 | m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option)); | ||
| 2120 | |||
| 2121 | // Ensure that the page group is closed when we're done working with it. | ||
| 2122 | SCOPE_EXIT({ pg.Close(); }); | ||
| 2123 | |||
| 2124 | // Clear all pages. | ||
| 2125 | for (const auto& it : pg) { | ||
| 2126 | std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value, | ||
| 2127 | it.GetSize()); | ||
| 2128 | } | ||
| 2129 | |||
| 2130 | // Map the pages. | ||
| 2131 | R_RETURN(this->Operate(address, num_pages, pg, OperationType::MapGroup)); | ||
| 2132 | } | ||
| 2133 | |||
| 2134 | Result KPageTable::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address, | ||
| 2135 | const KPageGroup& pg, const KPageProperties properties, | ||
| 2136 | bool reuse_ll) { | ||
| 2137 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 2138 | |||
| 2139 | // Note the current address, so that we can iterate. | ||
| 2140 | const KProcessAddress start_address = address; | ||
| 2141 | KProcessAddress cur_address = address; | ||
| 2142 | |||
| 2143 | // Ensure that we clean up on failure. | ||
| 2144 | ON_RESULT_FAILURE { | ||
| 2145 | ASSERT(!reuse_ll); | ||
| 2146 | if (cur_address != start_address) { | ||
| 2147 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 2148 | DisableMergeAttribute::None}; | ||
| 2149 | ASSERT(this->Operate(start_address, (cur_address - start_address) / PageSize, | ||
| 2150 | unmap_properties.perm, OperationType::Unmap) == ResultSuccess); | ||
| 2151 | } | ||
| 2152 | }; | ||
| 2153 | |||
| 2154 | // Iterate, mapping all pages in the group. | ||
| 2155 | for (const auto& block : pg) { | ||
| 2156 | // Map and advance. | ||
| 2157 | const KPageProperties cur_properties = | ||
| 2158 | (cur_address == start_address) | ||
| 2159 | ? properties | ||
| 2160 | : KPageProperties{properties.perm, properties.io, properties.uncached, | ||
| 2161 | DisableMergeAttribute::None}; | ||
| 2162 | this->Operate(cur_address, block.GetNumPages(), cur_properties.perm, OperationType::Map, | ||
| 2163 | block.GetAddress()); | ||
| 2164 | cur_address += block.GetSize(); | ||
| 2165 | } | ||
| 2166 | |||
| 2167 | // We succeeded! | ||
| 2168 | R_SUCCEED(); | ||
| 2169 | } | ||
| 2170 | |||
| 2171 | void KPageTable::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size, | ||
| 2172 | const KPageGroup& pg) { | ||
| 2173 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 2174 | |||
| 2175 | // Note the current address, so that we can iterate. | ||
| 2176 | const KProcessAddress start_address = address; | ||
| 2177 | const KProcessAddress last_address = start_address + size - 1; | ||
| 2178 | const KProcessAddress end_address = last_address + 1; | ||
| 2179 | |||
| 2180 | // Iterate over the memory. | ||
| 2181 | auto pg_it = pg.begin(); | ||
| 2182 | ASSERT(pg_it != pg.end()); | ||
| 2183 | |||
| 2184 | KPhysicalAddress pg_phys_addr = pg_it->GetAddress(); | ||
| 2185 | size_t pg_pages = pg_it->GetNumPages(); | ||
| 2186 | |||
| 2187 | auto it = m_memory_block_manager.FindIterator(start_address); | ||
| 2188 | while (true) { | ||
| 2189 | // Check that the iterator is valid. | ||
| 2190 | ASSERT(it != m_memory_block_manager.end()); | ||
| 2191 | |||
| 2192 | // Get the memory info. | ||
| 2193 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 2194 | |||
| 2195 | // Determine the range to map. | ||
| 2196 | KProcessAddress map_address = std::max<KProcessAddress>(info.GetAddress(), start_address); | ||
| 2197 | const KProcessAddress map_end_address = | ||
| 2198 | std::min<KProcessAddress>(info.GetEndAddress(), end_address); | ||
| 2199 | ASSERT(map_end_address != map_address); | ||
| 2200 | |||
| 2201 | // Determine if we should disable head merge. | ||
| 2202 | const bool disable_head_merge = | ||
| 2203 | info.GetAddress() >= GetInteger(start_address) && | ||
| 2204 | True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal); | ||
| 2205 | const KPageProperties map_properties = { | ||
| 2206 | info.GetPermission(), false, false, | ||
| 2207 | disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None}; | ||
| 2208 | |||
| 2209 | // While we have pages to map, map them. | ||
| 2210 | size_t map_pages = (map_end_address - map_address) / PageSize; | ||
| 2211 | while (map_pages > 0) { | ||
| 2212 | // Check if we're at the end of the physical block. | ||
| 2213 | if (pg_pages == 0) { | ||
| 2214 | // Ensure there are more pages to map. | ||
| 2215 | ASSERT(pg_it != pg.end()); | ||
| 2216 | |||
| 2217 | // Advance our physical block. | ||
| 2218 | ++pg_it; | ||
| 2219 | pg_phys_addr = pg_it->GetAddress(); | ||
| 2220 | pg_pages = pg_it->GetNumPages(); | ||
| 2221 | } | ||
| 2222 | |||
| 2223 | // Map whatever we can. | ||
| 2224 | const size_t cur_pages = std::min(pg_pages, map_pages); | ||
| 2225 | ASSERT(this->Operate(map_address, map_pages, map_properties.perm, OperationType::Map, | ||
| 2226 | pg_phys_addr) == ResultSuccess); | ||
| 2227 | |||
| 2228 | // Advance. | ||
| 2229 | map_address += cur_pages * PageSize; | ||
| 2230 | map_pages -= cur_pages; | ||
| 2231 | |||
| 2232 | pg_phys_addr += cur_pages * PageSize; | ||
| 2233 | pg_pages -= cur_pages; | ||
| 2234 | } | ||
| 2235 | |||
| 2236 | // Check if we're done. | ||
| 2237 | if (last_address <= info.GetLastAddress()) { | ||
| 2238 | break; | ||
| 2239 | } | ||
| 2240 | |||
| 2241 | // Advance. | ||
| 2242 | ++it; | ||
| 2243 | } | ||
| 2244 | |||
| 2245 | // Check that we re-mapped precisely the page group. | ||
| 2246 | ASSERT((++pg_it) == pg.end()); | ||
| 2247 | } | ||
| 2248 | |||
| 2249 | Result KPageTable::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, | ||
| 2250 | KPhysicalAddress phys_addr, bool is_pa_valid, | ||
| 2251 | KProcessAddress region_start, size_t region_num_pages, | ||
| 2252 | KMemoryState state, KMemoryPermission perm) { | ||
| 2253 | ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); | ||
| 2254 | |||
| 2255 | // Ensure this is a valid map request. | ||
| 2256 | R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), | ||
| 2257 | ResultInvalidCurrentMemory); | ||
| 2258 | R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); | ||
| 2259 | |||
| 2260 | // Lock the table. | ||
| 2261 | KScopedLightLock lk(m_general_lock); | ||
| 2262 | |||
| 2263 | // Find a random address to map at. | ||
| 2264 | KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, | ||
| 2265 | 0, this->GetNumGuardPages()); | ||
| 2266 | R_UNLESS(addr != 0, ResultOutOfMemory); | ||
| 2267 | ASSERT(Common::IsAligned(GetInteger(addr), alignment)); | ||
| 2268 | ASSERT(this->CanContain(addr, num_pages * PageSize, state)); | ||
| 2269 | ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, | ||
| 2270 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2271 | KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess); | ||
| 2272 | |||
| 2273 | // Create an update allocator. | ||
| 2274 | Result allocator_result; | ||
| 2275 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2276 | m_memory_block_slab_manager); | ||
| 2277 | R_TRY(allocator_result); | ||
| 2278 | |||
| 2279 | // We're going to perform an update, so create a helper. | ||
| 2280 | KScopedPageTableUpdater updater(this); | ||
| 2281 | |||
| 2282 | // Perform mapping operation. | ||
| 2283 | if (is_pa_valid) { | ||
| 2284 | const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead}; | ||
| 2285 | R_TRY(this->Operate(addr, num_pages, properties.perm, OperationType::Map, phys_addr)); | ||
| 2286 | } else { | ||
| 2287 | R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm)); | ||
| 2288 | } | ||
| 2289 | |||
| 2290 | // Update the blocks. | ||
| 2291 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, | ||
| 2292 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2293 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2294 | |||
| 2295 | // We successfully mapped the pages. | ||
| 2296 | *out_addr = addr; | ||
| 2297 | R_SUCCEED(); | ||
| 2298 | } | ||
| 2299 | |||
| 2300 | Result KPageTable::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, | ||
| 2301 | KMemoryPermission perm) { | ||
| 2302 | // Check that the map is in range. | ||
| 2303 | const size_t size = num_pages * PageSize; | ||
| 2304 | R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); | ||
| 2305 | |||
| 2306 | // Lock the table. | ||
| 2307 | KScopedLightLock lk(m_general_lock); | ||
| 2308 | |||
| 2309 | // Check the memory state. | ||
| 2310 | size_t num_allocator_blocks; | ||
| 2311 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | ||
| 2312 | KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, | ||
| 2313 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2314 | KMemoryAttribute::None)); | ||
| 2315 | |||
| 2316 | // Create an update allocator. | ||
| 2317 | Result allocator_result; | ||
| 2318 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2319 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2320 | R_TRY(allocator_result); | ||
| 2321 | |||
| 2322 | // We're going to perform an update, so create a helper. | ||
| 2323 | KScopedPageTableUpdater updater(this); | ||
| 2324 | |||
| 2325 | // Map the pages. | ||
| 2326 | R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm)); | ||
| 2327 | |||
| 2328 | // Update the blocks. | ||
| 2329 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, | ||
| 2330 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2331 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2332 | |||
| 2333 | R_SUCCEED(); | ||
| 2334 | } | ||
| 2335 | |||
| 2336 | Result KPageTable::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) { | ||
| 2337 | // Check that the unmap is in range. | ||
| 2338 | const size_t size = num_pages * PageSize; | ||
| 2339 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 2340 | |||
| 2341 | // Lock the table. | ||
| 2342 | KScopedLightLock lk(m_general_lock); | ||
| 2343 | |||
| 2344 | // Check the memory state. | ||
| 2345 | size_t num_allocator_blocks; | ||
| 2346 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | ||
| 2347 | KMemoryState::All, state, KMemoryPermission::None, | ||
| 2348 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 2349 | KMemoryAttribute::None)); | ||
| 2350 | |||
| 2351 | // Create an update allocator. | ||
| 2352 | Result allocator_result; | ||
| 2353 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2354 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2355 | R_TRY(allocator_result); | ||
| 2356 | |||
| 2357 | // We're going to perform an update, so create a helper. | ||
| 2358 | KScopedPageTableUpdater updater(this); | ||
| 2359 | |||
| 2360 | // Perform the unmap. | ||
| 2361 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 2362 | DisableMergeAttribute::None}; | ||
| 2363 | R_TRY(this->Operate(address, num_pages, unmap_properties.perm, OperationType::Unmap)); | ||
| 2364 | |||
| 2365 | // Update the blocks. | ||
| 2366 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, | ||
| 2367 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2368 | KMemoryBlockDisableMergeAttribute::None, | ||
| 2369 | KMemoryBlockDisableMergeAttribute::Normal); | ||
| 2370 | |||
| 2371 | R_SUCCEED(); | ||
| 2372 | } | ||
| 2373 | |||
| 2374 | Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg, | ||
| 2375 | KProcessAddress region_start, size_t region_num_pages, | ||
| 2376 | KMemoryState state, KMemoryPermission perm) { | ||
| 2377 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 2378 | |||
| 2379 | // Ensure this is a valid map request. | ||
| 2380 | const size_t num_pages = pg.GetNumPages(); | ||
| 2381 | R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), | ||
| 2382 | ResultInvalidCurrentMemory); | ||
| 2383 | R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); | ||
| 2384 | |||
| 2385 | // Lock the table. | ||
| 2386 | KScopedLightLock lk(m_general_lock); | ||
| 2387 | |||
| 2388 | // Find a random address to map at. | ||
| 2389 | KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize, | ||
| 2390 | 0, this->GetNumGuardPages()); | ||
| 2391 | R_UNLESS(addr != 0, ResultOutOfMemory); | ||
| 2392 | ASSERT(this->CanContain(addr, num_pages * PageSize, state)); | ||
| 2393 | ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, | ||
| 2394 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2395 | KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess); | ||
| 2396 | |||
| 2397 | // Create an update allocator. | ||
| 2398 | Result allocator_result; | ||
| 2399 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2400 | m_memory_block_slab_manager); | ||
| 2401 | R_TRY(allocator_result); | ||
| 2402 | |||
| 2403 | // We're going to perform an update, so create a helper. | ||
| 2404 | KScopedPageTableUpdater updater(this); | ||
| 2405 | |||
| 2406 | // Perform mapping operation. | ||
| 2407 | const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead}; | ||
| 2408 | R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); | ||
| 2409 | |||
| 2410 | // Update the blocks. | ||
| 2411 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, | ||
| 2412 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2413 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2414 | |||
| 2415 | // We successfully mapped the pages. | ||
| 2416 | *out_addr = addr; | ||
| 2417 | R_SUCCEED(); | ||
| 2418 | } | ||
| 2419 | |||
| 2420 | Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state, | ||
| 2421 | KMemoryPermission perm) { | ||
| 2422 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 2423 | |||
| 2424 | // Ensure this is a valid map request. | ||
| 2425 | const size_t num_pages = pg.GetNumPages(); | ||
| 2426 | const size_t size = num_pages * PageSize; | ||
| 2427 | R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); | ||
| 2428 | |||
| 2429 | // Lock the table. | ||
| 2430 | KScopedLightLock lk(m_general_lock); | ||
| 2431 | |||
| 2432 | // Check if state allows us to map. | ||
| 2433 | size_t num_allocator_blocks; | ||
| 2434 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size, | ||
| 2435 | KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, | ||
| 2436 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2437 | KMemoryAttribute::None)); | ||
| 2438 | |||
| 2439 | // Create an update allocator. | ||
| 2440 | Result allocator_result; | ||
| 2441 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2442 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2443 | R_TRY(allocator_result); | ||
| 2444 | |||
| 2445 | // We're going to perform an update, so create a helper. | ||
| 2446 | KScopedPageTableUpdater updater(this); | ||
| 2447 | |||
| 2448 | // Perform mapping operation. | ||
| 2449 | const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead}; | ||
| 2450 | R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); | ||
| 2451 | |||
| 2452 | // Update the blocks. | ||
| 2453 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, | ||
| 2454 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2455 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2456 | |||
| 2457 | // We successfully mapped the pages. | ||
| 2458 | R_SUCCEED(); | ||
| 2459 | } | ||
| 2460 | |||
| 2461 | Result KPageTable::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, | ||
| 2462 | KMemoryState state) { | ||
| 2463 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 2464 | |||
| 2465 | // Ensure this is a valid unmap request. | ||
| 2466 | const size_t num_pages = pg.GetNumPages(); | ||
| 2467 | const size_t size = num_pages * PageSize; | ||
| 2468 | R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); | ||
| 2469 | |||
| 2470 | // Lock the table. | ||
| 2471 | KScopedLightLock lk(m_general_lock); | ||
| 2472 | |||
| 2473 | // Check if state allows us to unmap. | ||
| 2474 | size_t num_allocator_blocks; | ||
| 2475 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | ||
| 2476 | KMemoryState::All, state, KMemoryPermission::None, | ||
| 2477 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 2478 | KMemoryAttribute::None)); | ||
| 2479 | |||
| 2480 | // Check that the page group is valid. | ||
| 2481 | R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory); | ||
| 2482 | |||
| 2483 | // Create an update allocator. | ||
| 2484 | Result allocator_result; | ||
| 2485 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2486 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2487 | R_TRY(allocator_result); | ||
| 2488 | |||
| 2489 | // We're going to perform an update, so create a helper. | ||
| 2490 | KScopedPageTableUpdater updater(this); | ||
| 2491 | |||
| 2492 | // Perform unmapping operation. | ||
| 2493 | const KPageProperties properties = {KMemoryPermission::None, false, false, | ||
| 2494 | DisableMergeAttribute::None}; | ||
| 2495 | R_TRY(this->Operate(address, num_pages, properties.perm, OperationType::Unmap)); | ||
| 2496 | |||
| 2497 | // Update the blocks. | ||
| 2498 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, | ||
| 2499 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2500 | KMemoryBlockDisableMergeAttribute::None, | ||
| 2501 | KMemoryBlockDisableMergeAttribute::Normal); | ||
| 2502 | |||
| 2503 | R_SUCCEED(); | ||
| 2504 | } | ||
| 2505 | |||
| 2506 | Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, | ||
| 2507 | KMemoryState state_mask, KMemoryState state, | ||
| 2508 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 2509 | KMemoryAttribute attr_mask, KMemoryAttribute attr) { | ||
| 2510 | // Ensure that the page group isn't null. | ||
| 2511 | ASSERT(out != nullptr); | ||
| 2512 | |||
| 2513 | // Make sure that the region we're mapping is valid for the table. | ||
| 2514 | const size_t size = num_pages * PageSize; | ||
| 2515 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 2516 | |||
| 2517 | // Lock the table. | ||
| 2518 | KScopedLightLock lk(m_general_lock); | ||
| 2519 | |||
| 2520 | // Check if state allows us to create the group. | ||
| 2521 | R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted, | ||
| 2522 | state | KMemoryState::FlagReferenceCounted, perm_mask, perm, | ||
| 2523 | attr_mask, attr)); | ||
| 2524 | |||
| 2525 | // Create a new page group for the region. | ||
| 2526 | R_TRY(this->MakePageGroup(*out, address, num_pages)); | ||
| 2527 | |||
| 2528 | R_SUCCEED(); | ||
| 2529 | } | ||
| 2530 | |||
| 2531 | Result KPageTable::SetProcessMemoryPermission(KProcessAddress addr, size_t size, | ||
| 2532 | Svc::MemoryPermission svc_perm) { | ||
| 2533 | const size_t num_pages = size / PageSize; | ||
| 2534 | |||
| 2535 | // Lock the table. | ||
| 2536 | KScopedLightLock lk(m_general_lock); | ||
| 2537 | |||
| 2538 | // Verify we can change the memory permission. | ||
| 2539 | KMemoryState old_state; | ||
| 2540 | KMemoryPermission old_perm; | ||
| 2541 | size_t num_allocator_blocks; | ||
| 2542 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr, | ||
| 2543 | std::addressof(num_allocator_blocks), addr, size, | ||
| 2544 | KMemoryState::FlagCode, KMemoryState::FlagCode, | ||
| 2545 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2546 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 2547 | |||
| 2548 | // Determine new perm/state. | ||
| 2549 | const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm); | ||
| 2550 | KMemoryState new_state = old_state; | ||
| 2551 | const bool is_w = (new_perm & KMemoryPermission::UserWrite) == KMemoryPermission::UserWrite; | ||
| 2552 | const bool is_x = (new_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute; | ||
| 2553 | const bool was_x = | ||
| 2554 | (old_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute; | ||
| 2555 | ASSERT(!(is_w && is_x)); | ||
| 2556 | |||
| 2557 | if (is_w) { | ||
| 2558 | switch (old_state) { | ||
| 2559 | case KMemoryState::Code: | ||
| 2560 | new_state = KMemoryState::CodeData; | ||
| 2561 | break; | ||
| 2562 | case KMemoryState::AliasCode: | ||
| 2563 | new_state = KMemoryState::AliasCodeData; | ||
| 2564 | break; | ||
| 2565 | default: | ||
| 2566 | ASSERT(false); | ||
| 2567 | break; | ||
| 2568 | } | ||
| 2569 | } | ||
| 2570 | |||
| 2571 | // Succeed if there's nothing to do. | ||
| 2572 | R_SUCCEED_IF(old_perm == new_perm && old_state == new_state); | ||
| 2573 | |||
| 2574 | // Create an update allocator. | ||
| 2575 | Result allocator_result{ResultSuccess}; | ||
| 2576 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2577 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2578 | R_TRY(allocator_result); | ||
| 2579 | |||
| 2580 | // Perform mapping operation. | ||
| 2581 | const auto operation = | ||
| 2582 | was_x ? OperationType::ChangePermissionsAndRefresh : OperationType::ChangePermissions; | ||
| 2583 | R_TRY(Operate(addr, num_pages, new_perm, operation)); | ||
| 2584 | |||
| 2585 | // Update the blocks. | ||
| 2586 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm, | ||
| 2587 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | ||
| 2588 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2589 | |||
| 2590 | // Ensure cache coherency, if we're setting pages as executable. | ||
| 2591 | if (is_x) { | ||
| 2592 | m_system.InvalidateCpuInstructionCacheRange(GetInteger(addr), size); | ||
| 2593 | } | ||
| 2594 | |||
| 2595 | R_SUCCEED(); | ||
| 2596 | } | ||
| 2597 | |||
| 2598 | KMemoryInfo KPageTable::QueryInfoImpl(KProcessAddress addr) { | ||
| 2599 | KScopedLightLock lk(m_general_lock); | ||
| 2600 | |||
| 2601 | return m_memory_block_manager.FindBlock(addr)->GetMemoryInfo(); | ||
| 2602 | } | ||
| 2603 | |||
| 2604 | KMemoryInfo KPageTable::QueryInfo(KProcessAddress addr) { | ||
| 2605 | if (!Contains(addr, 1)) { | ||
| 2606 | return { | ||
| 2607 | .m_address = GetInteger(m_address_space_end), | ||
| 2608 | .m_size = 0 - GetInteger(m_address_space_end), | ||
| 2609 | .m_state = static_cast<KMemoryState>(Svc::MemoryState::Inaccessible), | ||
| 2610 | .m_device_disable_merge_left_count = 0, | ||
| 2611 | .m_device_disable_merge_right_count = 0, | ||
| 2612 | .m_ipc_lock_count = 0, | ||
| 2613 | .m_device_use_count = 0, | ||
| 2614 | .m_ipc_disable_merge_count = 0, | ||
| 2615 | .m_permission = KMemoryPermission::None, | ||
| 2616 | .m_attribute = KMemoryAttribute::None, | ||
| 2617 | .m_original_permission = KMemoryPermission::None, | ||
| 2618 | .m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None, | ||
| 2619 | }; | ||
| 2620 | } | ||
| 2621 | |||
| 2622 | return QueryInfoImpl(addr); | ||
| 2623 | } | ||
| 2624 | |||
| 2625 | Result KPageTable::SetMemoryPermission(KProcessAddress addr, size_t size, | ||
| 2626 | Svc::MemoryPermission svc_perm) { | ||
| 2627 | const size_t num_pages = size / PageSize; | ||
| 2628 | |||
| 2629 | // Lock the table. | ||
| 2630 | KScopedLightLock lk(m_general_lock); | ||
| 2631 | |||
| 2632 | // Verify we can change the memory permission. | ||
| 2633 | KMemoryState old_state; | ||
| 2634 | KMemoryPermission old_perm; | ||
| 2635 | size_t num_allocator_blocks; | ||
| 2636 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr, | ||
| 2637 | std::addressof(num_allocator_blocks), addr, size, | ||
| 2638 | KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, | ||
| 2639 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2640 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 2641 | |||
| 2642 | // Determine new perm. | ||
| 2643 | const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm); | ||
| 2644 | R_SUCCEED_IF(old_perm == new_perm); | ||
| 2645 | |||
| 2646 | // Create an update allocator. | ||
| 2647 | Result allocator_result{ResultSuccess}; | ||
| 2648 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2649 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2650 | R_TRY(allocator_result); | ||
| 2651 | |||
| 2652 | // Perform mapping operation. | ||
| 2653 | R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); | ||
| 2654 | |||
| 2655 | // Update the blocks. | ||
| 2656 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, | ||
| 2657 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | ||
| 2658 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2659 | |||
| 2660 | R_SUCCEED(); | ||
| 2661 | } | ||
| 2662 | |||
| 2663 | Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr) { | ||
| 2664 | const size_t num_pages = size / PageSize; | ||
| 2665 | ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) == | ||
| 2666 | KMemoryAttribute::SetMask); | ||
| 2667 | |||
| 2668 | // Lock the table. | ||
| 2669 | KScopedLightLock lk(m_general_lock); | ||
| 2670 | |||
| 2671 | // Verify we can change the memory attribute. | ||
| 2672 | KMemoryState old_state; | ||
| 2673 | KMemoryPermission old_perm; | ||
| 2674 | KMemoryAttribute old_attr; | ||
| 2675 | size_t num_allocator_blocks; | ||
| 2676 | constexpr auto AttributeTestMask = | ||
| 2677 | ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared); | ||
| 2678 | const KMemoryState state_test_mask = | ||
| 2679 | static_cast<KMemoryState>(((mask & static_cast<u32>(KMemoryAttribute::Uncached)) | ||
| 2680 | ? static_cast<u32>(KMemoryState::FlagCanChangeAttribute) | ||
| 2681 | : 0) | | ||
| 2682 | ((mask & static_cast<u32>(KMemoryAttribute::PermissionLocked)) | ||
| 2683 | ? static_cast<u32>(KMemoryState::FlagCanPermissionLock) | ||
| 2684 | : 0)); | ||
| 2685 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), | ||
| 2686 | std::addressof(old_attr), std::addressof(num_allocator_blocks), | ||
| 2687 | addr, size, state_test_mask, state_test_mask, | ||
| 2688 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2689 | AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask)); | ||
| 2690 | |||
| 2691 | // Create an update allocator. | ||
| 2692 | Result allocator_result{ResultSuccess}; | ||
| 2693 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2694 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2695 | R_TRY(allocator_result); | ||
| 2696 | |||
| 2697 | // If we need to, perform a change attribute operation. | ||
| 2698 | if (True(KMemoryAttribute::Uncached & static_cast<KMemoryAttribute>(mask))) { | ||
| 2699 | // Perform operation. | ||
| 2700 | R_TRY(this->Operate(addr, num_pages, old_perm, | ||
| 2701 | OperationType::ChangePermissionsAndRefreshAndFlush, 0)); | ||
| 2702 | } | ||
| 2703 | |||
| 2704 | // Update the blocks. | ||
| 2705 | m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages, | ||
| 2706 | static_cast<KMemoryAttribute>(mask), | ||
| 2707 | static_cast<KMemoryAttribute>(attr)); | ||
| 2708 | |||
| 2709 | R_SUCCEED(); | ||
| 2710 | } | ||
| 2711 | |||
| 2712 | Result KPageTable::SetMaxHeapSize(size_t size) { | ||
| 2713 | // Lock the table. | ||
| 2714 | KScopedLightLock lk(m_general_lock); | ||
| 2715 | |||
| 2716 | // Only process page tables are allowed to set heap size. | ||
| 2717 | ASSERT(!this->IsKernel()); | ||
| 2718 | |||
| 2719 | m_max_heap_size = size; | ||
| 2720 | |||
| 2721 | R_SUCCEED(); | ||
| 2722 | } | ||
| 2723 | |||
| 2724 | Result KPageTable::SetHeapSize(u64* out, size_t size) { | ||
| 2725 | // Lock the physical memory mutex. | ||
| 2726 | KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock); | ||
| 2727 | |||
| 2728 | // Try to perform a reduction in heap, instead of an extension. | ||
| 2729 | KProcessAddress cur_address{}; | ||
| 2730 | size_t allocation_size{}; | ||
| 2731 | { | ||
| 2732 | // Lock the table. | ||
| 2733 | KScopedLightLock lk(m_general_lock); | ||
| 2734 | |||
| 2735 | // Validate that setting heap size is possible at all. | ||
| 2736 | R_UNLESS(!m_is_kernel, ResultOutOfMemory); | ||
| 2737 | R_UNLESS(size <= static_cast<size_t>(m_heap_region_end - m_heap_region_start), | ||
| 2738 | ResultOutOfMemory); | ||
| 2739 | R_UNLESS(size <= m_max_heap_size, ResultOutOfMemory); | ||
| 2740 | |||
| 2741 | if (size < GetHeapSize()) { | ||
| 2742 | // The size being requested is less than the current size, so we need to free the end of | ||
| 2743 | // the heap. | ||
| 2744 | |||
| 2745 | // Validate memory state. | ||
| 2746 | size_t num_allocator_blocks; | ||
| 2747 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), | ||
| 2748 | m_heap_region_start + size, GetHeapSize() - size, | ||
| 2749 | KMemoryState::All, KMemoryState::Normal, | ||
| 2750 | KMemoryPermission::All, KMemoryPermission::UserReadWrite, | ||
| 2751 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 2752 | |||
| 2753 | // Create an update allocator. | ||
| 2754 | Result allocator_result{ResultSuccess}; | ||
| 2755 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2756 | m_memory_block_slab_manager, | ||
| 2757 | num_allocator_blocks); | ||
| 2758 | R_TRY(allocator_result); | ||
| 2759 | |||
| 2760 | // Unmap the end of the heap. | ||
| 2761 | const auto num_pages = (GetHeapSize() - size) / PageSize; | ||
| 2762 | R_TRY(Operate(m_heap_region_start + size, num_pages, KMemoryPermission::None, | ||
| 2763 | OperationType::Unmap)); | ||
| 2764 | |||
| 2765 | // Release the memory from the resource limit. | ||
| 2766 | m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, num_pages * PageSize); | ||
| 2767 | |||
| 2768 | // Apply the memory block update. | ||
| 2769 | m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, | ||
| 2770 | num_pages, KMemoryState::Free, KMemoryPermission::None, | ||
| 2771 | KMemoryAttribute::None, | ||
| 2772 | KMemoryBlockDisableMergeAttribute::None, | ||
| 2773 | size == 0 ? KMemoryBlockDisableMergeAttribute::Normal | ||
| 2774 | : KMemoryBlockDisableMergeAttribute::None); | ||
| 2775 | |||
| 2776 | // Update the current heap end. | ||
| 2777 | m_current_heap_end = m_heap_region_start + size; | ||
| 2778 | |||
| 2779 | // Set the output. | ||
| 2780 | *out = GetInteger(m_heap_region_start); | ||
| 2781 | R_SUCCEED(); | ||
| 2782 | } else if (size == GetHeapSize()) { | ||
| 2783 | // The size requested is exactly the current size. | ||
| 2784 | *out = GetInteger(m_heap_region_start); | ||
| 2785 | R_SUCCEED(); | ||
| 2786 | } else { | ||
| 2787 | // We have to allocate memory. Determine how much to allocate and where while the table | ||
| 2788 | // is locked. | ||
| 2789 | cur_address = m_current_heap_end; | ||
| 2790 | allocation_size = size - GetHeapSize(); | ||
| 2791 | } | ||
| 2792 | } | ||
| 2793 | |||
| 2794 | // Reserve memory for the heap extension. | ||
| 2795 | KScopedResourceReservation memory_reservation( | ||
| 2796 | m_resource_limit, LimitableResource::PhysicalMemoryMax, allocation_size); | ||
| 2797 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | ||
| 2798 | |||
| 2799 | // Allocate pages for the heap extension. | ||
| 2800 | KPageGroup pg{m_kernel, m_block_info_manager}; | ||
| 2801 | R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( | ||
| 2802 | &pg, allocation_size / PageSize, | ||
| 2803 | KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option))); | ||
| 2804 | |||
| 2805 | // Clear all the newly allocated pages. | ||
| 2806 | for (const auto& it : pg) { | ||
| 2807 | std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value, | ||
| 2808 | it.GetSize()); | ||
| 2809 | } | ||
| 2810 | |||
| 2811 | // Map the pages. | ||
| 2812 | { | ||
| 2813 | // Lock the table. | ||
| 2814 | KScopedLightLock lk(m_general_lock); | ||
| 2815 | |||
| 2816 | // Ensure that the heap hasn't changed since we began executing. | ||
| 2817 | ASSERT(cur_address == m_current_heap_end); | ||
| 2818 | |||
| 2819 | // Check the memory state. | ||
| 2820 | size_t num_allocator_blocks{}; | ||
| 2821 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), m_current_heap_end, | ||
| 2822 | allocation_size, KMemoryState::All, KMemoryState::Free, | ||
| 2823 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2824 | KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 2825 | |||
| 2826 | // Create an update allocator. | ||
| 2827 | Result allocator_result{ResultSuccess}; | ||
| 2828 | KMemoryBlockManagerUpdateAllocator allocator( | ||
| 2829 | std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2830 | R_TRY(allocator_result); | ||
| 2831 | |||
| 2832 | // Map the pages. | ||
| 2833 | const auto num_pages = allocation_size / PageSize; | ||
| 2834 | R_TRY(Operate(m_current_heap_end, num_pages, pg, OperationType::MapGroup)); | ||
| 2835 | |||
| 2836 | // Clear all the newly allocated pages. | ||
| 2837 | for (size_t cur_page = 0; cur_page < num_pages; ++cur_page) { | ||
| 2838 | std::memset(m_memory->GetPointer(m_current_heap_end + (cur_page * PageSize)), 0, | ||
| 2839 | PageSize); | ||
| 2840 | } | ||
| 2841 | |||
| 2842 | // We succeeded, so commit our memory reservation. | ||
| 2843 | memory_reservation.Commit(); | ||
| 2844 | |||
| 2845 | // Apply the memory block update. | ||
| 2846 | m_memory_block_manager.Update( | ||
| 2847 | std::addressof(allocator), m_current_heap_end, num_pages, KMemoryState::Normal, | ||
| 2848 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 2849 | m_heap_region_start == m_current_heap_end ? KMemoryBlockDisableMergeAttribute::Normal | ||
| 2850 | : KMemoryBlockDisableMergeAttribute::None, | ||
| 2851 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2852 | |||
| 2853 | // Update the current heap end. | ||
| 2854 | m_current_heap_end = m_heap_region_start + size; | ||
| 2855 | |||
| 2856 | // Set the output. | ||
| 2857 | *out = GetInteger(m_heap_region_start); | ||
| 2858 | R_SUCCEED(); | ||
| 2859 | } | ||
| 2860 | } | ||
| 2861 | |||
| 2862 | Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, | ||
| 2863 | size_t size, KMemoryPermission perm, | ||
| 2864 | bool is_aligned, bool check_heap) { | ||
| 2865 | // Lightly validate the range before doing anything else. | ||
| 2866 | const size_t num_pages = size / PageSize; | ||
| 2867 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 2868 | |||
| 2869 | // Lock the table. | ||
| 2870 | KScopedLightLock lk(m_general_lock); | ||
| 2871 | |||
| 2872 | // Check the memory state. | ||
| 2873 | const auto test_state = | ||
| 2874 | (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap) | | ||
| 2875 | (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None); | ||
| 2876 | size_t num_allocator_blocks; | ||
| 2877 | KMemoryState old_state; | ||
| 2878 | R_TRY(this->CheckMemoryState(std::addressof(old_state), nullptr, nullptr, | ||
| 2879 | std::addressof(num_allocator_blocks), address, size, test_state, | ||
| 2880 | test_state, perm, perm, | ||
| 2881 | KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked, | ||
| 2882 | KMemoryAttribute::None, KMemoryAttribute::DeviceShared)); | ||
| 2883 | |||
| 2884 | // Create an update allocator. | ||
| 2885 | Result allocator_result; | ||
| 2886 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2887 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2888 | R_TRY(allocator_result); | ||
| 2889 | |||
| 2890 | // Update the memory blocks. | ||
| 2891 | m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, | ||
| 2892 | &KMemoryBlock::ShareToDevice, KMemoryPermission::None); | ||
| 2893 | |||
| 2894 | // Set whether the locked memory was io. | ||
| 2895 | *out_is_io = | ||
| 2896 | static_cast<Svc::MemoryState>(old_state & KMemoryState::Mask) == Svc::MemoryState::Io; | ||
| 2897 | |||
| 2898 | R_SUCCEED(); | ||
| 2899 | } | ||
| 2900 | |||
| 2901 | Result KPageTable::LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, | ||
| 2902 | bool check_heap) { | ||
| 2903 | // Lightly validate the range before doing anything else. | ||
| 2904 | const size_t num_pages = size / PageSize; | ||
| 2905 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 2906 | |||
| 2907 | // Lock the table. | ||
| 2908 | KScopedLightLock lk(m_general_lock); | ||
| 2909 | |||
| 2910 | // Check the memory state. | ||
| 2911 | const auto test_state = KMemoryState::FlagCanDeviceMap | | ||
| 2912 | (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None); | ||
| 2913 | size_t num_allocator_blocks; | ||
| 2914 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 2915 | std::addressof(num_allocator_blocks), address, size, test_state, test_state, | ||
| 2916 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2917 | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); | ||
| 2918 | |||
| 2919 | // Create an update allocator. | ||
| 2920 | Result allocator_result; | ||
| 2921 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2922 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2923 | R_TRY(allocator_result); | ||
| 2924 | |||
| 2925 | // Update the memory blocks. | ||
| 2926 | const KMemoryBlockManager::MemoryBlockLockFunction lock_func = | ||
| 2927 | m_enable_device_address_space_merge | ||
| 2928 | ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare | ||
| 2929 | : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight; | ||
| 2930 | m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func, | ||
| 2931 | KMemoryPermission::None); | ||
| 2932 | |||
| 2933 | R_SUCCEED(); | ||
| 2934 | } | ||
| 2935 | |||
| 2936 | Result KPageTable::UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) { | ||
| 2937 | // Lightly validate the range before doing anything else. | ||
| 2938 | const size_t num_pages = size / PageSize; | ||
| 2939 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 2940 | |||
| 2941 | // Lock the table. | ||
| 2942 | KScopedLightLock lk(m_general_lock); | ||
| 2943 | |||
| 2944 | // Check the memory state. | ||
| 2945 | size_t num_allocator_blocks; | ||
| 2946 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 2947 | std::addressof(num_allocator_blocks), address, size, KMemoryState::FlagCanDeviceMap, | ||
| 2948 | KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None, | ||
| 2949 | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); | ||
| 2950 | |||
| 2951 | // Create an update allocator. | ||
| 2952 | Result allocator_result{ResultSuccess}; | ||
| 2953 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2954 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2955 | R_TRY(allocator_result); | ||
| 2956 | |||
| 2957 | // Update the memory blocks. | ||
| 2958 | m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, | ||
| 2959 | &KMemoryBlock::UnshareToDevice, KMemoryPermission::None); | ||
| 2960 | |||
| 2961 | R_SUCCEED(); | ||
| 2962 | } | ||
| 2963 | |||
| 2964 | Result KPageTable::LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, | ||
| 2965 | size_t size) { | ||
| 2966 | R_RETURN(this->LockMemoryAndOpen( | ||
| 2967 | nullptr, out, address, size, KMemoryState::FlagCanIpcUserBuffer, | ||
| 2968 | KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::All, | ||
| 2969 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None, | ||
| 2970 | KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite, | ||
| 2971 | KMemoryAttribute::Locked)); | ||
| 2972 | } | ||
| 2973 | |||
| 2974 | Result KPageTable::UnlockForIpcUserBuffer(KProcessAddress address, size_t size) { | ||
| 2975 | R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanIpcUserBuffer, | ||
| 2976 | KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::None, | ||
| 2977 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 2978 | KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, | ||
| 2979 | KMemoryAttribute::Locked, nullptr)); | ||
| 2980 | } | ||
| 2981 | |||
| 2982 | Result KPageTable::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, | ||
| 2983 | KMemoryPermission perm) { | ||
| 2984 | R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer, | ||
| 2985 | KMemoryState::FlagCanTransfer, KMemoryPermission::All, | ||
| 2986 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, | ||
| 2987 | KMemoryAttribute::None, perm, KMemoryAttribute::Locked)); | ||
| 2988 | } | ||
| 2989 | |||
| 2990 | Result KPageTable::UnlockForTransferMemory(KProcessAddress address, size_t size, | ||
| 2991 | const KPageGroup& pg) { | ||
| 2992 | R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer, | ||
| 2993 | KMemoryState::FlagCanTransfer, KMemoryPermission::None, | ||
| 2994 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 2995 | KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, | ||
| 2996 | KMemoryAttribute::Locked, std::addressof(pg))); | ||
| 2997 | } | ||
| 2998 | |||
| 2999 | Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) { | ||
| 3000 | R_RETURN(this->LockMemoryAndOpen( | ||
| 3001 | out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, | ||
| 3002 | KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, | ||
| 3003 | KMemoryAttribute::None, KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite, | ||
| 3004 | KMemoryAttribute::Locked)); | ||
| 3005 | } | ||
| 3006 | |||
| 3007 | Result KPageTable::UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg) { | ||
| 3008 | R_RETURN(this->UnlockMemory( | ||
| 3009 | addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, | ||
| 3010 | KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, | ||
| 3011 | KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg)); | ||
| 3012 | } | ||
| 3013 | |||
| 3014 | bool KPageTable::IsRegionContiguous(KProcessAddress addr, u64 size) const { | ||
| 3015 | auto start_ptr = m_system.DeviceMemory().GetPointer<u8>(GetInteger(addr)); | ||
| 3016 | for (u64 offset{}; offset < size; offset += PageSize) { | ||
| 3017 | if (start_ptr != m_system.DeviceMemory().GetPointer<u8>(GetInteger(addr) + offset)) { | ||
| 3018 | return false; | ||
| 3019 | } | ||
| 3020 | start_ptr += PageSize; | ||
| 3021 | } | ||
| 3022 | return true; | ||
| 3023 | } | ||
| 3024 | |||
| 3025 | void KPageTable::AddRegionToPages(KProcessAddress start, size_t num_pages, | ||
| 3026 | KPageGroup& page_linked_list) { | ||
| 3027 | KProcessAddress addr{start}; | ||
| 3028 | while (addr < start + (num_pages * PageSize)) { | ||
| 3029 | const KPhysicalAddress paddr{GetPhysicalAddr(addr)}; | ||
| 3030 | ASSERT(paddr != 0); | ||
| 3031 | page_linked_list.AddBlock(paddr, 1); | ||
| 3032 | addr += PageSize; | ||
| 3033 | } | ||
| 3034 | } | ||
| 3035 | |||
| 3036 | KProcessAddress KPageTable::AllocateVirtualMemory(KProcessAddress start, size_t region_num_pages, | ||
| 3037 | u64 needed_num_pages, size_t align) { | ||
| 3038 | if (m_enable_aslr) { | ||
| 3039 | UNIMPLEMENTED(); | ||
| 3040 | } | ||
| 3041 | return m_memory_block_manager.FindFreeArea(start, region_num_pages, needed_num_pages, align, 0, | ||
| 3042 | IsKernel() ? 1 : 4); | ||
| 3043 | } | ||
| 3044 | |||
| 3045 | Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, const KPageGroup& page_group, | ||
| 3046 | OperationType operation) { | ||
| 3047 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 3048 | |||
| 3049 | ASSERT(Common::IsAligned(GetInteger(addr), PageSize)); | ||
| 3050 | ASSERT(num_pages > 0); | ||
| 3051 | ASSERT(num_pages == page_group.GetNumPages()); | ||
| 3052 | |||
| 3053 | switch (operation) { | ||
| 3054 | case OperationType::MapGroup: | ||
| 3055 | case OperationType::MapFirstGroup: { | ||
| 3056 | // We want to maintain a new reference to every page in the group. | ||
| 3057 | KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup); | ||
| 3058 | |||
| 3059 | for (const auto& node : page_group) { | ||
| 3060 | const size_t size{node.GetNumPages() * PageSize}; | ||
| 3061 | |||
| 3062 | // Map the pages. | ||
| 3063 | m_memory->MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress()); | ||
| 3064 | |||
| 3065 | addr += size; | ||
| 3066 | } | ||
| 3067 | |||
| 3068 | // We succeeded! We want to persist the reference to the pages. | ||
| 3069 | spg.CancelClose(); | ||
| 3070 | |||
| 3071 | break; | ||
| 3072 | } | ||
| 3073 | default: | ||
| 3074 | ASSERT(false); | ||
| 3075 | break; | ||
| 3076 | } | ||
| 3077 | |||
| 3078 | R_SUCCEED(); | ||
| 3079 | } | ||
| 3080 | |||
| 3081 | Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm, | ||
| 3082 | OperationType operation, KPhysicalAddress map_addr) { | ||
| 3083 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 3084 | |||
| 3085 | ASSERT(num_pages > 0); | ||
| 3086 | ASSERT(Common::IsAligned(GetInteger(addr), PageSize)); | ||
| 3087 | ASSERT(ContainsPages(addr, num_pages)); | ||
| 3088 | |||
| 3089 | switch (operation) { | ||
| 3090 | case OperationType::Unmap: { | ||
| 3091 | // Ensure that any pages we track close on exit. | ||
| 3092 | KPageGroup pages_to_close{m_kernel, this->GetBlockInfoManager()}; | ||
| 3093 | SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); | ||
| 3094 | |||
| 3095 | this->AddRegionToPages(addr, num_pages, pages_to_close); | ||
| 3096 | m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize); | ||
| 3097 | break; | ||
| 3098 | } | ||
| 3099 | case OperationType::Map: { | ||
| 3100 | ASSERT(map_addr); | ||
| 3101 | ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize)); | ||
| 3102 | m_memory->MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr); | ||
| 3103 | |||
| 3104 | // Open references to pages, if we should. | ||
| 3105 | if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) { | ||
| 3106 | m_kernel.MemoryManager().Open(map_addr, num_pages); | ||
| 3107 | } | ||
| 3108 | break; | ||
| 3109 | } | ||
| 3110 | case OperationType::Separate: { | ||
| 3111 | // HACK: Unimplemented. | ||
| 3112 | break; | ||
| 3113 | } | ||
| 3114 | case OperationType::ChangePermissions: | ||
| 3115 | case OperationType::ChangePermissionsAndRefresh: | ||
| 3116 | case OperationType::ChangePermissionsAndRefreshAndFlush: | ||
| 3117 | break; | ||
| 3118 | default: | ||
| 3119 | ASSERT(false); | ||
| 3120 | break; | ||
| 3121 | } | ||
| 3122 | R_SUCCEED(); | ||
| 3123 | } | ||
| 3124 | |||
| 3125 | void KPageTable::FinalizeUpdate(PageLinkedList* page_list) { | ||
| 3126 | while (page_list->Peek()) { | ||
| 3127 | [[maybe_unused]] auto page = page_list->Pop(); | ||
| 3128 | |||
| 3129 | // TODO(bunnei): Free pages once they are allocated in guest memory | ||
| 3130 | // ASSERT(this->GetPageTableManager().IsInPageTableHeap(page)); | ||
| 3131 | // ASSERT(this->GetPageTableManager().GetRefCount(page) == 0); | ||
| 3132 | // this->GetPageTableManager().Free(page); | ||
| 3133 | } | ||
| 3134 | } | ||
| 3135 | |||
| 3136 | KProcessAddress KPageTable::GetRegionAddress(Svc::MemoryState state) const { | ||
| 3137 | switch (state) { | ||
| 3138 | case Svc::MemoryState::Free: | ||
| 3139 | case Svc::MemoryState::Kernel: | ||
| 3140 | return m_address_space_start; | ||
| 3141 | case Svc::MemoryState::Normal: | ||
| 3142 | return m_heap_region_start; | ||
| 3143 | case Svc::MemoryState::Ipc: | ||
| 3144 | case Svc::MemoryState::NonSecureIpc: | ||
| 3145 | case Svc::MemoryState::NonDeviceIpc: | ||
| 3146 | return m_alias_region_start; | ||
| 3147 | case Svc::MemoryState::Stack: | ||
| 3148 | return m_stack_region_start; | ||
| 3149 | case Svc::MemoryState::Static: | ||
| 3150 | case Svc::MemoryState::ThreadLocal: | ||
| 3151 | return m_kernel_map_region_start; | ||
| 3152 | case Svc::MemoryState::Io: | ||
| 3153 | case Svc::MemoryState::Shared: | ||
| 3154 | case Svc::MemoryState::AliasCode: | ||
| 3155 | case Svc::MemoryState::AliasCodeData: | ||
| 3156 | case Svc::MemoryState::Transfered: | ||
| 3157 | case Svc::MemoryState::SharedTransfered: | ||
| 3158 | case Svc::MemoryState::SharedCode: | ||
| 3159 | case Svc::MemoryState::GeneratedCode: | ||
| 3160 | case Svc::MemoryState::CodeOut: | ||
| 3161 | case Svc::MemoryState::Coverage: | ||
| 3162 | case Svc::MemoryState::Insecure: | ||
| 3163 | return m_alias_code_region_start; | ||
| 3164 | case Svc::MemoryState::Code: | ||
| 3165 | case Svc::MemoryState::CodeData: | ||
| 3166 | return m_code_region_start; | ||
| 3167 | default: | ||
| 3168 | UNREACHABLE(); | ||
| 3169 | } | ||
| 3170 | } | ||
| 3171 | |||
| 3172 | size_t KPageTable::GetRegionSize(Svc::MemoryState state) const { | ||
| 3173 | switch (state) { | ||
| 3174 | case Svc::MemoryState::Free: | ||
| 3175 | case Svc::MemoryState::Kernel: | ||
| 3176 | return m_address_space_end - m_address_space_start; | ||
| 3177 | case Svc::MemoryState::Normal: | ||
| 3178 | return m_heap_region_end - m_heap_region_start; | ||
| 3179 | case Svc::MemoryState::Ipc: | ||
| 3180 | case Svc::MemoryState::NonSecureIpc: | ||
| 3181 | case Svc::MemoryState::NonDeviceIpc: | ||
| 3182 | return m_alias_region_end - m_alias_region_start; | ||
| 3183 | case Svc::MemoryState::Stack: | ||
| 3184 | return m_stack_region_end - m_stack_region_start; | ||
| 3185 | case Svc::MemoryState::Static: | ||
| 3186 | case Svc::MemoryState::ThreadLocal: | ||
| 3187 | return m_kernel_map_region_end - m_kernel_map_region_start; | ||
| 3188 | case Svc::MemoryState::Io: | ||
| 3189 | case Svc::MemoryState::Shared: | ||
| 3190 | case Svc::MemoryState::AliasCode: | ||
| 3191 | case Svc::MemoryState::AliasCodeData: | ||
| 3192 | case Svc::MemoryState::Transfered: | ||
| 3193 | case Svc::MemoryState::SharedTransfered: | ||
| 3194 | case Svc::MemoryState::SharedCode: | ||
| 3195 | case Svc::MemoryState::GeneratedCode: | ||
| 3196 | case Svc::MemoryState::CodeOut: | ||
| 3197 | case Svc::MemoryState::Coverage: | ||
| 3198 | case Svc::MemoryState::Insecure: | ||
| 3199 | return m_alias_code_region_end - m_alias_code_region_start; | ||
| 3200 | case Svc::MemoryState::Code: | ||
| 3201 | case Svc::MemoryState::CodeData: | ||
| 3202 | return m_code_region_end - m_code_region_start; | ||
| 3203 | default: | ||
| 3204 | UNREACHABLE(); | ||
| 3205 | } | ||
| 3206 | } | ||
| 3207 | |||
| 3208 | bool KPageTable::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const { | ||
| 3209 | const KProcessAddress end = addr + size; | ||
| 3210 | const KProcessAddress last = end - 1; | ||
| 3211 | |||
| 3212 | const KProcessAddress region_start = this->GetRegionAddress(state); | ||
| 3213 | const size_t region_size = this->GetRegionSize(state); | ||
| 3214 | |||
| 3215 | const bool is_in_region = | ||
| 3216 | region_start <= addr && addr < end && last <= region_start + region_size - 1; | ||
| 3217 | const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr || | ||
| 3218 | m_heap_region_start == m_heap_region_end); | ||
| 3219 | const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr || | ||
| 3220 | m_alias_region_start == m_alias_region_end); | ||
| 3221 | switch (state) { | ||
| 3222 | case Svc::MemoryState::Free: | ||
| 3223 | case Svc::MemoryState::Kernel: | ||
| 3224 | return is_in_region; | ||
| 3225 | case Svc::MemoryState::Io: | ||
| 3226 | case Svc::MemoryState::Static: | ||
| 3227 | case Svc::MemoryState::Code: | ||
| 3228 | case Svc::MemoryState::CodeData: | ||
| 3229 | case Svc::MemoryState::Shared: | ||
| 3230 | case Svc::MemoryState::AliasCode: | ||
| 3231 | case Svc::MemoryState::AliasCodeData: | ||
| 3232 | case Svc::MemoryState::Stack: | ||
| 3233 | case Svc::MemoryState::ThreadLocal: | ||
| 3234 | case Svc::MemoryState::Transfered: | ||
| 3235 | case Svc::MemoryState::SharedTransfered: | ||
| 3236 | case Svc::MemoryState::SharedCode: | ||
| 3237 | case Svc::MemoryState::GeneratedCode: | ||
| 3238 | case Svc::MemoryState::CodeOut: | ||
| 3239 | case Svc::MemoryState::Coverage: | ||
| 3240 | case Svc::MemoryState::Insecure: | ||
| 3241 | return is_in_region && !is_in_heap && !is_in_alias; | ||
| 3242 | case Svc::MemoryState::Normal: | ||
| 3243 | ASSERT(is_in_heap); | ||
| 3244 | return is_in_region && !is_in_alias; | ||
| 3245 | case Svc::MemoryState::Ipc: | ||
| 3246 | case Svc::MemoryState::NonSecureIpc: | ||
| 3247 | case Svc::MemoryState::NonDeviceIpc: | ||
| 3248 | ASSERT(is_in_alias); | ||
| 3249 | return is_in_region && !is_in_heap; | ||
| 3250 | default: | ||
| 3251 | return false; | ||
| 3252 | } | ||
| 3253 | } | ||
| 3254 | |||
| 3255 | Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, | ||
| 3256 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 3257 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 3258 | KMemoryAttribute attr) const { | ||
| 3259 | // Validate the states match expectation. | ||
| 3260 | R_UNLESS((info.m_state & state_mask) == state, ResultInvalidCurrentMemory); | ||
| 3261 | R_UNLESS((info.m_permission & perm_mask) == perm, ResultInvalidCurrentMemory); | ||
| 3262 | R_UNLESS((info.m_attribute & attr_mask) == attr, ResultInvalidCurrentMemory); | ||
| 3263 | |||
| 3264 | R_SUCCEED(); | ||
| 3265 | } | ||
| 3266 | |||
| 3267 | Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, | ||
| 3268 | size_t size, KMemoryState state_mask, | ||
| 3269 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 3270 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 3271 | KMemoryAttribute attr) const { | ||
| 3272 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 3273 | |||
| 3274 | // Get information about the first block. | ||
| 3275 | const KProcessAddress last_addr = addr + size - 1; | ||
| 3276 | KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr); | ||
| 3277 | KMemoryInfo info = it->GetMemoryInfo(); | ||
| 3278 | |||
| 3279 | // If the start address isn't aligned, we need a block. | ||
| 3280 | const size_t blocks_for_start_align = | ||
| 3281 | (Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0; | ||
| 3282 | |||
| 3283 | while (true) { | ||
| 3284 | // Validate against the provided masks. | ||
| 3285 | R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); | ||
| 3286 | |||
| 3287 | // Break once we're done. | ||
| 3288 | if (last_addr <= info.GetLastAddress()) { | ||
| 3289 | break; | ||
| 3290 | } | ||
| 3291 | |||
| 3292 | // Advance our iterator. | ||
| 3293 | it++; | ||
| 3294 | ASSERT(it != m_memory_block_manager.cend()); | ||
| 3295 | info = it->GetMemoryInfo(); | ||
| 3296 | } | ||
| 3297 | |||
| 3298 | // If the end address isn't aligned, we need a block. | ||
| 3299 | const size_t blocks_for_end_align = | ||
| 3300 | (Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0; | ||
| 3301 | |||
| 3302 | if (out_blocks_needed != nullptr) { | ||
| 3303 | *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; | ||
| 3304 | } | ||
| 3305 | |||
| 3306 | R_SUCCEED(); | ||
| 3307 | } | ||
| 3308 | |||
| 3309 | Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, | ||
| 3310 | KMemoryAttribute* out_attr, size_t* out_blocks_needed, | ||
| 3311 | KMemoryBlockManager::const_iterator it, | ||
| 3312 | KProcessAddress last_addr, KMemoryState state_mask, | ||
| 3313 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 3314 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 3315 | KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { | ||
| 3316 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 3317 | |||
| 3318 | // Get information about the first block. | ||
| 3319 | KMemoryInfo info = it->GetMemoryInfo(); | ||
| 3320 | |||
| 3321 | // Validate all blocks in the range have correct state. | ||
| 3322 | const KMemoryState first_state = info.m_state; | ||
| 3323 | const KMemoryPermission first_perm = info.m_permission; | ||
| 3324 | const KMemoryAttribute first_attr = info.m_attribute; | ||
| 3325 | while (true) { | ||
| 3326 | // Validate the current block. | ||
| 3327 | R_UNLESS(info.m_state == first_state, ResultInvalidCurrentMemory); | ||
| 3328 | R_UNLESS(info.m_permission == first_perm, ResultInvalidCurrentMemory); | ||
| 3329 | R_UNLESS((info.m_attribute | ignore_attr) == (first_attr | ignore_attr), | ||
| 3330 | ResultInvalidCurrentMemory); | ||
| 3331 | |||
| 3332 | // Validate against the provided masks. | ||
| 3333 | R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); | ||
| 3334 | |||
| 3335 | // Break once we're done. | ||
| 3336 | if (last_addr <= info.GetLastAddress()) { | ||
| 3337 | break; | ||
| 3338 | } | ||
| 3339 | |||
| 3340 | // Advance our iterator. | ||
| 3341 | it++; | ||
| 3342 | ASSERT(it != m_memory_block_manager.cend()); | ||
| 3343 | info = it->GetMemoryInfo(); | ||
| 3344 | } | ||
| 3345 | |||
| 3346 | // Write output state. | ||
| 3347 | if (out_state != nullptr) { | ||
| 3348 | *out_state = first_state; | ||
| 3349 | } | ||
| 3350 | if (out_perm != nullptr) { | ||
| 3351 | *out_perm = first_perm; | ||
| 3352 | } | ||
| 3353 | if (out_attr != nullptr) { | ||
| 3354 | *out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr); | ||
| 3355 | } | ||
| 3356 | |||
| 3357 | // If the end address isn't aligned, we need a block. | ||
| 3358 | if (out_blocks_needed != nullptr) { | ||
| 3359 | const size_t blocks_for_end_align = | ||
| 3360 | (Common::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress()) | ||
| 3361 | ? 1 | ||
| 3362 | : 0; | ||
| 3363 | *out_blocks_needed = blocks_for_end_align; | ||
| 3364 | } | ||
| 3365 | |||
| 3366 | R_SUCCEED(); | ||
| 3367 | } | ||
| 3368 | |||
| 3369 | Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, | ||
| 3370 | KMemoryAttribute* out_attr, size_t* out_blocks_needed, | ||
| 3371 | KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 3372 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 3373 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 3374 | KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { | ||
| 3375 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 3376 | |||
| 3377 | // Check memory state. | ||
| 3378 | const KProcessAddress last_addr = addr + size - 1; | ||
| 3379 | KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr); | ||
| 3380 | R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr, | ||
| 3381 | state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr)); | ||
| 3382 | |||
| 3383 | // If the start address isn't aligned, we need a block. | ||
| 3384 | if (out_blocks_needed != nullptr && | ||
| 3385 | Common::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) { | ||
| 3386 | ++(*out_blocks_needed); | ||
| 3387 | } | ||
| 3388 | |||
| 3389 | R_SUCCEED(); | ||
| 3390 | } | ||
| 3391 | |||
| 3392 | Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_KPhysicalAddress, | ||
| 3393 | KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 3394 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 3395 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 3396 | KMemoryAttribute attr, KMemoryPermission new_perm, | ||
| 3397 | KMemoryAttribute lock_attr) { | ||
| 3398 | // Validate basic preconditions. | ||
| 3399 | ASSERT((lock_attr & attr) == KMemoryAttribute::None); | ||
| 3400 | ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) == | ||
| 3401 | KMemoryAttribute::None); | ||
| 3402 | |||
| 3403 | // Validate the lock request. | ||
| 3404 | const size_t num_pages = size / PageSize; | ||
| 3405 | R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); | ||
| 3406 | |||
| 3407 | // Lock the table. | ||
| 3408 | KScopedLightLock lk(m_general_lock); | ||
| 3409 | |||
| 3410 | // Check that the output page group is empty, if it exists. | ||
| 3411 | if (out_pg) { | ||
| 3412 | ASSERT(out_pg->GetNumPages() == 0); | ||
| 3413 | } | ||
| 3414 | |||
| 3415 | // Check the state. | ||
| 3416 | KMemoryState old_state{}; | ||
| 3417 | KMemoryPermission old_perm{}; | ||
| 3418 | KMemoryAttribute old_attr{}; | ||
| 3419 | size_t num_allocator_blocks{}; | ||
| 3420 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), | ||
| 3421 | std::addressof(old_attr), std::addressof(num_allocator_blocks), | ||
| 3422 | addr, size, state_mask | KMemoryState::FlagReferenceCounted, | ||
| 3423 | state | KMemoryState::FlagReferenceCounted, perm_mask, perm, | ||
| 3424 | attr_mask, attr)); | ||
| 3425 | |||
| 3426 | // Get the physical address, if we're supposed to. | ||
| 3427 | if (out_KPhysicalAddress != nullptr) { | ||
| 3428 | ASSERT(this->GetPhysicalAddressLocked(out_KPhysicalAddress, addr)); | ||
| 3429 | } | ||
| 3430 | |||
| 3431 | // Make the page group, if we're supposed to. | ||
| 3432 | if (out_pg != nullptr) { | ||
| 3433 | R_TRY(this->MakePageGroup(*out_pg, addr, num_pages)); | ||
| 3434 | } | ||
| 3435 | |||
| 3436 | // Create an update allocator. | ||
| 3437 | Result allocator_result{ResultSuccess}; | ||
| 3438 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 3439 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 3440 | R_TRY(allocator_result); | ||
| 3441 | |||
| 3442 | // Decide on new perm and attr. | ||
| 3443 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 3444 | KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr); | ||
| 3445 | |||
| 3446 | // Update permission, if we need to. | ||
| 3447 | if (new_perm != old_perm) { | ||
| 3448 | R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); | ||
| 3449 | } | ||
| 3450 | |||
| 3451 | // Apply the memory block updates. | ||
| 3452 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, | ||
| 3453 | new_attr, KMemoryBlockDisableMergeAttribute::Locked, | ||
| 3454 | KMemoryBlockDisableMergeAttribute::None); | ||
| 3455 | |||
| 3456 | // If we have an output page group, open. | ||
| 3457 | if (out_pg) { | ||
| 3458 | out_pg->Open(); | ||
| 3459 | } | ||
| 3460 | |||
| 3461 | R_SUCCEED(); | ||
| 3462 | } | ||
| 3463 | |||
| 3464 | Result KPageTable::UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 3465 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 3466 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 3467 | KMemoryAttribute attr, KMemoryPermission new_perm, | ||
| 3468 | KMemoryAttribute lock_attr, const KPageGroup* pg) { | ||
| 3469 | // Validate basic preconditions. | ||
| 3470 | ASSERT((attr_mask & lock_attr) == lock_attr); | ||
| 3471 | ASSERT((attr & lock_attr) == lock_attr); | ||
| 3472 | |||
| 3473 | // Validate the unlock request. | ||
| 3474 | const size_t num_pages = size / PageSize; | ||
| 3475 | R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); | ||
| 3476 | |||
| 3477 | // Lock the table. | ||
| 3478 | KScopedLightLock lk(m_general_lock); | ||
| 3479 | |||
| 3480 | // Check the state. | ||
| 3481 | KMemoryState old_state{}; | ||
| 3482 | KMemoryPermission old_perm{}; | ||
| 3483 | KMemoryAttribute old_attr{}; | ||
| 3484 | size_t num_allocator_blocks{}; | ||
| 3485 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), | ||
| 3486 | std::addressof(old_attr), std::addressof(num_allocator_blocks), | ||
| 3487 | addr, size, state_mask | KMemoryState::FlagReferenceCounted, | ||
| 3488 | state | KMemoryState::FlagReferenceCounted, perm_mask, perm, | ||
| 3489 | attr_mask, attr)); | ||
| 3490 | |||
| 3491 | // Check the page group. | ||
| 3492 | if (pg != nullptr) { | ||
| 3493 | R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion); | ||
| 3494 | } | ||
| 3495 | |||
| 3496 | // Decide on new perm and attr. | ||
| 3497 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 3498 | KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr); | ||
| 3499 | |||
| 3500 | // Create an update allocator. | ||
| 3501 | Result allocator_result{ResultSuccess}; | ||
| 3502 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 3503 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 3504 | R_TRY(allocator_result); | ||
| 3505 | |||
| 3506 | // Update permission, if we need to. | ||
| 3507 | if (new_perm != old_perm) { | ||
| 3508 | R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions)); | ||
| 3509 | } | ||
| 3510 | |||
| 3511 | // Apply the memory block updates. | ||
| 3512 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, | ||
| 3513 | new_attr, KMemoryBlockDisableMergeAttribute::None, | ||
| 3514 | KMemoryBlockDisableMergeAttribute::Locked); | ||
| 3515 | |||
| 3516 | R_SUCCEED(); | ||
| 3517 | } | ||
| 3518 | |||
| 3519 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 66f16faaf..5541bc13f 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -3,548 +3,14 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include "core/hle/kernel/k_page_table_base.h" |
| 7 | |||
| 8 | #include "common/common_funcs.h" | ||
| 9 | #include "common/page_table.h" | ||
| 10 | #include "core/file_sys/program_metadata.h" | ||
| 11 | #include "core/hle/kernel/k_dynamic_resource_manager.h" | ||
| 12 | #include "core/hle/kernel/k_light_lock.h" | ||
| 13 | #include "core/hle/kernel/k_memory_block.h" | ||
| 14 | #include "core/hle/kernel/k_memory_block_manager.h" | ||
| 15 | #include "core/hle/kernel/k_memory_layout.h" | ||
| 16 | #include "core/hle/kernel/k_memory_manager.h" | ||
| 17 | #include "core/hle/kernel/k_typed_address.h" | ||
| 18 | #include "core/hle/result.h" | ||
| 19 | #include "core/memory.h" | ||
| 20 | |||
| 21 | namespace Core { | ||
| 22 | class System; | ||
| 23 | } | ||
| 24 | 7 | ||
| 25 | namespace Kernel { | 8 | namespace Kernel { |
| 26 | 9 | ||
| 27 | enum class DisableMergeAttribute : u8 { | 10 | class KPageTable final : public KPageTableBase { |
| 28 | None = (0U << 0), | ||
| 29 | DisableHead = (1U << 0), | ||
| 30 | DisableHeadAndBody = (1U << 1), | ||
| 31 | EnableHeadAndBody = (1U << 2), | ||
| 32 | DisableTail = (1U << 3), | ||
| 33 | EnableTail = (1U << 4), | ||
| 34 | EnableAndMergeHeadBodyTail = (1U << 5), | ||
| 35 | EnableHeadBodyTail = EnableHeadAndBody | EnableTail, | ||
| 36 | DisableHeadBodyTail = DisableHeadAndBody | DisableTail, | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct KPageProperties { | ||
| 40 | KMemoryPermission perm; | ||
| 41 | bool io; | ||
| 42 | bool uncached; | ||
| 43 | DisableMergeAttribute disable_merge_attributes; | ||
| 44 | }; | ||
| 45 | static_assert(std::is_trivial_v<KPageProperties>); | ||
| 46 | static_assert(sizeof(KPageProperties) == sizeof(u32)); | ||
| 47 | |||
| 48 | class KBlockInfoManager; | ||
| 49 | class KMemoryBlockManager; | ||
| 50 | class KResourceLimit; | ||
| 51 | class KSystemResource; | ||
| 52 | |||
| 53 | class KPageTable final { | ||
| 54 | protected: | ||
| 55 | struct PageLinkedList; | ||
| 56 | |||
| 57 | public: | ||
| 58 | enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; | ||
| 59 | |||
| 60 | YUZU_NON_COPYABLE(KPageTable); | ||
| 61 | YUZU_NON_MOVEABLE(KPageTable); | ||
| 62 | |||
| 63 | explicit KPageTable(Core::System& system_); | ||
| 64 | ~KPageTable(); | ||
| 65 | |||
| 66 | Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, | ||
| 67 | bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, | ||
| 68 | KProcessAddress code_addr, size_t code_size, | ||
| 69 | KSystemResource* system_resource, KResourceLimit* resource_limit, | ||
| 70 | Core::Memory::Memory& memory); | ||
| 71 | |||
| 72 | void Finalize(); | ||
| 73 | |||
| 74 | Result MapProcessCode(KProcessAddress addr, size_t pages_count, KMemoryState state, | ||
| 75 | KMemoryPermission perm); | ||
| 76 | Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); | ||
| 77 | Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size, | ||
| 78 | ICacheInvalidationStrategy icache_invalidation_strategy); | ||
| 79 | Result UnmapProcessMemory(KProcessAddress dst_addr, size_t size, KPageTable& src_page_table, | ||
| 80 | KProcessAddress src_addr); | ||
| 81 | Result MapPhysicalMemory(KProcessAddress addr, size_t size); | ||
| 82 | Result UnmapPhysicalMemory(KProcessAddress addr, size_t size); | ||
| 83 | Result MapMemory(KProcessAddress dst_addr, KProcessAddress src_addr, size_t size); | ||
| 84 | Result UnmapMemory(KProcessAddress dst_addr, KProcessAddress src_addr, size_t size); | ||
| 85 | Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, | ||
| 86 | Svc::MemoryPermission svc_perm); | ||
| 87 | KMemoryInfo QueryInfo(KProcessAddress addr); | ||
| 88 | Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm); | ||
| 89 | Result SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr); | ||
| 90 | Result SetMaxHeapSize(size_t size); | ||
| 91 | Result SetHeapSize(u64* out, size_t size); | ||
| 92 | Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size, | ||
| 93 | KMemoryPermission perm, bool is_aligned, bool check_heap); | ||
| 94 | Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap); | ||
| 95 | |||
| 96 | Result UnlockForDeviceAddressSpace(KProcessAddress addr, size_t size); | ||
| 97 | |||
| 98 | Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size); | ||
| 99 | Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size); | ||
| 100 | |||
| 101 | Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr, | ||
| 102 | KPageTable& src_page_table, KMemoryPermission test_perm, | ||
| 103 | KMemoryState dst_state, bool send); | ||
| 104 | Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state); | ||
| 105 | Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); | ||
| 106 | |||
| 107 | Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, | ||
| 108 | KMemoryPermission perm); | ||
| 109 | Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg); | ||
| 110 | Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size); | ||
| 111 | Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg); | ||
| 112 | Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, | ||
| 113 | KMemoryState state_mask, KMemoryState state, | ||
| 114 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 115 | KMemoryAttribute attr_mask, KMemoryAttribute attr); | ||
| 116 | |||
| 117 | Common::PageTable& PageTableImpl() { | ||
| 118 | return *m_page_table_impl; | ||
| 119 | } | ||
| 120 | |||
| 121 | const Common::PageTable& PageTableImpl() const { | ||
| 122 | return *m_page_table_impl; | ||
| 123 | } | ||
| 124 | |||
| 125 | KBlockInfoManager* GetBlockInfoManager() { | ||
| 126 | return m_block_info_manager; | ||
| 127 | } | ||
| 128 | |||
| 129 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, | ||
| 130 | KPhysicalAddress phys_addr, KProcessAddress region_start, | ||
| 131 | size_t region_num_pages, KMemoryState state, KMemoryPermission perm) { | ||
| 132 | R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start, | ||
| 133 | region_num_pages, state, perm)); | ||
| 134 | } | ||
| 135 | |||
| 136 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, | ||
| 137 | KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) { | ||
| 138 | R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, | ||
| 139 | this->GetRegionAddress(state), | ||
| 140 | this->GetRegionSize(state) / PageSize, state, perm)); | ||
| 141 | } | ||
| 142 | |||
| 143 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state, | ||
| 144 | KMemoryPermission perm) { | ||
| 145 | R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false, | ||
| 146 | this->GetRegionAddress(state), | ||
| 147 | this->GetRegionSize(state) / PageSize, state, perm)); | ||
| 148 | } | ||
| 149 | |||
| 150 | Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, | ||
| 151 | KMemoryPermission perm); | ||
| 152 | Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state); | ||
| 153 | |||
| 154 | Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg, | ||
| 155 | KProcessAddress region_start, size_t region_num_pages, KMemoryState state, | ||
| 156 | KMemoryPermission perm); | ||
| 157 | Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state, | ||
| 158 | KMemoryPermission perm); | ||
| 159 | Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state); | ||
| 160 | void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size, | ||
| 161 | const KPageGroup& pg); | ||
| 162 | |||
| 163 | KProcessAddress GetRegionAddress(Svc::MemoryState state) const; | ||
| 164 | size_t GetRegionSize(Svc::MemoryState state) const; | ||
| 165 | bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const; | ||
| 166 | |||
| 167 | KProcessAddress GetRegionAddress(KMemoryState state) const { | ||
| 168 | return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask)); | ||
| 169 | } | ||
| 170 | size_t GetRegionSize(KMemoryState state) const { | ||
| 171 | return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask)); | ||
| 172 | } | ||
| 173 | bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { | ||
| 174 | return this->CanContain(addr, size, | ||
| 175 | static_cast<Svc::MemoryState>(state & KMemoryState::Mask)); | ||
| 176 | } | ||
| 177 | |||
| 178 | protected: | ||
| 179 | struct PageLinkedList { | ||
| 180 | private: | ||
| 181 | struct Node { | ||
| 182 | Node* m_next; | ||
| 183 | std::array<u8, PageSize - sizeof(Node*)> m_buffer; | ||
| 184 | }; | ||
| 185 | |||
| 186 | public: | ||
| 187 | constexpr PageLinkedList() = default; | ||
| 188 | |||
| 189 | void Push(Node* n) { | ||
| 190 | ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize)); | ||
| 191 | n->m_next = m_root; | ||
| 192 | m_root = n; | ||
| 193 | } | ||
| 194 | |||
| 195 | void Push(Core::Memory::Memory& memory, KVirtualAddress addr) { | ||
| 196 | this->Push(memory.GetPointer<Node>(GetInteger(addr))); | ||
| 197 | } | ||
| 198 | |||
| 199 | Node* Peek() const { | ||
| 200 | return m_root; | ||
| 201 | } | ||
| 202 | |||
| 203 | Node* Pop() { | ||
| 204 | Node* const r = m_root; | ||
| 205 | |||
| 206 | m_root = r->m_next; | ||
| 207 | r->m_next = nullptr; | ||
| 208 | |||
| 209 | return r; | ||
| 210 | } | ||
| 211 | |||
| 212 | private: | ||
| 213 | Node* m_root{}; | ||
| 214 | }; | ||
| 215 | static_assert(std::is_trivially_destructible<PageLinkedList>::value); | ||
| 216 | |||
| 217 | private: | ||
| 218 | enum class OperationType : u32 { | ||
| 219 | Map = 0, | ||
| 220 | MapGroup = 1, | ||
| 221 | MapFirstGroup = 2, | ||
| 222 | Unmap = 3, | ||
| 223 | ChangePermissions = 4, | ||
| 224 | ChangePermissionsAndRefresh = 5, | ||
| 225 | ChangePermissionsAndRefreshAndFlush = 6, | ||
| 226 | Separate = 7, | ||
| 227 | }; | ||
| 228 | |||
| 229 | static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = | ||
| 230 | KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; | ||
| 231 | |||
| 232 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, | ||
| 233 | KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, | ||
| 234 | size_t region_num_pages, KMemoryState state, KMemoryPermission perm); | ||
| 235 | bool IsRegionContiguous(KProcessAddress addr, u64 size) const; | ||
| 236 | void AddRegionToPages(KProcessAddress start, size_t num_pages, KPageGroup& page_linked_list); | ||
| 237 | KMemoryInfo QueryInfoImpl(KProcessAddress addr); | ||
| 238 | KProcessAddress AllocateVirtualMemory(KProcessAddress start, size_t region_num_pages, | ||
| 239 | u64 needed_num_pages, size_t align); | ||
| 240 | Result Operate(KProcessAddress addr, size_t num_pages, const KPageGroup& page_group, | ||
| 241 | OperationType operation); | ||
| 242 | Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm, | ||
| 243 | OperationType operation, KPhysicalAddress map_addr = 0); | ||
| 244 | void FinalizeUpdate(PageLinkedList* page_list); | ||
| 245 | |||
| 246 | KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages, | ||
| 247 | size_t num_pages, size_t alignment, size_t offset, | ||
| 248 | size_t guard_pages); | ||
| 249 | |||
| 250 | Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size, | ||
| 251 | KMemoryState state_mask, KMemoryState state, | ||
| 252 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 253 | KMemoryAttribute attr_mask, KMemoryAttribute attr) const; | ||
| 254 | Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 255 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 256 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 257 | KMemoryAttribute attr) const { | ||
| 258 | R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, | ||
| 259 | perm, attr_mask, attr)); | ||
| 260 | } | ||
| 261 | |||
| 262 | Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state, | ||
| 263 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 264 | KMemoryAttribute attr_mask, KMemoryAttribute attr) const; | ||
| 265 | Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, | ||
| 266 | KMemoryAttribute* out_attr, size_t* out_blocks_needed, | ||
| 267 | KMemoryBlockManager::const_iterator it, KProcessAddress last_addr, | ||
| 268 | KMemoryState state_mask, KMemoryState state, | ||
| 269 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 270 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 271 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; | ||
| 272 | Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, | ||
| 273 | KMemoryAttribute* out_attr, size_t* out_blocks_needed, | ||
| 274 | KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 275 | KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 276 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 277 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; | ||
| 278 | Result CheckMemoryState(size_t* out_blocks_needed, KProcessAddress addr, size_t size, | ||
| 279 | KMemoryState state_mask, KMemoryState state, | ||
| 280 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 281 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 282 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { | ||
| 283 | R_RETURN(CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, | ||
| 284 | state_mask, state, perm_mask, perm, attr_mask, attr, | ||
| 285 | ignore_attr)); | ||
| 286 | } | ||
| 287 | Result CheckMemoryState(KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 288 | KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 289 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 290 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { | ||
| 291 | R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, | ||
| 292 | attr_mask, attr, ignore_attr)); | ||
| 293 | } | ||
| 294 | |||
| 295 | Result LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_KPhysicalAddress, | ||
| 296 | KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 297 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 298 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 299 | KMemoryAttribute attr, KMemoryPermission new_perm, | ||
| 300 | KMemoryAttribute lock_attr); | ||
| 301 | Result UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 302 | KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 303 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 304 | KMemoryPermission new_perm, KMemoryAttribute lock_attr, | ||
| 305 | const KPageGroup* pg); | ||
| 306 | |||
| 307 | Result MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages); | ||
| 308 | bool IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages); | ||
| 309 | |||
| 310 | bool IsLockedByCurrentThread() const { | ||
| 311 | return m_general_lock.IsLockedByCurrentThread(); | ||
| 312 | } | ||
| 313 | |||
| 314 | bool IsHeapPhysicalAddress(const KMemoryLayout& layout, KPhysicalAddress phys_addr) { | ||
| 315 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 316 | |||
| 317 | return layout.IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr); | ||
| 318 | } | ||
| 319 | |||
| 320 | bool GetPhysicalAddressLocked(KPhysicalAddress* out, KProcessAddress virt_addr) const { | ||
| 321 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 322 | |||
| 323 | *out = GetPhysicalAddr(virt_addr); | ||
| 324 | |||
| 325 | return *out != 0; | ||
| 326 | } | ||
| 327 | |||
| 328 | Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed, | ||
| 329 | KProcessAddress address, size_t size, KMemoryPermission test_perm, | ||
| 330 | KMemoryState dst_state); | ||
| 331 | Result SetupForIpcServer(KProcessAddress* out_addr, size_t size, KProcessAddress src_addr, | ||
| 332 | KMemoryPermission test_perm, KMemoryState dst_state, | ||
| 333 | KPageTable& src_page_table, bool send); | ||
| 334 | void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, KProcessAddress address, | ||
| 335 | size_t size, KMemoryPermission prot_perm); | ||
| 336 | |||
| 337 | Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address, | ||
| 338 | size_t num_pages, KMemoryPermission perm); | ||
| 339 | Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address, | ||
| 340 | const KPageGroup& pg, const KPageProperties properties, bool reuse_ll); | ||
| 341 | |||
| 342 | mutable KLightLock m_general_lock; | ||
| 343 | mutable KLightLock m_map_physical_memory_lock; | ||
| 344 | |||
| 345 | public: | ||
| 346 | constexpr KProcessAddress GetAddressSpaceStart() const { | ||
| 347 | return m_address_space_start; | ||
| 348 | } | ||
| 349 | constexpr KProcessAddress GetAddressSpaceEnd() const { | ||
| 350 | return m_address_space_end; | ||
| 351 | } | ||
| 352 | constexpr size_t GetAddressSpaceSize() const { | ||
| 353 | return m_address_space_end - m_address_space_start; | ||
| 354 | } | ||
| 355 | constexpr KProcessAddress GetHeapRegionStart() const { | ||
| 356 | return m_heap_region_start; | ||
| 357 | } | ||
| 358 | constexpr KProcessAddress GetHeapRegionEnd() const { | ||
| 359 | return m_heap_region_end; | ||
| 360 | } | ||
| 361 | constexpr size_t GetHeapRegionSize() const { | ||
| 362 | return m_heap_region_end - m_heap_region_start; | ||
| 363 | } | ||
| 364 | constexpr KProcessAddress GetAliasRegionStart() const { | ||
| 365 | return m_alias_region_start; | ||
| 366 | } | ||
| 367 | constexpr KProcessAddress GetAliasRegionEnd() const { | ||
| 368 | return m_alias_region_end; | ||
| 369 | } | ||
| 370 | constexpr size_t GetAliasRegionSize() const { | ||
| 371 | return m_alias_region_end - m_alias_region_start; | ||
| 372 | } | ||
| 373 | constexpr KProcessAddress GetStackRegionStart() const { | ||
| 374 | return m_stack_region_start; | ||
| 375 | } | ||
| 376 | constexpr KProcessAddress GetStackRegionEnd() const { | ||
| 377 | return m_stack_region_end; | ||
| 378 | } | ||
| 379 | constexpr size_t GetStackRegionSize() const { | ||
| 380 | return m_stack_region_end - m_stack_region_start; | ||
| 381 | } | ||
| 382 | constexpr KProcessAddress GetKernelMapRegionStart() const { | ||
| 383 | return m_kernel_map_region_start; | ||
| 384 | } | ||
| 385 | constexpr KProcessAddress GetKernelMapRegionEnd() const { | ||
| 386 | return m_kernel_map_region_end; | ||
| 387 | } | ||
| 388 | constexpr KProcessAddress GetCodeRegionStart() const { | ||
| 389 | return m_code_region_start; | ||
| 390 | } | ||
| 391 | constexpr KProcessAddress GetCodeRegionEnd() const { | ||
| 392 | return m_code_region_end; | ||
| 393 | } | ||
| 394 | constexpr KProcessAddress GetAliasCodeRegionStart() const { | ||
| 395 | return m_alias_code_region_start; | ||
| 396 | } | ||
| 397 | constexpr KProcessAddress GetAliasCodeRegionEnd() const { | ||
| 398 | return m_alias_code_region_end; | ||
| 399 | } | ||
| 400 | constexpr size_t GetAliasCodeRegionSize() const { | ||
| 401 | return m_alias_code_region_end - m_alias_code_region_start; | ||
| 402 | } | ||
| 403 | size_t GetNormalMemorySize() const { | ||
| 404 | KScopedLightLock lk(m_general_lock); | ||
| 405 | return GetHeapSize() + m_mapped_physical_memory_size; | ||
| 406 | } | ||
| 407 | constexpr size_t GetAddressSpaceWidth() const { | ||
| 408 | return m_address_space_width; | ||
| 409 | } | ||
| 410 | constexpr size_t GetHeapSize() const { | ||
| 411 | return m_current_heap_end - m_heap_region_start; | ||
| 412 | } | ||
| 413 | constexpr size_t GetNumGuardPages() const { | ||
| 414 | return IsKernel() ? 1 : 4; | ||
| 415 | } | ||
| 416 | KPhysicalAddress GetPhysicalAddr(KProcessAddress addr) const { | ||
| 417 | const auto backing_addr = m_page_table_impl->backing_addr[addr >> PageBits]; | ||
| 418 | ASSERT(backing_addr); | ||
| 419 | return backing_addr + GetInteger(addr); | ||
| 420 | } | ||
| 421 | constexpr bool Contains(KProcessAddress addr) const { | ||
| 422 | return m_address_space_start <= addr && addr <= m_address_space_end - 1; | ||
| 423 | } | ||
| 424 | constexpr bool Contains(KProcessAddress addr, size_t size) const { | ||
| 425 | return m_address_space_start <= addr && addr < addr + size && | ||
| 426 | addr + size - 1 <= m_address_space_end - 1; | ||
| 427 | } | ||
| 428 | constexpr bool IsInAliasRegion(KProcessAddress addr, size_t size) const { | ||
| 429 | return this->Contains(addr, size) && m_alias_region_start <= addr && | ||
| 430 | addr + size - 1 <= m_alias_region_end - 1; | ||
| 431 | } | ||
| 432 | constexpr bool IsInHeapRegion(KProcessAddress addr, size_t size) const { | ||
| 433 | return this->Contains(addr, size) && m_heap_region_start <= addr && | ||
| 434 | addr + size - 1 <= m_heap_region_end - 1; | ||
| 435 | } | ||
| 436 | |||
| 437 | public: | 11 | public: |
| 438 | static KVirtualAddress GetLinearMappedVirtualAddress(const KMemoryLayout& layout, | 12 | explicit KPageTable(KernelCore& kernel) : KPageTableBase(kernel) {} |
| 439 | KPhysicalAddress addr) { | 13 | ~KPageTable() = default; |
| 440 | return layout.GetLinearVirtualAddress(addr); | ||
| 441 | } | ||
| 442 | |||
| 443 | static KPhysicalAddress GetLinearMappedPhysicalAddress(const KMemoryLayout& layout, | ||
| 444 | KVirtualAddress addr) { | ||
| 445 | return layout.GetLinearPhysicalAddress(addr); | ||
| 446 | } | ||
| 447 | |||
| 448 | static KVirtualAddress GetHeapVirtualAddress(const KMemoryLayout& layout, | ||
| 449 | KPhysicalAddress addr) { | ||
| 450 | return GetLinearMappedVirtualAddress(layout, addr); | ||
| 451 | } | ||
| 452 | |||
| 453 | static KPhysicalAddress GetHeapPhysicalAddress(const KMemoryLayout& layout, | ||
| 454 | KVirtualAddress addr) { | ||
| 455 | return GetLinearMappedPhysicalAddress(layout, addr); | ||
| 456 | } | ||
| 457 | |||
| 458 | static KVirtualAddress GetPageTableVirtualAddress(const KMemoryLayout& layout, | ||
| 459 | KPhysicalAddress addr) { | ||
| 460 | return GetLinearMappedVirtualAddress(layout, addr); | ||
| 461 | } | ||
| 462 | |||
| 463 | static KPhysicalAddress GetPageTablePhysicalAddress(const KMemoryLayout& layout, | ||
| 464 | KVirtualAddress addr) { | ||
| 465 | return GetLinearMappedPhysicalAddress(layout, addr); | ||
| 466 | } | ||
| 467 | |||
| 468 | private: | ||
| 469 | constexpr bool IsKernel() const { | ||
| 470 | return m_is_kernel; | ||
| 471 | } | ||
| 472 | constexpr bool IsAslrEnabled() const { | ||
| 473 | return m_enable_aslr; | ||
| 474 | } | ||
| 475 | |||
| 476 | constexpr bool ContainsPages(KProcessAddress addr, size_t num_pages) const { | ||
| 477 | return (m_address_space_start <= addr) && | ||
| 478 | (num_pages <= (m_address_space_end - m_address_space_start) / PageSize) && | ||
| 479 | (addr + num_pages * PageSize - 1 <= m_address_space_end - 1); | ||
| 480 | } | ||
| 481 | |||
| 482 | private: | ||
| 483 | class KScopedPageTableUpdater { | ||
| 484 | private: | ||
| 485 | KPageTable* m_pt{}; | ||
| 486 | PageLinkedList m_ll; | ||
| 487 | |||
| 488 | public: | ||
| 489 | explicit KScopedPageTableUpdater(KPageTable* pt) : m_pt(pt) {} | ||
| 490 | explicit KScopedPageTableUpdater(KPageTable& pt) : KScopedPageTableUpdater(&pt) {} | ||
| 491 | ~KScopedPageTableUpdater() { | ||
| 492 | m_pt->FinalizeUpdate(this->GetPageList()); | ||
| 493 | } | ||
| 494 | |||
| 495 | PageLinkedList* GetPageList() { | ||
| 496 | return std::addressof(m_ll); | ||
| 497 | } | ||
| 498 | }; | ||
| 499 | |||
| 500 | private: | ||
| 501 | KProcessAddress m_address_space_start{}; | ||
| 502 | KProcessAddress m_address_space_end{}; | ||
| 503 | KProcessAddress m_heap_region_start{}; | ||
| 504 | KProcessAddress m_heap_region_end{}; | ||
| 505 | KProcessAddress m_current_heap_end{}; | ||
| 506 | KProcessAddress m_alias_region_start{}; | ||
| 507 | KProcessAddress m_alias_region_end{}; | ||
| 508 | KProcessAddress m_stack_region_start{}; | ||
| 509 | KProcessAddress m_stack_region_end{}; | ||
| 510 | KProcessAddress m_kernel_map_region_start{}; | ||
| 511 | KProcessAddress m_kernel_map_region_end{}; | ||
| 512 | KProcessAddress m_code_region_start{}; | ||
| 513 | KProcessAddress m_code_region_end{}; | ||
| 514 | KProcessAddress m_alias_code_region_start{}; | ||
| 515 | KProcessAddress m_alias_code_region_end{}; | ||
| 516 | |||
| 517 | size_t m_max_heap_size{}; | ||
| 518 | size_t m_mapped_physical_memory_size{}; | ||
| 519 | size_t m_mapped_unsafe_physical_memory{}; | ||
| 520 | size_t m_mapped_insecure_memory{}; | ||
| 521 | size_t m_mapped_ipc_server_memory{}; | ||
| 522 | size_t m_address_space_width{}; | ||
| 523 | |||
| 524 | KMemoryBlockManager m_memory_block_manager; | ||
| 525 | u32 m_allocate_option{}; | ||
| 526 | |||
| 527 | bool m_is_kernel{}; | ||
| 528 | bool m_enable_aslr{}; | ||
| 529 | bool m_enable_device_address_space_merge{}; | ||
| 530 | |||
| 531 | KMemoryBlockSlabManager* m_memory_block_slab_manager{}; | ||
| 532 | KBlockInfoManager* m_block_info_manager{}; | ||
| 533 | KResourceLimit* m_resource_limit{}; | ||
| 534 | |||
| 535 | u32 m_heap_fill_value{}; | ||
| 536 | u32 m_ipc_fill_value{}; | ||
| 537 | u32 m_stack_fill_value{}; | ||
| 538 | const KMemoryRegion* m_cached_physical_heap_region{}; | ||
| 539 | |||
| 540 | KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application}; | ||
| 541 | KMemoryManager::Direction m_allocation_option{KMemoryManager::Direction::FromFront}; | ||
| 542 | |||
| 543 | std::unique_ptr<Common::PageTable> m_page_table_impl; | ||
| 544 | |||
| 545 | Core::System& m_system; | ||
| 546 | KernelCore& m_kernel; | ||
| 547 | Core::Memory::Memory* m_memory{}; | ||
| 548 | }; | 14 | }; |
| 549 | 15 | ||
| 550 | } // namespace Kernel | 16 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp new file mode 100644 index 000000000..47dc8fd35 --- /dev/null +++ b/src/core/hle/kernel/k_page_table_base.cpp | |||
| @@ -0,0 +1,5716 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/scope_exit.h" | ||
| 5 | #include "common/settings.h" | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/hle/kernel/k_address_space_info.h" | ||
| 8 | #include "core/hle/kernel/k_page_table_base.h" | ||
| 9 | #include "core/hle/kernel/k_scoped_resource_reservation.h" | ||
| 10 | #include "core/hle/kernel/k_system_resource.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | namespace { | ||
| 15 | |||
| 16 | class KScopedLightLockPair { | ||
| 17 | YUZU_NON_COPYABLE(KScopedLightLockPair); | ||
| 18 | YUZU_NON_MOVEABLE(KScopedLightLockPair); | ||
| 19 | |||
| 20 | private: | ||
| 21 | KLightLock* m_lower; | ||
| 22 | KLightLock* m_upper; | ||
| 23 | |||
| 24 | public: | ||
| 25 | KScopedLightLockPair(KLightLock& lhs, KLightLock& rhs) { | ||
| 26 | // Ensure our locks are in a consistent order. | ||
| 27 | if (std::addressof(lhs) <= std::addressof(rhs)) { | ||
| 28 | m_lower = std::addressof(lhs); | ||
| 29 | m_upper = std::addressof(rhs); | ||
| 30 | } else { | ||
| 31 | m_lower = std::addressof(rhs); | ||
| 32 | m_upper = std::addressof(lhs); | ||
| 33 | } | ||
| 34 | |||
| 35 | // Acquire both locks. | ||
| 36 | m_lower->Lock(); | ||
| 37 | if (m_lower != m_upper) { | ||
| 38 | m_upper->Lock(); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | ~KScopedLightLockPair() { | ||
| 43 | // Unlock the upper lock. | ||
| 44 | if (m_upper != nullptr && m_upper != m_lower) { | ||
| 45 | m_upper->Unlock(); | ||
| 46 | } | ||
| 47 | |||
| 48 | // Unlock the lower lock. | ||
| 49 | if (m_lower != nullptr) { | ||
| 50 | m_lower->Unlock(); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | public: | ||
| 55 | // Utility. | ||
| 56 | void TryUnlockHalf(KLightLock& lock) { | ||
| 57 | // Only allow unlocking if the lock is half the pair. | ||
| 58 | if (m_lower != m_upper) { | ||
| 59 | // We want to be sure the lock is one we own. | ||
| 60 | if (m_lower == std::addressof(lock)) { | ||
| 61 | lock.Unlock(); | ||
| 62 | m_lower = nullptr; | ||
| 63 | } else if (m_upper == std::addressof(lock)) { | ||
| 64 | lock.Unlock(); | ||
| 65 | m_upper = nullptr; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | }; | ||
| 70 | |||
| 71 | template <typename AddressType> | ||
| 72 | void InvalidateInstructionCache(Core::System& system, AddressType addr, u64 size) { | ||
| 73 | system.InvalidateCpuInstructionCacheRange(GetInteger(addr), size); | ||
| 74 | } | ||
| 75 | |||
| 76 | template <typename AddressType> | ||
| 77 | Result InvalidateDataCache(AddressType addr, u64 size) { | ||
| 78 | R_SUCCEED(); | ||
| 79 | } | ||
| 80 | |||
| 81 | template <typename AddressType> | ||
| 82 | Result StoreDataCache(AddressType addr, u64 size) { | ||
| 83 | R_SUCCEED(); | ||
| 84 | } | ||
| 85 | |||
| 86 | template <typename AddressType> | ||
| 87 | Result FlushDataCache(AddressType addr, u64 size) { | ||
| 88 | R_SUCCEED(); | ||
| 89 | } | ||
| 90 | |||
| 91 | } // namespace | ||
| 92 | |||
| 93 | void KPageTableBase::MemoryRange::Open() { | ||
| 94 | // If the range contains heap pages, open them. | ||
| 95 | if (this->IsHeap()) { | ||
| 96 | m_kernel.MemoryManager().Open(this->GetAddress(), this->GetSize() / PageSize); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | void KPageTableBase::MemoryRange::Close() { | ||
| 101 | // If the range contains heap pages, close them. | ||
| 102 | if (this->IsHeap()) { | ||
| 103 | m_kernel.MemoryManager().Close(this->GetAddress(), this->GetSize() / PageSize); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | KPageTableBase::KPageTableBase(KernelCore& kernel) | ||
| 108 | : m_kernel(kernel), m_system(kernel.System()), m_general_lock(kernel), | ||
| 109 | m_map_physical_memory_lock(kernel), m_device_map_lock(kernel) {} | ||
| 110 | KPageTableBase::~KPageTableBase() = default; | ||
| 111 | |||
| 112 | Result KPageTableBase::InitializeForKernel(bool is_64_bit, KVirtualAddress start, | ||
| 113 | KVirtualAddress end, Core::Memory::Memory& memory) { | ||
| 114 | // Initialize our members. | ||
| 115 | m_address_space_width = | ||
| 116 | static_cast<u32>(is_64_bit ? Common::BitSize<u64>() : Common::BitSize<u32>()); | ||
| 117 | m_address_space_start = KProcessAddress(GetInteger(start)); | ||
| 118 | m_address_space_end = KProcessAddress(GetInteger(end)); | ||
| 119 | m_is_kernel = true; | ||
| 120 | m_enable_aslr = true; | ||
| 121 | m_enable_device_address_space_merge = false; | ||
| 122 | |||
| 123 | m_heap_region_start = 0; | ||
| 124 | m_heap_region_end = 0; | ||
| 125 | m_current_heap_end = 0; | ||
| 126 | m_alias_region_start = 0; | ||
| 127 | m_alias_region_end = 0; | ||
| 128 | m_stack_region_start = 0; | ||
| 129 | m_stack_region_end = 0; | ||
| 130 | m_kernel_map_region_start = 0; | ||
| 131 | m_kernel_map_region_end = 0; | ||
| 132 | m_alias_code_region_start = 0; | ||
| 133 | m_alias_code_region_end = 0; | ||
| 134 | m_code_region_start = 0; | ||
| 135 | m_code_region_end = 0; | ||
| 136 | m_max_heap_size = 0; | ||
| 137 | m_mapped_physical_memory_size = 0; | ||
| 138 | m_mapped_unsafe_physical_memory = 0; | ||
| 139 | m_mapped_insecure_memory = 0; | ||
| 140 | m_mapped_ipc_server_memory = 0; | ||
| 141 | |||
| 142 | m_memory_block_slab_manager = | ||
| 143 | m_kernel.GetSystemSystemResource().GetMemoryBlockSlabManagerPointer(); | ||
| 144 | m_block_info_manager = m_kernel.GetSystemSystemResource().GetBlockInfoManagerPointer(); | ||
| 145 | m_resource_limit = m_kernel.GetSystemResourceLimit(); | ||
| 146 | |||
| 147 | m_allocate_option = KMemoryManager::EncodeOption(KMemoryManager::Pool::System, | ||
| 148 | KMemoryManager::Direction::FromFront); | ||
| 149 | m_heap_fill_value = MemoryFillValue_Zero; | ||
| 150 | m_ipc_fill_value = MemoryFillValue_Zero; | ||
| 151 | m_stack_fill_value = MemoryFillValue_Zero; | ||
| 152 | |||
| 153 | m_cached_physical_linear_region = nullptr; | ||
| 154 | m_cached_physical_heap_region = nullptr; | ||
| 155 | |||
| 156 | // Initialize our implementation. | ||
| 157 | m_impl = std::make_unique<Common::PageTable>(); | ||
| 158 | m_impl->Resize(m_address_space_width, PageBits); | ||
| 159 | |||
| 160 | // Set the tracking memory. | ||
| 161 | m_memory = std::addressof(memory); | ||
| 162 | |||
| 163 | // Initialize our memory block manager. | ||
| 164 | R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end, | ||
| 165 | m_memory_block_slab_manager)); | ||
| 166 | } | ||
| 167 | |||
| 168 | Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, | ||
| 169 | bool enable_das_merge, bool from_back, | ||
| 170 | KMemoryManager::Pool pool, KProcessAddress code_address, | ||
| 171 | size_t code_size, KSystemResource* system_resource, | ||
| 172 | KResourceLimit* resource_limit, | ||
| 173 | Core::Memory::Memory& memory) { | ||
| 174 | // Calculate region extents. | ||
| 175 | const size_t as_width = GetAddressSpaceWidth(as_type); | ||
| 176 | const KProcessAddress start = 0; | ||
| 177 | const KProcessAddress end = (1ULL << as_width); | ||
| 178 | |||
| 179 | // Validate the region. | ||
| 180 | ASSERT(start <= code_address); | ||
| 181 | ASSERT(code_address < code_address + code_size); | ||
| 182 | ASSERT(code_address + code_size - 1 <= end - 1); | ||
| 183 | |||
| 184 | // Define helpers. | ||
| 185 | auto GetSpaceStart = [&](KAddressSpaceInfo::Type type) { | ||
| 186 | return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type); | ||
| 187 | }; | ||
| 188 | auto GetSpaceSize = [&](KAddressSpaceInfo::Type type) { | ||
| 189 | return KAddressSpaceInfo::GetAddressSpaceSize(m_address_space_width, type); | ||
| 190 | }; | ||
| 191 | |||
| 192 | // Set our bit width and heap/alias sizes. | ||
| 193 | m_address_space_width = static_cast<u32>(GetAddressSpaceWidth(as_type)); | ||
| 194 | size_t alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias); | ||
| 195 | size_t heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap); | ||
| 196 | |||
| 197 | // Adjust heap/alias size if we don't have an alias region. | ||
| 198 | if ((as_type & Svc::CreateProcessFlag::AddressSpaceMask) == | ||
| 199 | Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias) { | ||
| 200 | heap_region_size += alias_region_size; | ||
| 201 | alias_region_size = 0; | ||
| 202 | } | ||
| 203 | |||
| 204 | // Set code regions and determine remaining sizes. | ||
| 205 | KProcessAddress process_code_start; | ||
| 206 | KProcessAddress process_code_end; | ||
| 207 | size_t stack_region_size; | ||
| 208 | size_t kernel_map_region_size; | ||
| 209 | if (m_address_space_width == 39) { | ||
| 210 | alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias); | ||
| 211 | heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap); | ||
| 212 | stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack); | ||
| 213 | kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); | ||
| 214 | m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit); | ||
| 215 | m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit); | ||
| 216 | m_alias_code_region_start = m_code_region_start; | ||
| 217 | m_alias_code_region_end = m_code_region_end; | ||
| 218 | process_code_start = Common::AlignDown(GetInteger(code_address), RegionAlignment); | ||
| 219 | process_code_end = Common::AlignUp(GetInteger(code_address) + code_size, RegionAlignment); | ||
| 220 | } else { | ||
| 221 | stack_region_size = 0; | ||
| 222 | kernel_map_region_size = 0; | ||
| 223 | m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall); | ||
| 224 | m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); | ||
| 225 | m_stack_region_start = m_code_region_start; | ||
| 226 | m_alias_code_region_start = m_code_region_start; | ||
| 227 | m_alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) + | ||
| 228 | GetSpaceSize(KAddressSpaceInfo::Type::MapLarge); | ||
| 229 | m_stack_region_end = m_code_region_end; | ||
| 230 | m_kernel_map_region_start = m_code_region_start; | ||
| 231 | m_kernel_map_region_end = m_code_region_end; | ||
| 232 | process_code_start = m_code_region_start; | ||
| 233 | process_code_end = m_code_region_end; | ||
| 234 | } | ||
| 235 | |||
| 236 | // Set other basic fields. | ||
| 237 | m_enable_aslr = enable_aslr; | ||
| 238 | m_enable_device_address_space_merge = enable_das_merge; | ||
| 239 | m_address_space_start = start; | ||
| 240 | m_address_space_end = end; | ||
| 241 | m_is_kernel = false; | ||
| 242 | m_memory_block_slab_manager = system_resource->GetMemoryBlockSlabManagerPointer(); | ||
| 243 | m_block_info_manager = system_resource->GetBlockInfoManagerPointer(); | ||
| 244 | m_resource_limit = resource_limit; | ||
| 245 | |||
| 246 | // Determine the region we can place our undetermineds in. | ||
| 247 | KProcessAddress alloc_start; | ||
| 248 | size_t alloc_size; | ||
| 249 | if ((GetInteger(process_code_start) - GetInteger(m_code_region_start)) >= | ||
| 250 | (GetInteger(end) - GetInteger(process_code_end))) { | ||
| 251 | alloc_start = m_code_region_start; | ||
| 252 | alloc_size = GetInteger(process_code_start) - GetInteger(m_code_region_start); | ||
| 253 | } else { | ||
| 254 | alloc_start = process_code_end; | ||
| 255 | alloc_size = GetInteger(end) - GetInteger(process_code_end); | ||
| 256 | } | ||
| 257 | const size_t needed_size = | ||
| 258 | (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size); | ||
| 259 | R_UNLESS(alloc_size >= needed_size, ResultOutOfMemory); | ||
| 260 | |||
| 261 | const size_t remaining_size = alloc_size - needed_size; | ||
| 262 | |||
| 263 | // Determine random placements for each region. | ||
| 264 | size_t alias_rnd = 0, heap_rnd = 0, stack_rnd = 0, kmap_rnd = 0; | ||
| 265 | if (enable_aslr) { | ||
| 266 | alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * | ||
| 267 | RegionAlignment; | ||
| 268 | heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * | ||
| 269 | RegionAlignment; | ||
| 270 | stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * | ||
| 271 | RegionAlignment; | ||
| 272 | kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * | ||
| 273 | RegionAlignment; | ||
| 274 | } | ||
| 275 | |||
| 276 | // Setup heap and alias regions. | ||
| 277 | m_alias_region_start = alloc_start + alias_rnd; | ||
| 278 | m_alias_region_end = m_alias_region_start + alias_region_size; | ||
| 279 | m_heap_region_start = alloc_start + heap_rnd; | ||
| 280 | m_heap_region_end = m_heap_region_start + heap_region_size; | ||
| 281 | |||
| 282 | if (alias_rnd <= heap_rnd) { | ||
| 283 | m_heap_region_start += alias_region_size; | ||
| 284 | m_heap_region_end += alias_region_size; | ||
| 285 | } else { | ||
| 286 | m_alias_region_start += heap_region_size; | ||
| 287 | m_alias_region_end += heap_region_size; | ||
| 288 | } | ||
| 289 | |||
| 290 | // Setup stack region. | ||
| 291 | if (stack_region_size) { | ||
| 292 | m_stack_region_start = alloc_start + stack_rnd; | ||
| 293 | m_stack_region_end = m_stack_region_start + stack_region_size; | ||
| 294 | |||
| 295 | if (alias_rnd < stack_rnd) { | ||
| 296 | m_stack_region_start += alias_region_size; | ||
| 297 | m_stack_region_end += alias_region_size; | ||
| 298 | } else { | ||
| 299 | m_alias_region_start += stack_region_size; | ||
| 300 | m_alias_region_end += stack_region_size; | ||
| 301 | } | ||
| 302 | |||
| 303 | if (heap_rnd < stack_rnd) { | ||
| 304 | m_stack_region_start += heap_region_size; | ||
| 305 | m_stack_region_end += heap_region_size; | ||
| 306 | } else { | ||
| 307 | m_heap_region_start += stack_region_size; | ||
| 308 | m_heap_region_end += stack_region_size; | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 312 | // Setup kernel map region. | ||
| 313 | if (kernel_map_region_size) { | ||
| 314 | m_kernel_map_region_start = alloc_start + kmap_rnd; | ||
| 315 | m_kernel_map_region_end = m_kernel_map_region_start + kernel_map_region_size; | ||
| 316 | |||
| 317 | if (alias_rnd < kmap_rnd) { | ||
| 318 | m_kernel_map_region_start += alias_region_size; | ||
| 319 | m_kernel_map_region_end += alias_region_size; | ||
| 320 | } else { | ||
| 321 | m_alias_region_start += kernel_map_region_size; | ||
| 322 | m_alias_region_end += kernel_map_region_size; | ||
| 323 | } | ||
| 324 | |||
| 325 | if (heap_rnd < kmap_rnd) { | ||
| 326 | m_kernel_map_region_start += heap_region_size; | ||
| 327 | m_kernel_map_region_end += heap_region_size; | ||
| 328 | } else { | ||
| 329 | m_heap_region_start += kernel_map_region_size; | ||
| 330 | m_heap_region_end += kernel_map_region_size; | ||
| 331 | } | ||
| 332 | |||
| 333 | if (stack_region_size) { | ||
| 334 | if (stack_rnd < kmap_rnd) { | ||
| 335 | m_kernel_map_region_start += stack_region_size; | ||
| 336 | m_kernel_map_region_end += stack_region_size; | ||
| 337 | } else { | ||
| 338 | m_stack_region_start += kernel_map_region_size; | ||
| 339 | m_stack_region_end += kernel_map_region_size; | ||
| 340 | } | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | // Set heap and fill members. | ||
| 345 | m_current_heap_end = m_heap_region_start; | ||
| 346 | m_max_heap_size = 0; | ||
| 347 | m_mapped_physical_memory_size = 0; | ||
| 348 | m_mapped_unsafe_physical_memory = 0; | ||
| 349 | m_mapped_insecure_memory = 0; | ||
| 350 | m_mapped_ipc_server_memory = 0; | ||
| 351 | |||
| 352 | // const bool fill_memory = KTargetSystem::IsDebugMemoryFillEnabled(); | ||
| 353 | const bool fill_memory = false; | ||
| 354 | m_heap_fill_value = fill_memory ? MemoryFillValue_Heap : MemoryFillValue_Zero; | ||
| 355 | m_ipc_fill_value = fill_memory ? MemoryFillValue_Ipc : MemoryFillValue_Zero; | ||
| 356 | m_stack_fill_value = fill_memory ? MemoryFillValue_Stack : MemoryFillValue_Zero; | ||
| 357 | |||
| 358 | // Set allocation option. | ||
| 359 | m_allocate_option = | ||
| 360 | KMemoryManager::EncodeOption(pool, from_back ? KMemoryManager::Direction::FromBack | ||
| 361 | : KMemoryManager::Direction::FromFront); | ||
| 362 | |||
| 363 | // Ensure that we regions inside our address space. | ||
| 364 | auto IsInAddressSpace = [&](KProcessAddress addr) { | ||
| 365 | return m_address_space_start <= addr && addr <= m_address_space_end; | ||
| 366 | }; | ||
| 367 | ASSERT(IsInAddressSpace(m_alias_region_start)); | ||
| 368 | ASSERT(IsInAddressSpace(m_alias_region_end)); | ||
| 369 | ASSERT(IsInAddressSpace(m_heap_region_start)); | ||
| 370 | ASSERT(IsInAddressSpace(m_heap_region_end)); | ||
| 371 | ASSERT(IsInAddressSpace(m_stack_region_start)); | ||
| 372 | ASSERT(IsInAddressSpace(m_stack_region_end)); | ||
| 373 | ASSERT(IsInAddressSpace(m_kernel_map_region_start)); | ||
| 374 | ASSERT(IsInAddressSpace(m_kernel_map_region_end)); | ||
| 375 | |||
| 376 | // Ensure that we selected regions that don't overlap. | ||
| 377 | const KProcessAddress alias_start = m_alias_region_start; | ||
| 378 | const KProcessAddress alias_last = m_alias_region_end - 1; | ||
| 379 | const KProcessAddress heap_start = m_heap_region_start; | ||
| 380 | const KProcessAddress heap_last = m_heap_region_end - 1; | ||
| 381 | const KProcessAddress stack_start = m_stack_region_start; | ||
| 382 | const KProcessAddress stack_last = m_stack_region_end - 1; | ||
| 383 | const KProcessAddress kmap_start = m_kernel_map_region_start; | ||
| 384 | const KProcessAddress kmap_last = m_kernel_map_region_end - 1; | ||
| 385 | ASSERT(alias_last < heap_start || heap_last < alias_start); | ||
| 386 | ASSERT(alias_last < stack_start || stack_last < alias_start); | ||
| 387 | ASSERT(alias_last < kmap_start || kmap_last < alias_start); | ||
| 388 | ASSERT(heap_last < stack_start || stack_last < heap_start); | ||
| 389 | ASSERT(heap_last < kmap_start || kmap_last < heap_start); | ||
| 390 | |||
| 391 | // Initialize our implementation. | ||
| 392 | m_impl = std::make_unique<Common::PageTable>(); | ||
| 393 | m_impl->Resize(m_address_space_width, PageBits); | ||
| 394 | |||
| 395 | // Set the tracking memory. | ||
| 396 | m_memory = std::addressof(memory); | ||
| 397 | |||
| 398 | // Initialize our memory block manager. | ||
| 399 | R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end, | ||
| 400 | m_memory_block_slab_manager)); | ||
| 401 | } | ||
| 402 | |||
| 403 | void KPageTableBase::Finalize() { | ||
| 404 | auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { | ||
| 405 | if (Settings::IsFastmemEnabled()) { | ||
| 406 | m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size); | ||
| 407 | } | ||
| 408 | }; | ||
| 409 | |||
| 410 | // Finalize memory blocks. | ||
| 411 | m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback)); | ||
| 412 | |||
| 413 | // Free any unsafe mapped memory. | ||
| 414 | if (m_mapped_unsafe_physical_memory) { | ||
| 415 | UNIMPLEMENTED(); | ||
| 416 | } | ||
| 417 | |||
| 418 | // Release any insecure mapped memory. | ||
| 419 | if (m_mapped_insecure_memory) { | ||
| 420 | if (auto* const insecure_resource_limit = | ||
| 421 | KSystemControl::GetInsecureMemoryResourceLimit(m_kernel); | ||
| 422 | insecure_resource_limit != nullptr) { | ||
| 423 | insecure_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, | ||
| 424 | m_mapped_insecure_memory); | ||
| 425 | } | ||
| 426 | } | ||
| 427 | |||
| 428 | // Release any ipc server memory. | ||
| 429 | if (m_mapped_ipc_server_memory) { | ||
| 430 | m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, | ||
| 431 | m_mapped_ipc_server_memory); | ||
| 432 | } | ||
| 433 | |||
| 434 | // Close the backing page table, as the destructor is not called for guest objects. | ||
| 435 | m_impl.reset(); | ||
| 436 | } | ||
| 437 | |||
| 438 | KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const { | ||
| 439 | switch (state) { | ||
| 440 | case Svc::MemoryState::Free: | ||
| 441 | case Svc::MemoryState::Kernel: | ||
| 442 | return m_address_space_start; | ||
| 443 | case Svc::MemoryState::Normal: | ||
| 444 | return m_heap_region_start; | ||
| 445 | case Svc::MemoryState::Ipc: | ||
| 446 | case Svc::MemoryState::NonSecureIpc: | ||
| 447 | case Svc::MemoryState::NonDeviceIpc: | ||
| 448 | return m_alias_region_start; | ||
| 449 | case Svc::MemoryState::Stack: | ||
| 450 | return m_stack_region_start; | ||
| 451 | case Svc::MemoryState::Static: | ||
| 452 | case Svc::MemoryState::ThreadLocal: | ||
| 453 | return m_kernel_map_region_start; | ||
| 454 | case Svc::MemoryState::Io: | ||
| 455 | case Svc::MemoryState::Shared: | ||
| 456 | case Svc::MemoryState::AliasCode: | ||
| 457 | case Svc::MemoryState::AliasCodeData: | ||
| 458 | case Svc::MemoryState::Transfered: | ||
| 459 | case Svc::MemoryState::SharedTransfered: | ||
| 460 | case Svc::MemoryState::SharedCode: | ||
| 461 | case Svc::MemoryState::GeneratedCode: | ||
| 462 | case Svc::MemoryState::CodeOut: | ||
| 463 | case Svc::MemoryState::Coverage: | ||
| 464 | case Svc::MemoryState::Insecure: | ||
| 465 | return m_alias_code_region_start; | ||
| 466 | case Svc::MemoryState::Code: | ||
| 467 | case Svc::MemoryState::CodeData: | ||
| 468 | return m_code_region_start; | ||
| 469 | default: | ||
| 470 | UNREACHABLE(); | ||
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 474 | size_t KPageTableBase::GetRegionSize(Svc::MemoryState state) const { | ||
| 475 | switch (state) { | ||
| 476 | case Svc::MemoryState::Free: | ||
| 477 | case Svc::MemoryState::Kernel: | ||
| 478 | return m_address_space_end - m_address_space_start; | ||
| 479 | case Svc::MemoryState::Normal: | ||
| 480 | return m_heap_region_end - m_heap_region_start; | ||
| 481 | case Svc::MemoryState::Ipc: | ||
| 482 | case Svc::MemoryState::NonSecureIpc: | ||
| 483 | case Svc::MemoryState::NonDeviceIpc: | ||
| 484 | return m_alias_region_end - m_alias_region_start; | ||
| 485 | case Svc::MemoryState::Stack: | ||
| 486 | return m_stack_region_end - m_stack_region_start; | ||
| 487 | case Svc::MemoryState::Static: | ||
| 488 | case Svc::MemoryState::ThreadLocal: | ||
| 489 | return m_kernel_map_region_end - m_kernel_map_region_start; | ||
| 490 | case Svc::MemoryState::Io: | ||
| 491 | case Svc::MemoryState::Shared: | ||
| 492 | case Svc::MemoryState::AliasCode: | ||
| 493 | case Svc::MemoryState::AliasCodeData: | ||
| 494 | case Svc::MemoryState::Transfered: | ||
| 495 | case Svc::MemoryState::SharedTransfered: | ||
| 496 | case Svc::MemoryState::SharedCode: | ||
| 497 | case Svc::MemoryState::GeneratedCode: | ||
| 498 | case Svc::MemoryState::CodeOut: | ||
| 499 | case Svc::MemoryState::Coverage: | ||
| 500 | case Svc::MemoryState::Insecure: | ||
| 501 | return m_alias_code_region_end - m_alias_code_region_start; | ||
| 502 | case Svc::MemoryState::Code: | ||
| 503 | case Svc::MemoryState::CodeData: | ||
| 504 | return m_code_region_end - m_code_region_start; | ||
| 505 | default: | ||
| 506 | UNREACHABLE(); | ||
| 507 | } | ||
| 508 | } | ||
| 509 | |||
| 510 | bool KPageTableBase::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const { | ||
| 511 | const KProcessAddress end = addr + size; | ||
| 512 | const KProcessAddress last = end - 1; | ||
| 513 | |||
| 514 | const KProcessAddress region_start = this->GetRegionAddress(state); | ||
| 515 | const size_t region_size = this->GetRegionSize(state); | ||
| 516 | |||
| 517 | const bool is_in_region = | ||
| 518 | region_start <= addr && addr < end && last <= region_start + region_size - 1; | ||
| 519 | const bool is_in_heap = !(end <= m_heap_region_start || m_heap_region_end <= addr || | ||
| 520 | m_heap_region_start == m_heap_region_end); | ||
| 521 | const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr || | ||
| 522 | m_alias_region_start == m_alias_region_end); | ||
| 523 | switch (state) { | ||
| 524 | case Svc::MemoryState::Free: | ||
| 525 | case Svc::MemoryState::Kernel: | ||
| 526 | return is_in_region; | ||
| 527 | case Svc::MemoryState::Io: | ||
| 528 | case Svc::MemoryState::Static: | ||
| 529 | case Svc::MemoryState::Code: | ||
| 530 | case Svc::MemoryState::CodeData: | ||
| 531 | case Svc::MemoryState::Shared: | ||
| 532 | case Svc::MemoryState::AliasCode: | ||
| 533 | case Svc::MemoryState::AliasCodeData: | ||
| 534 | case Svc::MemoryState::Stack: | ||
| 535 | case Svc::MemoryState::ThreadLocal: | ||
| 536 | case Svc::MemoryState::Transfered: | ||
| 537 | case Svc::MemoryState::SharedTransfered: | ||
| 538 | case Svc::MemoryState::SharedCode: | ||
| 539 | case Svc::MemoryState::GeneratedCode: | ||
| 540 | case Svc::MemoryState::CodeOut: | ||
| 541 | case Svc::MemoryState::Coverage: | ||
| 542 | case Svc::MemoryState::Insecure: | ||
| 543 | return is_in_region && !is_in_heap && !is_in_alias; | ||
| 544 | case Svc::MemoryState::Normal: | ||
| 545 | ASSERT(is_in_heap); | ||
| 546 | return is_in_region && !is_in_alias; | ||
| 547 | case Svc::MemoryState::Ipc: | ||
| 548 | case Svc::MemoryState::NonSecureIpc: | ||
| 549 | case Svc::MemoryState::NonDeviceIpc: | ||
| 550 | ASSERT(is_in_alias); | ||
| 551 | return is_in_region && !is_in_heap; | ||
| 552 | default: | ||
| 553 | return false; | ||
| 554 | } | ||
| 555 | } | ||
| 556 | |||
| 557 | Result KPageTableBase::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, | ||
| 558 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 559 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 560 | KMemoryAttribute attr) const { | ||
| 561 | // Validate the states match expectation. | ||
| 562 | R_UNLESS((info.m_state & state_mask) == state, ResultInvalidCurrentMemory); | ||
| 563 | R_UNLESS((info.m_permission & perm_mask) == perm, ResultInvalidCurrentMemory); | ||
| 564 | R_UNLESS((info.m_attribute & attr_mask) == attr, ResultInvalidCurrentMemory); | ||
| 565 | |||
| 566 | R_SUCCEED(); | ||
| 567 | } | ||
| 568 | |||
| 569 | Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, | ||
| 570 | size_t size, KMemoryState state_mask, | ||
| 571 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 572 | KMemoryPermission perm, | ||
| 573 | KMemoryAttribute attr_mask, | ||
| 574 | KMemoryAttribute attr) const { | ||
| 575 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 576 | |||
| 577 | // Get information about the first block. | ||
| 578 | const KProcessAddress last_addr = addr + size - 1; | ||
| 579 | KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr); | ||
| 580 | KMemoryInfo info = it->GetMemoryInfo(); | ||
| 581 | |||
| 582 | // If the start address isn't aligned, we need a block. | ||
| 583 | const size_t blocks_for_start_align = | ||
| 584 | (Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0; | ||
| 585 | |||
| 586 | while (true) { | ||
| 587 | // Validate against the provided masks. | ||
| 588 | R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); | ||
| 589 | |||
| 590 | // Break once we're done. | ||
| 591 | if (last_addr <= info.GetLastAddress()) { | ||
| 592 | break; | ||
| 593 | } | ||
| 594 | |||
| 595 | // Advance our iterator. | ||
| 596 | it++; | ||
| 597 | ASSERT(it != m_memory_block_manager.cend()); | ||
| 598 | info = it->GetMemoryInfo(); | ||
| 599 | } | ||
| 600 | |||
| 601 | // If the end address isn't aligned, we need a block. | ||
| 602 | const size_t blocks_for_end_align = | ||
| 603 | (Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0; | ||
| 604 | |||
| 605 | if (out_blocks_needed != nullptr) { | ||
| 606 | *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; | ||
| 607 | } | ||
| 608 | |||
| 609 | R_SUCCEED(); | ||
| 610 | } | ||
| 611 | |||
| 612 | Result KPageTableBase::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, | ||
| 613 | KMemoryAttribute* out_attr, size_t* out_blocks_needed, | ||
| 614 | KMemoryBlockManager::const_iterator it, | ||
| 615 | KProcessAddress last_addr, KMemoryState state_mask, | ||
| 616 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 617 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 618 | KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { | ||
| 619 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 620 | |||
| 621 | // Get information about the first block. | ||
| 622 | KMemoryInfo info = it->GetMemoryInfo(); | ||
| 623 | |||
| 624 | // Validate all blocks in the range have correct state. | ||
| 625 | const KMemoryState first_state = info.m_state; | ||
| 626 | const KMemoryPermission first_perm = info.m_permission; | ||
| 627 | const KMemoryAttribute first_attr = info.m_attribute; | ||
| 628 | while (true) { | ||
| 629 | // Validate the current block. | ||
| 630 | R_UNLESS(info.m_state == first_state, ResultInvalidCurrentMemory); | ||
| 631 | R_UNLESS(info.m_permission == first_perm, ResultInvalidCurrentMemory); | ||
| 632 | R_UNLESS((info.m_attribute | ignore_attr) == (first_attr | ignore_attr), | ||
| 633 | ResultInvalidCurrentMemory); | ||
| 634 | |||
| 635 | // Validate against the provided masks. | ||
| 636 | R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); | ||
| 637 | |||
| 638 | // Break once we're done. | ||
| 639 | if (last_addr <= info.GetLastAddress()) { | ||
| 640 | break; | ||
| 641 | } | ||
| 642 | |||
| 643 | // Advance our iterator. | ||
| 644 | it++; | ||
| 645 | ASSERT(it != m_memory_block_manager.cend()); | ||
| 646 | info = it->GetMemoryInfo(); | ||
| 647 | } | ||
| 648 | |||
| 649 | // Write output state. | ||
| 650 | if (out_state != nullptr) { | ||
| 651 | *out_state = first_state; | ||
| 652 | } | ||
| 653 | if (out_perm != nullptr) { | ||
| 654 | *out_perm = first_perm; | ||
| 655 | } | ||
| 656 | if (out_attr != nullptr) { | ||
| 657 | *out_attr = first_attr & ~ignore_attr; | ||
| 658 | } | ||
| 659 | |||
| 660 | // If the end address isn't aligned, we need a block. | ||
| 661 | if (out_blocks_needed != nullptr) { | ||
| 662 | const size_t blocks_for_end_align = | ||
| 663 | (Common::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress()) | ||
| 664 | ? 1 | ||
| 665 | : 0; | ||
| 666 | *out_blocks_needed = blocks_for_end_align; | ||
| 667 | } | ||
| 668 | |||
| 669 | R_SUCCEED(); | ||
| 670 | } | ||
| 671 | |||
| 672 | Result KPageTableBase::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, | ||
| 673 | KMemoryAttribute* out_attr, size_t* out_blocks_needed, | ||
| 674 | KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 675 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 676 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 677 | KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { | ||
| 678 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 679 | |||
| 680 | // Check memory state. | ||
| 681 | const KProcessAddress last_addr = addr + size - 1; | ||
| 682 | KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr); | ||
| 683 | R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr, | ||
| 684 | state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr)); | ||
| 685 | |||
| 686 | // If the start address isn't aligned, we need a block. | ||
| 687 | if (out_blocks_needed != nullptr && | ||
| 688 | Common::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) { | ||
| 689 | ++(*out_blocks_needed); | ||
| 690 | } | ||
| 691 | |||
| 692 | R_SUCCEED(); | ||
| 693 | } | ||
| 694 | |||
| 695 | Result KPageTableBase::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_paddr, | ||
| 696 | KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 697 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 698 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 699 | KMemoryAttribute attr, KMemoryPermission new_perm, | ||
| 700 | KMemoryAttribute lock_attr) { | ||
| 701 | // Validate basic preconditions. | ||
| 702 | ASSERT(False(lock_attr & attr)); | ||
| 703 | ASSERT(False(lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared))); | ||
| 704 | |||
| 705 | // Validate the lock request. | ||
| 706 | const size_t num_pages = size / PageSize; | ||
| 707 | R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); | ||
| 708 | |||
| 709 | // Lock the table. | ||
| 710 | KScopedLightLock lk(m_general_lock); | ||
| 711 | |||
| 712 | // Check that the output page group is empty, if it exists. | ||
| 713 | if (out_pg) { | ||
| 714 | ASSERT(out_pg->GetNumPages() == 0); | ||
| 715 | } | ||
| 716 | |||
| 717 | // Check the state. | ||
| 718 | KMemoryState old_state; | ||
| 719 | KMemoryPermission old_perm; | ||
| 720 | KMemoryAttribute old_attr; | ||
| 721 | size_t num_allocator_blocks; | ||
| 722 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), | ||
| 723 | std::addressof(old_attr), std::addressof(num_allocator_blocks), | ||
| 724 | addr, size, state_mask | KMemoryState::FlagReferenceCounted, | ||
| 725 | state | KMemoryState::FlagReferenceCounted, perm_mask, perm, | ||
| 726 | attr_mask, attr)); | ||
| 727 | |||
| 728 | // Get the physical address, if we're supposed to. | ||
| 729 | if (out_paddr != nullptr) { | ||
| 730 | ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr)); | ||
| 731 | } | ||
| 732 | |||
| 733 | // Make the page group, if we're supposed to. | ||
| 734 | if (out_pg != nullptr) { | ||
| 735 | R_TRY(this->MakePageGroup(*out_pg, addr, num_pages)); | ||
| 736 | } | ||
| 737 | |||
| 738 | // Create an update allocator. | ||
| 739 | Result allocator_result; | ||
| 740 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 741 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 742 | R_TRY(allocator_result); | ||
| 743 | |||
| 744 | // Decide on new perm and attr. | ||
| 745 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 746 | KMemoryAttribute new_attr = old_attr | static_cast<KMemoryAttribute>(lock_attr); | ||
| 747 | |||
| 748 | // Update permission, if we need to. | ||
| 749 | if (new_perm != old_perm) { | ||
| 750 | // We're going to perform an update, so create a helper. | ||
| 751 | KScopedPageTableUpdater updater(this); | ||
| 752 | |||
| 753 | const KPageProperties properties = {new_perm, false, | ||
| 754 | True(old_attr & KMemoryAttribute::Uncached), | ||
| 755 | DisableMergeAttribute::DisableHeadBodyTail}; | ||
| 756 | R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties, | ||
| 757 | OperationType::ChangePermissions, false)); | ||
| 758 | } | ||
| 759 | |||
| 760 | // Apply the memory block updates. | ||
| 761 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, | ||
| 762 | new_attr, KMemoryBlockDisableMergeAttribute::Locked, | ||
| 763 | KMemoryBlockDisableMergeAttribute::None); | ||
| 764 | |||
| 765 | // If we have an output group, open. | ||
| 766 | if (out_pg) { | ||
| 767 | out_pg->Open(); | ||
| 768 | } | ||
| 769 | |||
| 770 | R_SUCCEED(); | ||
| 771 | } | ||
| 772 | |||
| 773 | Result KPageTableBase::UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 774 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 775 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 776 | KMemoryAttribute attr, KMemoryPermission new_perm, | ||
| 777 | KMemoryAttribute lock_attr, const KPageGroup* pg) { | ||
| 778 | // Validate basic preconditions. | ||
| 779 | ASSERT((attr_mask & lock_attr) == lock_attr); | ||
| 780 | ASSERT((attr & lock_attr) == lock_attr); | ||
| 781 | |||
| 782 | // Validate the unlock request. | ||
| 783 | const size_t num_pages = size / PageSize; | ||
| 784 | R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); | ||
| 785 | |||
| 786 | // Lock the table. | ||
| 787 | KScopedLightLock lk(m_general_lock); | ||
| 788 | |||
| 789 | // Check the state. | ||
| 790 | KMemoryState old_state; | ||
| 791 | KMemoryPermission old_perm; | ||
| 792 | KMemoryAttribute old_attr; | ||
| 793 | size_t num_allocator_blocks; | ||
| 794 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), | ||
| 795 | std::addressof(old_attr), std::addressof(num_allocator_blocks), | ||
| 796 | addr, size, state_mask | KMemoryState::FlagReferenceCounted, | ||
| 797 | state | KMemoryState::FlagReferenceCounted, perm_mask, perm, | ||
| 798 | attr_mask, attr)); | ||
| 799 | |||
| 800 | // Check the page group. | ||
| 801 | if (pg != nullptr) { | ||
| 802 | R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion); | ||
| 803 | } | ||
| 804 | |||
| 805 | // Decide on new perm and attr. | ||
| 806 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 807 | KMemoryAttribute new_attr = old_attr & ~static_cast<KMemoryAttribute>(lock_attr); | ||
| 808 | |||
| 809 | // Create an update allocator. | ||
| 810 | Result allocator_result; | ||
| 811 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 812 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 813 | R_TRY(allocator_result); | ||
| 814 | |||
| 815 | // Update permission, if we need to. | ||
| 816 | if (new_perm != old_perm) { | ||
| 817 | // We're going to perform an update, so create a helper. | ||
| 818 | KScopedPageTableUpdater updater(this); | ||
| 819 | |||
| 820 | const KPageProperties properties = {new_perm, false, | ||
| 821 | True(old_attr & KMemoryAttribute::Uncached), | ||
| 822 | DisableMergeAttribute::EnableAndMergeHeadBodyTail}; | ||
| 823 | R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties, | ||
| 824 | OperationType::ChangePermissions, false)); | ||
| 825 | } | ||
| 826 | |||
| 827 | // Apply the memory block updates. | ||
| 828 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, | ||
| 829 | new_attr, KMemoryBlockDisableMergeAttribute::None, | ||
| 830 | KMemoryBlockDisableMergeAttribute::Locked); | ||
| 831 | |||
| 832 | R_SUCCEED(); | ||
| 833 | } | ||
| 834 | |||
| 835 | Result KPageTableBase::QueryInfoImpl(KMemoryInfo* out_info, Svc::PageInfo* out_page, | ||
| 836 | KProcessAddress address) const { | ||
| 837 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 838 | ASSERT(out_info != nullptr); | ||
| 839 | ASSERT(out_page != nullptr); | ||
| 840 | |||
| 841 | const KMemoryBlock* block = m_memory_block_manager.FindBlock(address); | ||
| 842 | R_UNLESS(block != nullptr, ResultInvalidCurrentMemory); | ||
| 843 | |||
| 844 | *out_info = block->GetMemoryInfo(); | ||
| 845 | out_page->flags = 0; | ||
| 846 | R_SUCCEED(); | ||
| 847 | } | ||
| 848 | |||
| 849 | Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress address, size_t size, | ||
| 850 | Svc::MemoryState state) const { | ||
| 851 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 852 | ASSERT(out != nullptr); | ||
| 853 | |||
| 854 | const KProcessAddress region_start = this->GetRegionAddress(state); | ||
| 855 | const size_t region_size = this->GetRegionSize(state); | ||
| 856 | |||
| 857 | // Check that the address/size are potentially valid. | ||
| 858 | R_UNLESS((address < address + size), ResultNotFound); | ||
| 859 | |||
| 860 | // Lock the table. | ||
| 861 | KScopedLightLock lk(m_general_lock); | ||
| 862 | |||
| 863 | auto& impl = this->GetImpl(); | ||
| 864 | |||
| 865 | // Begin traversal. | ||
| 866 | TraversalContext context; | ||
| 867 | TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0}; | ||
| 868 | bool cur_valid = false; | ||
| 869 | TraversalEntry next_entry; | ||
| 870 | bool next_valid; | ||
| 871 | size_t tot_size = 0; | ||
| 872 | |||
| 873 | next_valid = | ||
| 874 | impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start); | ||
| 875 | next_entry.block_size = | ||
| 876 | (next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1))); | ||
| 877 | |||
| 878 | // Iterate, looking for entry. | ||
| 879 | while (true) { | ||
| 880 | if ((!next_valid && !cur_valid) || | ||
| 881 | (next_valid && cur_valid && | ||
| 882 | next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) { | ||
| 883 | cur_entry.block_size += next_entry.block_size; | ||
| 884 | } else { | ||
| 885 | if (cur_valid && cur_entry.phys_addr <= address && | ||
| 886 | address + size <= cur_entry.phys_addr + cur_entry.block_size) { | ||
| 887 | // Check if this region is valid. | ||
| 888 | const KProcessAddress mapped_address = | ||
| 889 | (region_start + tot_size) + GetInteger(address - cur_entry.phys_addr); | ||
| 890 | if (R_SUCCEEDED(this->CheckMemoryState( | ||
| 891 | mapped_address, size, KMemoryState::Mask, static_cast<KMemoryState>(state), | ||
| 892 | KMemoryPermission::UserRead, KMemoryPermission::UserRead, | ||
| 893 | KMemoryAttribute::None, KMemoryAttribute::None))) { | ||
| 894 | // It is! | ||
| 895 | *out = mapped_address; | ||
| 896 | R_SUCCEED(); | ||
| 897 | } | ||
| 898 | } | ||
| 899 | |||
| 900 | // Update tracking variables. | ||
| 901 | tot_size += cur_entry.block_size; | ||
| 902 | cur_entry = next_entry; | ||
| 903 | cur_valid = next_valid; | ||
| 904 | } | ||
| 905 | |||
| 906 | if (cur_entry.block_size + tot_size >= region_size) { | ||
| 907 | break; | ||
| 908 | } | ||
| 909 | |||
| 910 | next_valid = impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 911 | } | ||
| 912 | |||
| 913 | // Check the last entry. | ||
| 914 | R_UNLESS(cur_valid, ResultNotFound); | ||
| 915 | R_UNLESS(cur_entry.phys_addr <= address, ResultNotFound); | ||
| 916 | R_UNLESS(address + size <= cur_entry.phys_addr + cur_entry.block_size, ResultNotFound); | ||
| 917 | |||
| 918 | // Check if the last region is valid. | ||
| 919 | const KProcessAddress mapped_address = | ||
| 920 | (region_start + tot_size) + GetInteger(address - cur_entry.phys_addr); | ||
| 921 | R_TRY_CATCH(this->CheckMemoryState(mapped_address, size, KMemoryState::All, | ||
| 922 | static_cast<KMemoryState>(state), | ||
| 923 | KMemoryPermission::UserRead, KMemoryPermission::UserRead, | ||
| 924 | KMemoryAttribute::None, KMemoryAttribute::None)) { | ||
| 925 | R_CONVERT_ALL(ResultNotFound); | ||
| 926 | } | ||
| 927 | R_END_TRY_CATCH; | ||
| 928 | |||
| 929 | // We found the region. | ||
| 930 | *out = mapped_address; | ||
| 931 | R_SUCCEED(); | ||
| 932 | } | ||
| 933 | |||
| 934 | Result KPageTableBase::MapMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 935 | size_t size) { | ||
| 936 | // Lock the table. | ||
| 937 | KScopedLightLock lk(m_general_lock); | ||
| 938 | |||
| 939 | // Validate that the source address's state is valid. | ||
| 940 | KMemoryState src_state; | ||
| 941 | size_t num_src_allocator_blocks; | ||
| 942 | R_TRY(this->CheckMemoryState(std::addressof(src_state), nullptr, nullptr, | ||
| 943 | std::addressof(num_src_allocator_blocks), src_address, size, | ||
| 944 | KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias, | ||
| 945 | KMemoryPermission::All, KMemoryPermission::UserReadWrite, | ||
| 946 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 947 | |||
| 948 | // Validate that the dst address's state is valid. | ||
| 949 | size_t num_dst_allocator_blocks; | ||
| 950 | R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size, | ||
| 951 | KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, | ||
| 952 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 953 | KMemoryAttribute::None)); | ||
| 954 | |||
| 955 | // Create an update allocator for the source. | ||
| 956 | Result src_allocator_result; | ||
| 957 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), | ||
| 958 | m_memory_block_slab_manager, | ||
| 959 | num_src_allocator_blocks); | ||
| 960 | R_TRY(src_allocator_result); | ||
| 961 | |||
| 962 | // Create an update allocator for the destination. | ||
| 963 | Result dst_allocator_result; | ||
| 964 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), | ||
| 965 | m_memory_block_slab_manager, | ||
| 966 | num_dst_allocator_blocks); | ||
| 967 | R_TRY(dst_allocator_result); | ||
| 968 | |||
| 969 | // Map the memory. | ||
| 970 | { | ||
| 971 | // Determine the number of pages being operated on. | ||
| 972 | const size_t num_pages = size / PageSize; | ||
| 973 | |||
| 974 | // Create page groups for the memory being unmapped. | ||
| 975 | KPageGroup pg(m_kernel, m_block_info_manager); | ||
| 976 | |||
| 977 | // Create the page group representing the source. | ||
| 978 | R_TRY(this->MakePageGroup(pg, src_address, num_pages)); | ||
| 979 | |||
| 980 | // We're going to perform an update, so create a helper. | ||
| 981 | KScopedPageTableUpdater updater(this); | ||
| 982 | |||
| 983 | // Reprotect the source as kernel-read/not mapped. | ||
| 984 | const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>( | ||
| 985 | KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); | ||
| 986 | const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked; | ||
| 987 | const KPageProperties src_properties = {new_src_perm, false, false, | ||
| 988 | DisableMergeAttribute::DisableHeadBodyTail}; | ||
| 989 | R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties, | ||
| 990 | OperationType::ChangePermissions, false)); | ||
| 991 | |||
| 992 | // Ensure that we unprotect the source pages on failure. | ||
| 993 | ON_RESULT_FAILURE { | ||
| 994 | const KPageProperties unprotect_properties = { | ||
| 995 | KMemoryPermission::UserReadWrite, false, false, | ||
| 996 | DisableMergeAttribute::EnableHeadBodyTail}; | ||
| 997 | R_ASSERT(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, | ||
| 998 | unprotect_properties, OperationType::ChangePermissions, true)); | ||
| 999 | }; | ||
| 1000 | |||
| 1001 | // Map the alias pages. | ||
| 1002 | const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false, | ||
| 1003 | DisableMergeAttribute::DisableHead}; | ||
| 1004 | R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties, | ||
| 1005 | false)); | ||
| 1006 | |||
| 1007 | // Apply the memory block updates. | ||
| 1008 | m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, | ||
| 1009 | src_state, new_src_perm, new_src_attr, | ||
| 1010 | KMemoryBlockDisableMergeAttribute::Locked, | ||
| 1011 | KMemoryBlockDisableMergeAttribute::None); | ||
| 1012 | m_memory_block_manager.Update( | ||
| 1013 | std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack, | ||
| 1014 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 1015 | KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None); | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | R_SUCCEED(); | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | Result KPageTableBase::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 1022 | size_t size) { | ||
| 1023 | // Lock the table. | ||
| 1024 | KScopedLightLock lk(m_general_lock); | ||
| 1025 | |||
| 1026 | // Validate that the source address's state is valid. | ||
| 1027 | KMemoryState src_state; | ||
| 1028 | size_t num_src_allocator_blocks; | ||
| 1029 | R_TRY(this->CheckMemoryState( | ||
| 1030 | std::addressof(src_state), nullptr, nullptr, std::addressof(num_src_allocator_blocks), | ||
| 1031 | src_address, size, KMemoryState::FlagCanAlias, KMemoryState::FlagCanAlias, | ||
| 1032 | KMemoryPermission::All, KMemoryPermission::NotMapped | KMemoryPermission::KernelRead, | ||
| 1033 | KMemoryAttribute::All, KMemoryAttribute::Locked)); | ||
| 1034 | |||
| 1035 | // Validate that the dst address's state is valid. | ||
| 1036 | KMemoryPermission dst_perm; | ||
| 1037 | size_t num_dst_allocator_blocks; | ||
| 1038 | R_TRY(this->CheckMemoryState( | ||
| 1039 | nullptr, std::addressof(dst_perm), nullptr, std::addressof(num_dst_allocator_blocks), | ||
| 1040 | dst_address, size, KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None, | ||
| 1041 | KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 1042 | |||
| 1043 | // Create an update allocator for the source. | ||
| 1044 | Result src_allocator_result; | ||
| 1045 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), | ||
| 1046 | m_memory_block_slab_manager, | ||
| 1047 | num_src_allocator_blocks); | ||
| 1048 | R_TRY(src_allocator_result); | ||
| 1049 | |||
| 1050 | // Create an update allocator for the destination. | ||
| 1051 | Result dst_allocator_result; | ||
| 1052 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), | ||
| 1053 | m_memory_block_slab_manager, | ||
| 1054 | num_dst_allocator_blocks); | ||
| 1055 | R_TRY(dst_allocator_result); | ||
| 1056 | |||
| 1057 | // Unmap the memory. | ||
| 1058 | { | ||
| 1059 | // Determine the number of pages being operated on. | ||
| 1060 | const size_t num_pages = size / PageSize; | ||
| 1061 | |||
| 1062 | // Create page groups for the memory being unmapped. | ||
| 1063 | KPageGroup pg(m_kernel, m_block_info_manager); | ||
| 1064 | |||
| 1065 | // Create the page group representing the destination. | ||
| 1066 | R_TRY(this->MakePageGroup(pg, dst_address, num_pages)); | ||
| 1067 | |||
| 1068 | // Ensure the page group is the valid for the source. | ||
| 1069 | R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion); | ||
| 1070 | |||
| 1071 | // We're going to perform an update, so create a helper. | ||
| 1072 | KScopedPageTableUpdater updater(this); | ||
| 1073 | |||
| 1074 | // Unmap the aliased copy of the pages. | ||
| 1075 | const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false, | ||
| 1076 | DisableMergeAttribute::None}; | ||
| 1077 | R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false, | ||
| 1078 | dst_unmap_properties, OperationType::Unmap, false)); | ||
| 1079 | |||
| 1080 | // Ensure that we re-map the aliased pages on failure. | ||
| 1081 | ON_RESULT_FAILURE { | ||
| 1082 | this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg); | ||
| 1083 | }; | ||
| 1084 | |||
| 1085 | // Try to set the permissions for the source pages back to what they should be. | ||
| 1086 | const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false, | ||
| 1087 | DisableMergeAttribute::EnableAndMergeHeadBodyTail}; | ||
| 1088 | R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties, | ||
| 1089 | OperationType::ChangePermissions, false)); | ||
| 1090 | |||
| 1091 | // Apply the memory block updates. | ||
| 1092 | m_memory_block_manager.Update( | ||
| 1093 | std::addressof(src_allocator), src_address, num_pages, src_state, | ||
| 1094 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 1095 | KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked); | ||
| 1096 | m_memory_block_manager.Update( | ||
| 1097 | std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None, | ||
| 1098 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 1099 | KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | R_SUCCEED(); | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | Result KPageTableBase::MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 1106 | size_t size) { | ||
| 1107 | // Validate the mapping request. | ||
| 1108 | R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), | ||
| 1109 | ResultInvalidMemoryRegion); | ||
| 1110 | |||
| 1111 | // Lock the table. | ||
| 1112 | KScopedLightLock lk(m_general_lock); | ||
| 1113 | |||
| 1114 | // Verify that the source memory is normal heap. | ||
| 1115 | KMemoryState src_state; | ||
| 1116 | KMemoryPermission src_perm; | ||
| 1117 | size_t num_src_allocator_blocks; | ||
| 1118 | R_TRY(this->CheckMemoryState(std::addressof(src_state), std::addressof(src_perm), nullptr, | ||
| 1119 | std::addressof(num_src_allocator_blocks), src_address, size, | ||
| 1120 | KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, | ||
| 1121 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, | ||
| 1122 | KMemoryAttribute::None)); | ||
| 1123 | |||
| 1124 | // Verify that the destination memory is unmapped. | ||
| 1125 | size_t num_dst_allocator_blocks; | ||
| 1126 | R_TRY(this->CheckMemoryState(std::addressof(num_dst_allocator_blocks), dst_address, size, | ||
| 1127 | KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, | ||
| 1128 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 1129 | KMemoryAttribute::None)); | ||
| 1130 | |||
| 1131 | // Create an update allocator for the source. | ||
| 1132 | Result src_allocator_result; | ||
| 1133 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), | ||
| 1134 | m_memory_block_slab_manager, | ||
| 1135 | num_src_allocator_blocks); | ||
| 1136 | R_TRY(src_allocator_result); | ||
| 1137 | |||
| 1138 | // Create an update allocator for the destination. | ||
| 1139 | Result dst_allocator_result; | ||
| 1140 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), | ||
| 1141 | m_memory_block_slab_manager, | ||
| 1142 | num_dst_allocator_blocks); | ||
| 1143 | R_TRY(dst_allocator_result); | ||
| 1144 | |||
| 1145 | // Map the code memory. | ||
| 1146 | { | ||
| 1147 | // Determine the number of pages being operated on. | ||
| 1148 | const size_t num_pages = size / PageSize; | ||
| 1149 | |||
| 1150 | // Create page groups for the memory being unmapped. | ||
| 1151 | KPageGroup pg(m_kernel, m_block_info_manager); | ||
| 1152 | |||
| 1153 | // Create the page group representing the source. | ||
| 1154 | R_TRY(this->MakePageGroup(pg, src_address, num_pages)); | ||
| 1155 | |||
| 1156 | // We're going to perform an update, so create a helper. | ||
| 1157 | KScopedPageTableUpdater updater(this); | ||
| 1158 | |||
| 1159 | // Reprotect the source as kernel-read/not mapped. | ||
| 1160 | const KMemoryPermission new_perm = static_cast<KMemoryPermission>( | ||
| 1161 | KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); | ||
| 1162 | const KPageProperties src_properties = {new_perm, false, false, | ||
| 1163 | DisableMergeAttribute::DisableHeadBodyTail}; | ||
| 1164 | R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties, | ||
| 1165 | OperationType::ChangePermissions, false)); | ||
| 1166 | |||
| 1167 | // Ensure that we unprotect the source pages on failure. | ||
| 1168 | ON_RESULT_FAILURE { | ||
| 1169 | const KPageProperties unprotect_properties = { | ||
| 1170 | src_perm, false, false, DisableMergeAttribute::EnableHeadBodyTail}; | ||
| 1171 | R_ASSERT(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, | ||
| 1172 | unprotect_properties, OperationType::ChangePermissions, true)); | ||
| 1173 | }; | ||
| 1174 | |||
| 1175 | // Map the alias pages. | ||
| 1176 | const KPageProperties dst_properties = {new_perm, false, false, | ||
| 1177 | DisableMergeAttribute::DisableHead}; | ||
| 1178 | R_TRY( | ||
| 1179 | this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false)); | ||
| 1180 | |||
| 1181 | // Apply the memory block updates. | ||
| 1182 | m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, | ||
| 1183 | src_state, new_perm, KMemoryAttribute::Locked, | ||
| 1184 | KMemoryBlockDisableMergeAttribute::Locked, | ||
| 1185 | KMemoryBlockDisableMergeAttribute::None); | ||
| 1186 | m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, | ||
| 1187 | KMemoryState::AliasCode, new_perm, KMemoryAttribute::None, | ||
| 1188 | KMemoryBlockDisableMergeAttribute::Normal, | ||
| 1189 | KMemoryBlockDisableMergeAttribute::None); | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | R_SUCCEED(); | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 1196 | size_t size) { | ||
| 1197 | // Validate the mapping request. | ||
| 1198 | R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), | ||
| 1199 | ResultInvalidMemoryRegion); | ||
| 1200 | |||
| 1201 | // Lock the table. | ||
| 1202 | KScopedLightLock lk(m_general_lock); | ||
| 1203 | |||
| 1204 | // Verify that the source memory is locked normal heap. | ||
| 1205 | size_t num_src_allocator_blocks; | ||
| 1206 | R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size, | ||
| 1207 | KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, | ||
| 1208 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 1209 | KMemoryAttribute::Locked)); | ||
| 1210 | |||
| 1211 | // Verify that the destination memory is aliasable code. | ||
| 1212 | size_t num_dst_allocator_blocks; | ||
| 1213 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 1214 | std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias, | ||
| 1215 | KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, | ||
| 1216 | KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None)); | ||
| 1217 | |||
| 1218 | // Determine whether any pages being unmapped are code. | ||
| 1219 | bool any_code_pages = false; | ||
| 1220 | { | ||
| 1221 | KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(dst_address); | ||
| 1222 | while (true) { | ||
| 1223 | // Get the memory info. | ||
| 1224 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1225 | |||
| 1226 | // Check if the memory has code flag. | ||
| 1227 | if (True(info.GetState() & KMemoryState::FlagCode)) { | ||
| 1228 | any_code_pages = true; | ||
| 1229 | break; | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | // Check if we're done. | ||
| 1233 | if (dst_address + size - 1 <= info.GetLastAddress()) { | ||
| 1234 | break; | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | // Advance. | ||
| 1238 | ++it; | ||
| 1239 | } | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | // Ensure that we maintain the instruction cache. | ||
| 1243 | bool reprotected_pages = false; | ||
| 1244 | SCOPE_EXIT({ | ||
| 1245 | if (reprotected_pages && any_code_pages) { | ||
| 1246 | InvalidateInstructionCache(m_system, dst_address, size); | ||
| 1247 | } | ||
| 1248 | }); | ||
| 1249 | |||
| 1250 | // Unmap. | ||
| 1251 | { | ||
| 1252 | // Determine the number of pages being operated on. | ||
| 1253 | const size_t num_pages = size / PageSize; | ||
| 1254 | |||
| 1255 | // Create page groups for the memory being unmapped. | ||
| 1256 | KPageGroup pg(m_kernel, m_block_info_manager); | ||
| 1257 | |||
| 1258 | // Create the page group representing the destination. | ||
| 1259 | R_TRY(this->MakePageGroup(pg, dst_address, num_pages)); | ||
| 1260 | |||
| 1261 | // Verify that the page group contains the same pages as the source. | ||
| 1262 | R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion); | ||
| 1263 | |||
| 1264 | // Create an update allocator for the source. | ||
| 1265 | Result src_allocator_result; | ||
| 1266 | KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), | ||
| 1267 | m_memory_block_slab_manager, | ||
| 1268 | num_src_allocator_blocks); | ||
| 1269 | R_TRY(src_allocator_result); | ||
| 1270 | |||
| 1271 | // Create an update allocator for the destination. | ||
| 1272 | Result dst_allocator_result; | ||
| 1273 | KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), | ||
| 1274 | m_memory_block_slab_manager, | ||
| 1275 | num_dst_allocator_blocks); | ||
| 1276 | R_TRY(dst_allocator_result); | ||
| 1277 | |||
| 1278 | // We're going to perform an update, so create a helper. | ||
| 1279 | KScopedPageTableUpdater updater(this); | ||
| 1280 | |||
| 1281 | // Unmap the aliased copy of the pages. | ||
| 1282 | const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false, | ||
| 1283 | DisableMergeAttribute::None}; | ||
| 1284 | R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false, | ||
| 1285 | dst_unmap_properties, OperationType::Unmap, false)); | ||
| 1286 | |||
| 1287 | // Ensure that we re-map the aliased pages on failure. | ||
| 1288 | ON_RESULT_FAILURE { | ||
| 1289 | this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg); | ||
| 1290 | }; | ||
| 1291 | |||
| 1292 | // Try to set the permissions for the source pages back to what they should be. | ||
| 1293 | const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false, | ||
| 1294 | DisableMergeAttribute::EnableAndMergeHeadBodyTail}; | ||
| 1295 | R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, 0, false, src_properties, | ||
| 1296 | OperationType::ChangePermissions, false)); | ||
| 1297 | |||
| 1298 | // Apply the memory block updates. | ||
| 1299 | m_memory_block_manager.Update( | ||
| 1300 | std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None, | ||
| 1301 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 1302 | KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); | ||
| 1303 | m_memory_block_manager.Update( | ||
| 1304 | std::addressof(src_allocator), src_address, num_pages, KMemoryState::Normal, | ||
| 1305 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 1306 | KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked); | ||
| 1307 | |||
| 1308 | // Note that we reprotected pages. | ||
| 1309 | reprotected_pages = true; | ||
| 1310 | } | ||
| 1311 | |||
| 1312 | R_SUCCEED(); | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) { | ||
| 1316 | // Get the insecure memory resource limit and pool. | ||
| 1317 | auto* const insecure_resource_limit = KSystemControl::GetInsecureMemoryResourceLimit(m_kernel); | ||
| 1318 | const auto insecure_pool = | ||
| 1319 | static_cast<KMemoryManager::Pool>(KSystemControl::GetInsecureMemoryPool()); | ||
| 1320 | |||
| 1321 | // Reserve the insecure memory. | ||
| 1322 | // NOTE: ResultOutOfMemory is returned here instead of the usual LimitReached. | ||
| 1323 | KScopedResourceReservation memory_reservation(insecure_resource_limit, | ||
| 1324 | Svc::LimitableResource::PhysicalMemoryMax, size); | ||
| 1325 | R_UNLESS(memory_reservation.Succeeded(), ResultOutOfMemory); | ||
| 1326 | |||
| 1327 | // Allocate pages for the insecure memory. | ||
| 1328 | KPageGroup pg(m_kernel, m_block_info_manager); | ||
| 1329 | R_TRY(m_kernel.MemoryManager().AllocateAndOpen( | ||
| 1330 | std::addressof(pg), size / PageSize, | ||
| 1331 | KMemoryManager::EncodeOption(insecure_pool, KMemoryManager::Direction::FromFront))); | ||
| 1332 | |||
| 1333 | // Close the opened pages when we're done with them. | ||
| 1334 | // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed | ||
| 1335 | // automatically. | ||
| 1336 | SCOPE_EXIT({ pg.Close(); }); | ||
| 1337 | |||
| 1338 | // Clear all the newly allocated pages. | ||
| 1339 | for (const auto& it : pg) { | ||
| 1340 | std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()), | ||
| 1341 | static_cast<u32>(m_heap_fill_value), it.GetSize()); | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | // Lock the table. | ||
| 1345 | KScopedLightLock lk(m_general_lock); | ||
| 1346 | |||
| 1347 | // Validate that the address's state is valid. | ||
| 1348 | size_t num_allocator_blocks; | ||
| 1349 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | ||
| 1350 | KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, | ||
| 1351 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 1352 | KMemoryAttribute::None)); | ||
| 1353 | |||
| 1354 | // Create an update allocator. | ||
| 1355 | Result allocator_result; | ||
| 1356 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 1357 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 1358 | R_TRY(allocator_result); | ||
| 1359 | |||
| 1360 | // We're going to perform an update, so create a helper. | ||
| 1361 | KScopedPageTableUpdater updater(this); | ||
| 1362 | |||
| 1363 | // Map the pages. | ||
| 1364 | const size_t num_pages = size / PageSize; | ||
| 1365 | const KPageProperties map_properties = {KMemoryPermission::UserReadWrite, false, false, | ||
| 1366 | DisableMergeAttribute::DisableHead}; | ||
| 1367 | R_TRY(this->Operate(updater.GetPageList(), address, num_pages, pg, map_properties, | ||
| 1368 | OperationType::MapGroup, false)); | ||
| 1369 | |||
| 1370 | // Apply the memory block update. | ||
| 1371 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, | ||
| 1372 | KMemoryState::Insecure, KMemoryPermission::UserReadWrite, | ||
| 1373 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 1374 | KMemoryBlockDisableMergeAttribute::None); | ||
| 1375 | |||
| 1376 | // Update our mapped insecure size. | ||
| 1377 | m_mapped_insecure_memory += size; | ||
| 1378 | |||
| 1379 | // Commit the memory reservation. | ||
| 1380 | memory_reservation.Commit(); | ||
| 1381 | |||
| 1382 | // We succeeded. | ||
| 1383 | R_SUCCEED(); | ||
| 1384 | } | ||
| 1385 | |||
| 1386 | Result KPageTableBase::UnmapInsecureMemory(KProcessAddress address, size_t size) { | ||
| 1387 | // Lock the table. | ||
| 1388 | KScopedLightLock lk(m_general_lock); | ||
| 1389 | |||
| 1390 | // Check the memory state. | ||
| 1391 | size_t num_allocator_blocks; | ||
| 1392 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | ||
| 1393 | KMemoryState::All, KMemoryState::Insecure, KMemoryPermission::All, | ||
| 1394 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, | ||
| 1395 | KMemoryAttribute::None)); | ||
| 1396 | |||
| 1397 | // Create an update allocator. | ||
| 1398 | Result allocator_result; | ||
| 1399 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 1400 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 1401 | R_TRY(allocator_result); | ||
| 1402 | |||
| 1403 | // We're going to perform an update, so create a helper. | ||
| 1404 | KScopedPageTableUpdater updater(this); | ||
| 1405 | |||
| 1406 | // Unmap the memory. | ||
| 1407 | const size_t num_pages = size / PageSize; | ||
| 1408 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 1409 | DisableMergeAttribute::None}; | ||
| 1410 | R_TRY(this->Operate(updater.GetPageList(), address, num_pages, 0, false, unmap_properties, | ||
| 1411 | OperationType::Unmap, false)); | ||
| 1412 | |||
| 1413 | // Apply the memory block update. | ||
| 1414 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, | ||
| 1415 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 1416 | KMemoryBlockDisableMergeAttribute::None, | ||
| 1417 | KMemoryBlockDisableMergeAttribute::Normal); | ||
| 1418 | |||
| 1419 | // Update our mapped insecure size. | ||
| 1420 | m_mapped_insecure_memory -= size; | ||
| 1421 | |||
| 1422 | // Release the insecure memory from the insecure limit. | ||
| 1423 | if (auto* const insecure_resource_limit = | ||
| 1424 | KSystemControl::GetInsecureMemoryResourceLimit(m_kernel); | ||
| 1425 | insecure_resource_limit != nullptr) { | ||
| 1426 | insecure_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, size); | ||
| 1427 | } | ||
| 1428 | |||
| 1429 | R_SUCCEED(); | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | KProcessAddress KPageTableBase::FindFreeArea(KProcessAddress region_start, size_t region_num_pages, | ||
| 1433 | size_t num_pages, size_t alignment, size_t offset, | ||
| 1434 | size_t guard_pages) const { | ||
| 1435 | KProcessAddress address = 0; | ||
| 1436 | |||
| 1437 | if (num_pages <= region_num_pages) { | ||
| 1438 | if (this->IsAslrEnabled()) { | ||
| 1439 | // Try to directly find a free area up to 8 times. | ||
| 1440 | for (size_t i = 0; i < 8; i++) { | ||
| 1441 | const size_t random_offset = | ||
| 1442 | KSystemControl::GenerateRandomRange( | ||
| 1443 | 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) * | ||
| 1444 | alignment; | ||
| 1445 | const KProcessAddress candidate = | ||
| 1446 | Common::AlignDown(GetInteger(region_start + random_offset), alignment) + offset; | ||
| 1447 | |||
| 1448 | KMemoryInfo info; | ||
| 1449 | Svc::PageInfo page_info; | ||
| 1450 | R_ASSERT(this->QueryInfoImpl(std::addressof(info), std::addressof(page_info), | ||
| 1451 | candidate)); | ||
| 1452 | |||
| 1453 | if (info.m_state != KMemoryState::Free) { | ||
| 1454 | continue; | ||
| 1455 | } | ||
| 1456 | if (!(region_start <= candidate)) { | ||
| 1457 | continue; | ||
| 1458 | } | ||
| 1459 | if (!(info.GetAddress() + guard_pages * PageSize <= GetInteger(candidate))) { | ||
| 1460 | continue; | ||
| 1461 | } | ||
| 1462 | if (!(candidate + (num_pages + guard_pages) * PageSize - 1 <= | ||
| 1463 | info.GetLastAddress())) { | ||
| 1464 | continue; | ||
| 1465 | } | ||
| 1466 | if (!(candidate + (num_pages + guard_pages) * PageSize - 1 <= | ||
| 1467 | region_start + region_num_pages * PageSize - 1)) { | ||
| 1468 | continue; | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | address = candidate; | ||
| 1472 | break; | ||
| 1473 | } | ||
| 1474 | // Fall back to finding the first free area with a random offset. | ||
| 1475 | if (address == 0) { | ||
| 1476 | // NOTE: Nintendo does not account for guard pages here. | ||
| 1477 | // This may theoretically cause an offset to be chosen that cannot be mapped. | ||
| 1478 | // We will account for guard pages. | ||
| 1479 | const size_t offset_pages = KSystemControl::GenerateRandomRange( | ||
| 1480 | 0, region_num_pages - num_pages - guard_pages); | ||
| 1481 | address = m_memory_block_manager.FindFreeArea( | ||
| 1482 | region_start + offset_pages * PageSize, region_num_pages - offset_pages, | ||
| 1483 | num_pages, alignment, offset, guard_pages); | ||
| 1484 | } | ||
| 1485 | } | ||
| 1486 | // Find the first free area. | ||
| 1487 | if (address == 0) { | ||
| 1488 | address = m_memory_block_manager.FindFreeArea(region_start, region_num_pages, num_pages, | ||
| 1489 | alignment, offset, guard_pages); | ||
| 1490 | } | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | return address; | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | size_t KPageTableBase::GetSize(KMemoryState state) const { | ||
| 1497 | // Lock the table. | ||
| 1498 | KScopedLightLock lk(m_general_lock); | ||
| 1499 | |||
| 1500 | // Iterate, counting blocks with the desired state. | ||
| 1501 | size_t total_size = 0; | ||
| 1502 | for (KMemoryBlockManager::const_iterator it = | ||
| 1503 | m_memory_block_manager.FindIterator(m_address_space_start); | ||
| 1504 | it != m_memory_block_manager.end(); ++it) { | ||
| 1505 | // Get the memory info. | ||
| 1506 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1507 | if (info.GetState() == state) { | ||
| 1508 | total_size += info.GetSize(); | ||
| 1509 | } | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | return total_size; | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | size_t KPageTableBase::GetCodeSize() const { | ||
| 1516 | return this->GetSize(KMemoryState::Code); | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | size_t KPageTableBase::GetCodeDataSize() const { | ||
| 1520 | return this->GetSize(KMemoryState::CodeData); | ||
| 1521 | } | ||
| 1522 | |||
| 1523 | size_t KPageTableBase::GetAliasCodeSize() const { | ||
| 1524 | return this->GetSize(KMemoryState::AliasCode); | ||
| 1525 | } | ||
| 1526 | |||
| 1527 | size_t KPageTableBase::GetAliasCodeDataSize() const { | ||
| 1528 | return this->GetSize(KMemoryState::AliasCodeData); | ||
| 1529 | } | ||
| 1530 | |||
| 1531 | Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address, | ||
| 1532 | size_t num_pages, KMemoryPermission perm) { | ||
| 1533 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 1534 | |||
| 1535 | // Create a page group to hold the pages we allocate. | ||
| 1536 | KPageGroup pg(m_kernel, m_block_info_manager); | ||
| 1537 | |||
| 1538 | // Allocate the pages. | ||
| 1539 | R_TRY( | ||
| 1540 | m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option)); | ||
| 1541 | |||
| 1542 | // Ensure that the page group is closed when we're done working with it. | ||
| 1543 | SCOPE_EXIT({ pg.Close(); }); | ||
| 1544 | |||
| 1545 | // Clear all pages. | ||
| 1546 | for (const auto& it : pg) { | ||
| 1547 | std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()), | ||
| 1548 | static_cast<u32>(m_heap_fill_value), it.GetSize()); | ||
| 1549 | } | ||
| 1550 | |||
| 1551 | // Map the pages. | ||
| 1552 | const KPageProperties properties = {perm, false, false, DisableMergeAttribute::None}; | ||
| 1553 | R_RETURN(this->Operate(page_list, address, num_pages, pg, properties, OperationType::MapGroup, | ||
| 1554 | false)); | ||
| 1555 | } | ||
| 1556 | |||
| 1557 | Result KPageTableBase::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address, | ||
| 1558 | const KPageGroup& pg, const KPageProperties properties, | ||
| 1559 | bool reuse_ll) { | ||
| 1560 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 1561 | |||
| 1562 | // Note the current address, so that we can iterate. | ||
| 1563 | const KProcessAddress start_address = address; | ||
| 1564 | KProcessAddress cur_address = address; | ||
| 1565 | |||
| 1566 | // Ensure that we clean up on failure. | ||
| 1567 | ON_RESULT_FAILURE { | ||
| 1568 | ASSERT(!reuse_ll); | ||
| 1569 | if (cur_address != start_address) { | ||
| 1570 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 1571 | DisableMergeAttribute::None}; | ||
| 1572 | R_ASSERT(this->Operate(page_list, start_address, | ||
| 1573 | (cur_address - start_address) / PageSize, 0, false, | ||
| 1574 | unmap_properties, OperationType::Unmap, true)); | ||
| 1575 | } | ||
| 1576 | }; | ||
| 1577 | |||
| 1578 | // Iterate, mapping all pages in the group. | ||
| 1579 | for (const auto& block : pg) { | ||
| 1580 | // Map and advance. | ||
| 1581 | const KPageProperties cur_properties = | ||
| 1582 | (cur_address == start_address) | ||
| 1583 | ? properties | ||
| 1584 | : KPageProperties{properties.perm, properties.io, properties.uncached, | ||
| 1585 | DisableMergeAttribute::None}; | ||
| 1586 | R_TRY(this->Operate(page_list, cur_address, block.GetNumPages(), block.GetAddress(), true, | ||
| 1587 | cur_properties, OperationType::Map, reuse_ll)); | ||
| 1588 | cur_address += block.GetSize(); | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | // We succeeded! | ||
| 1592 | R_SUCCEED(); | ||
| 1593 | } | ||
| 1594 | |||
| 1595 | void KPageTableBase::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size, | ||
| 1596 | const KPageGroup& pg) { | ||
| 1597 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 1598 | |||
| 1599 | // Note the current address, so that we can iterate. | ||
| 1600 | const KProcessAddress start_address = address; | ||
| 1601 | const KProcessAddress last_address = start_address + size - 1; | ||
| 1602 | const KProcessAddress end_address = last_address + 1; | ||
| 1603 | |||
| 1604 | // Iterate over the memory. | ||
| 1605 | auto pg_it = pg.begin(); | ||
| 1606 | ASSERT(pg_it != pg.end()); | ||
| 1607 | |||
| 1608 | KPhysicalAddress pg_phys_addr = pg_it->GetAddress(); | ||
| 1609 | size_t pg_pages = pg_it->GetNumPages(); | ||
| 1610 | |||
| 1611 | auto it = m_memory_block_manager.FindIterator(start_address); | ||
| 1612 | while (true) { | ||
| 1613 | // Check that the iterator is valid. | ||
| 1614 | ASSERT(it != m_memory_block_manager.end()); | ||
| 1615 | |||
| 1616 | // Get the memory info. | ||
| 1617 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1618 | |||
| 1619 | // Determine the range to map. | ||
| 1620 | KProcessAddress map_address = std::max<u64>(info.GetAddress(), GetInteger(start_address)); | ||
| 1621 | const KProcessAddress map_end_address = | ||
| 1622 | std::min<u64>(info.GetEndAddress(), GetInteger(end_address)); | ||
| 1623 | ASSERT(map_end_address != map_address); | ||
| 1624 | |||
| 1625 | // Determine if we should disable head merge. | ||
| 1626 | const bool disable_head_merge = | ||
| 1627 | info.GetAddress() >= GetInteger(start_address) && | ||
| 1628 | True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal); | ||
| 1629 | const KPageProperties map_properties = { | ||
| 1630 | info.GetPermission(), false, false, | ||
| 1631 | disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None}; | ||
| 1632 | |||
| 1633 | // While we have pages to map, map them. | ||
| 1634 | size_t map_pages = (map_end_address - map_address) / PageSize; | ||
| 1635 | while (map_pages > 0) { | ||
| 1636 | // Check if we're at the end of the physical block. | ||
| 1637 | if (pg_pages == 0) { | ||
| 1638 | // Ensure there are more pages to map. | ||
| 1639 | ASSERT(pg_it != pg.end()); | ||
| 1640 | |||
| 1641 | // Advance our physical block. | ||
| 1642 | ++pg_it; | ||
| 1643 | pg_phys_addr = pg_it->GetAddress(); | ||
| 1644 | pg_pages = pg_it->GetNumPages(); | ||
| 1645 | } | ||
| 1646 | |||
| 1647 | // Map whatever we can. | ||
| 1648 | const size_t cur_pages = std::min(pg_pages, map_pages); | ||
| 1649 | R_ASSERT(this->Operate(page_list, map_address, map_pages, pg_phys_addr, true, | ||
| 1650 | map_properties, OperationType::Map, true)); | ||
| 1651 | |||
| 1652 | // Advance. | ||
| 1653 | map_address += cur_pages * PageSize; | ||
| 1654 | map_pages -= cur_pages; | ||
| 1655 | |||
| 1656 | pg_phys_addr += cur_pages * PageSize; | ||
| 1657 | pg_pages -= cur_pages; | ||
| 1658 | } | ||
| 1659 | |||
| 1660 | // Check if we're done. | ||
| 1661 | if (last_address <= info.GetLastAddress()) { | ||
| 1662 | break; | ||
| 1663 | } | ||
| 1664 | |||
| 1665 | // Advance. | ||
| 1666 | ++it; | ||
| 1667 | } | ||
| 1668 | |||
| 1669 | // Check that we re-mapped precisely the page group. | ||
| 1670 | ASSERT((++pg_it) == pg.end()); | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages) { | ||
| 1674 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 1675 | |||
| 1676 | const size_t size = num_pages * PageSize; | ||
| 1677 | |||
| 1678 | // We're making a new group, not adding to an existing one. | ||
| 1679 | R_UNLESS(pg.empty(), ResultInvalidCurrentMemory); | ||
| 1680 | |||
| 1681 | auto& impl = this->GetImpl(); | ||
| 1682 | |||
| 1683 | // Begin traversal. | ||
| 1684 | TraversalContext context; | ||
| 1685 | TraversalEntry next_entry; | ||
| 1686 | R_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr), | ||
| 1687 | ResultInvalidCurrentMemory); | ||
| 1688 | |||
| 1689 | // Prepare tracking variables. | ||
| 1690 | KPhysicalAddress cur_addr = next_entry.phys_addr; | ||
| 1691 | size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1)); | ||
| 1692 | size_t tot_size = cur_size; | ||
| 1693 | |||
| 1694 | // Iterate, adding to group as we go. | ||
| 1695 | while (tot_size < size) { | ||
| 1696 | R_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)), | ||
| 1697 | ResultInvalidCurrentMemory); | ||
| 1698 | |||
| 1699 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 1700 | const size_t cur_pages = cur_size / PageSize; | ||
| 1701 | |||
| 1702 | R_UNLESS(IsHeapPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); | ||
| 1703 | R_TRY(pg.AddBlock(cur_addr, cur_pages)); | ||
| 1704 | |||
| 1705 | cur_addr = next_entry.phys_addr; | ||
| 1706 | cur_size = next_entry.block_size; | ||
| 1707 | } else { | ||
| 1708 | cur_size += next_entry.block_size; | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | tot_size += next_entry.block_size; | ||
| 1712 | } | ||
| 1713 | |||
| 1714 | // Ensure we add the right amount for the last block. | ||
| 1715 | if (tot_size > size) { | ||
| 1716 | cur_size -= (tot_size - size); | ||
| 1717 | } | ||
| 1718 | |||
| 1719 | // add the last block. | ||
| 1720 | const size_t cur_pages = cur_size / PageSize; | ||
| 1721 | R_UNLESS(IsHeapPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); | ||
| 1722 | R_TRY(pg.AddBlock(cur_addr, cur_pages)); | ||
| 1723 | |||
| 1724 | R_SUCCEED(); | ||
| 1725 | } | ||
| 1726 | |||
| 1727 | bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, | ||
| 1728 | size_t num_pages) { | ||
| 1729 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 1730 | |||
| 1731 | const size_t size = num_pages * PageSize; | ||
| 1732 | |||
| 1733 | // Empty groups are necessarily invalid. | ||
| 1734 | if (pg.empty()) { | ||
| 1735 | return false; | ||
| 1736 | } | ||
| 1737 | |||
| 1738 | auto& impl = this->GetImpl(); | ||
| 1739 | |||
| 1740 | // We're going to validate that the group we'd expect is the group we see. | ||
| 1741 | auto cur_it = pg.begin(); | ||
| 1742 | KPhysicalAddress cur_block_address = cur_it->GetAddress(); | ||
| 1743 | size_t cur_block_pages = cur_it->GetNumPages(); | ||
| 1744 | |||
| 1745 | auto UpdateCurrentIterator = [&]() { | ||
| 1746 | if (cur_block_pages == 0) { | ||
| 1747 | if ((++cur_it) == pg.end()) { | ||
| 1748 | return false; | ||
| 1749 | } | ||
| 1750 | |||
| 1751 | cur_block_address = cur_it->GetAddress(); | ||
| 1752 | cur_block_pages = cur_it->GetNumPages(); | ||
| 1753 | } | ||
| 1754 | return true; | ||
| 1755 | }; | ||
| 1756 | |||
| 1757 | // Begin traversal. | ||
| 1758 | TraversalContext context; | ||
| 1759 | TraversalEntry next_entry; | ||
| 1760 | if (!impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr)) { | ||
| 1761 | return false; | ||
| 1762 | } | ||
| 1763 | |||
| 1764 | // Prepare tracking variables. | ||
| 1765 | KPhysicalAddress cur_addr = next_entry.phys_addr; | ||
| 1766 | size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1)); | ||
| 1767 | size_t tot_size = cur_size; | ||
| 1768 | |||
| 1769 | // Iterate, comparing expected to actual. | ||
| 1770 | while (tot_size < size) { | ||
| 1771 | if (!impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))) { | ||
| 1772 | return false; | ||
| 1773 | } | ||
| 1774 | |||
| 1775 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 1776 | const size_t cur_pages = cur_size / PageSize; | ||
| 1777 | |||
| 1778 | if (!IsHeapPhysicalAddress(cur_addr)) { | ||
| 1779 | return false; | ||
| 1780 | } | ||
| 1781 | |||
| 1782 | if (!UpdateCurrentIterator()) { | ||
| 1783 | return false; | ||
| 1784 | } | ||
| 1785 | |||
| 1786 | if (cur_block_address != cur_addr || cur_block_pages < cur_pages) { | ||
| 1787 | return false; | ||
| 1788 | } | ||
| 1789 | |||
| 1790 | cur_block_address += cur_size; | ||
| 1791 | cur_block_pages -= cur_pages; | ||
| 1792 | cur_addr = next_entry.phys_addr; | ||
| 1793 | cur_size = next_entry.block_size; | ||
| 1794 | } else { | ||
| 1795 | cur_size += next_entry.block_size; | ||
| 1796 | } | ||
| 1797 | |||
| 1798 | tot_size += next_entry.block_size; | ||
| 1799 | } | ||
| 1800 | |||
| 1801 | // Ensure we compare the right amount for the last block. | ||
| 1802 | if (tot_size > size) { | ||
| 1803 | cur_size -= (tot_size - size); | ||
| 1804 | } | ||
| 1805 | |||
| 1806 | if (!IsHeapPhysicalAddress(cur_addr)) { | ||
| 1807 | return false; | ||
| 1808 | } | ||
| 1809 | |||
| 1810 | if (!UpdateCurrentIterator()) { | ||
| 1811 | return false; | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize); | ||
| 1815 | } | ||
| 1816 | |||
| 1817 | Result KPageTableBase::GetContiguousMemoryRangeWithState( | ||
| 1818 | MemoryRange* out, KProcessAddress address, size_t size, KMemoryState state_mask, | ||
| 1819 | KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 1820 | KMemoryAttribute attr_mask, KMemoryAttribute attr) { | ||
| 1821 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 1822 | |||
| 1823 | auto& impl = this->GetImpl(); | ||
| 1824 | |||
| 1825 | // Begin a traversal. | ||
| 1826 | TraversalContext context; | ||
| 1827 | TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0}; | ||
| 1828 | R_UNLESS(impl.BeginTraversal(std::addressof(cur_entry), std::addressof(context), address), | ||
| 1829 | ResultInvalidCurrentMemory); | ||
| 1830 | |||
| 1831 | // Traverse until we have enough size or we aren't contiguous any more. | ||
| 1832 | const KPhysicalAddress phys_address = cur_entry.phys_addr; | ||
| 1833 | size_t contig_size; | ||
| 1834 | for (contig_size = | ||
| 1835 | cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1)); | ||
| 1836 | contig_size < size; contig_size += cur_entry.block_size) { | ||
| 1837 | if (!impl.ContinueTraversal(std::addressof(cur_entry), std::addressof(context))) { | ||
| 1838 | break; | ||
| 1839 | } | ||
| 1840 | if (cur_entry.phys_addr != phys_address + contig_size) { | ||
| 1841 | break; | ||
| 1842 | } | ||
| 1843 | } | ||
| 1844 | |||
| 1845 | // Take the minimum size for our region. | ||
| 1846 | size = std::min(size, contig_size); | ||
| 1847 | |||
| 1848 | // Check that the memory is contiguous (modulo the reference count bit). | ||
| 1849 | const KMemoryState test_state_mask = state_mask | KMemoryState::FlagReferenceCounted; | ||
| 1850 | const bool is_heap = R_SUCCEEDED(this->CheckMemoryStateContiguous( | ||
| 1851 | address, size, test_state_mask, state | KMemoryState::FlagReferenceCounted, perm_mask, perm, | ||
| 1852 | attr_mask, attr)); | ||
| 1853 | if (!is_heap) { | ||
| 1854 | R_TRY(this->CheckMemoryStateContiguous(address, size, test_state_mask, state, perm_mask, | ||
| 1855 | perm, attr_mask, attr)); | ||
| 1856 | } | ||
| 1857 | |||
| 1858 | // The memory is contiguous, so set the output range. | ||
| 1859 | out->Set(phys_address, size, is_heap); | ||
| 1860 | R_SUCCEED(); | ||
| 1861 | } | ||
| 1862 | |||
| 1863 | Result KPageTableBase::SetMemoryPermission(KProcessAddress addr, size_t size, | ||
| 1864 | Svc::MemoryPermission svc_perm) { | ||
| 1865 | const size_t num_pages = size / PageSize; | ||
| 1866 | |||
| 1867 | // Lock the table. | ||
| 1868 | KScopedLightLock lk(m_general_lock); | ||
| 1869 | |||
| 1870 | // Verify we can change the memory permission. | ||
| 1871 | KMemoryState old_state; | ||
| 1872 | KMemoryPermission old_perm; | ||
| 1873 | size_t num_allocator_blocks; | ||
| 1874 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr, | ||
| 1875 | std::addressof(num_allocator_blocks), addr, size, | ||
| 1876 | KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, | ||
| 1877 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 1878 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 1879 | |||
| 1880 | // Determine new perm. | ||
| 1881 | const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm); | ||
| 1882 | R_SUCCEED_IF(old_perm == new_perm); | ||
| 1883 | |||
| 1884 | // Create an update allocator. | ||
| 1885 | Result allocator_result; | ||
| 1886 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 1887 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 1888 | R_TRY(allocator_result); | ||
| 1889 | |||
| 1890 | // We're going to perform an update, so create a helper. | ||
| 1891 | KScopedPageTableUpdater updater(this); | ||
| 1892 | |||
| 1893 | // Perform mapping operation. | ||
| 1894 | const KPageProperties properties = {new_perm, false, false, DisableMergeAttribute::None}; | ||
| 1895 | R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties, | ||
| 1896 | OperationType::ChangePermissions, false)); | ||
| 1897 | |||
| 1898 | // Update the blocks. | ||
| 1899 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, | ||
| 1900 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | ||
| 1901 | KMemoryBlockDisableMergeAttribute::None); | ||
| 1902 | |||
| 1903 | R_SUCCEED(); | ||
| 1904 | } | ||
| 1905 | |||
| 1906 | Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t size, | ||
| 1907 | Svc::MemoryPermission svc_perm) { | ||
| 1908 | const size_t num_pages = size / PageSize; | ||
| 1909 | |||
| 1910 | // Lock the table. | ||
| 1911 | KScopedLightLock lk(m_general_lock); | ||
| 1912 | |||
| 1913 | // Verify we can change the memory permission. | ||
| 1914 | KMemoryState old_state; | ||
| 1915 | KMemoryPermission old_perm; | ||
| 1916 | size_t num_allocator_blocks; | ||
| 1917 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr, | ||
| 1918 | std::addressof(num_allocator_blocks), addr, size, | ||
| 1919 | KMemoryState::FlagCode, KMemoryState::FlagCode, | ||
| 1920 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 1921 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 1922 | |||
| 1923 | // Make a new page group for the region. | ||
| 1924 | KPageGroup pg(m_kernel, m_block_info_manager); | ||
| 1925 | |||
| 1926 | // Determine new perm/state. | ||
| 1927 | const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm); | ||
| 1928 | KMemoryState new_state = old_state; | ||
| 1929 | const bool is_w = (new_perm & KMemoryPermission::UserWrite) == KMemoryPermission::UserWrite; | ||
| 1930 | const bool is_x = (new_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute; | ||
| 1931 | const bool was_x = | ||
| 1932 | (old_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute; | ||
| 1933 | ASSERT(!(is_w && is_x)); | ||
| 1934 | |||
| 1935 | if (is_w) { | ||
| 1936 | switch (old_state) { | ||
| 1937 | case KMemoryState::Code: | ||
| 1938 | new_state = KMemoryState::CodeData; | ||
| 1939 | break; | ||
| 1940 | case KMemoryState::AliasCode: | ||
| 1941 | new_state = KMemoryState::AliasCodeData; | ||
| 1942 | break; | ||
| 1943 | default: | ||
| 1944 | UNREACHABLE(); | ||
| 1945 | } | ||
| 1946 | } | ||
| 1947 | |||
| 1948 | // Create a page group, if we're setting execute permissions. | ||
| 1949 | if (is_x) { | ||
| 1950 | R_TRY(this->MakePageGroup(pg, GetInteger(addr), num_pages)); | ||
| 1951 | } | ||
| 1952 | |||
| 1953 | // Succeed if there's nothing to do. | ||
| 1954 | R_SUCCEED_IF(old_perm == new_perm && old_state == new_state); | ||
| 1955 | |||
| 1956 | // Create an update allocator. | ||
| 1957 | Result allocator_result; | ||
| 1958 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 1959 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 1960 | R_TRY(allocator_result); | ||
| 1961 | |||
| 1962 | // We're going to perform an update, so create a helper. | ||
| 1963 | KScopedPageTableUpdater updater(this); | ||
| 1964 | |||
| 1965 | // Perform mapping operation. | ||
| 1966 | const KPageProperties properties = {new_perm, false, false, DisableMergeAttribute::None}; | ||
| 1967 | const auto operation = was_x ? OperationType::ChangePermissionsAndRefreshAndFlush | ||
| 1968 | : OperationType::ChangePermissions; | ||
| 1969 | R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties, operation, | ||
| 1970 | false)); | ||
| 1971 | |||
| 1972 | // Update the blocks. | ||
| 1973 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm, | ||
| 1974 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | ||
| 1975 | KMemoryBlockDisableMergeAttribute::None); | ||
| 1976 | |||
| 1977 | // Ensure cache coherency, if we're setting pages as executable. | ||
| 1978 | if (is_x) { | ||
| 1979 | for (const auto& block : pg) { | ||
| 1980 | StoreDataCache(GetHeapVirtualPointer(m_kernel, block.GetAddress()), block.GetSize()); | ||
| 1981 | } | ||
| 1982 | InvalidateInstructionCache(m_system, addr, size); | ||
| 1983 | } | ||
| 1984 | |||
| 1985 | R_SUCCEED(); | ||
| 1986 | } | ||
| 1987 | |||
| 1988 | Result KPageTableBase::SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask, | ||
| 1989 | KMemoryAttribute attr) { | ||
| 1990 | const size_t num_pages = size / PageSize; | ||
| 1991 | ASSERT((mask | KMemoryAttribute::SetMask) == KMemoryAttribute::SetMask); | ||
| 1992 | |||
| 1993 | // Lock the table. | ||
| 1994 | KScopedLightLock lk(m_general_lock); | ||
| 1995 | |||
| 1996 | // Verify we can change the memory attribute. | ||
| 1997 | KMemoryState old_state; | ||
| 1998 | KMemoryPermission old_perm; | ||
| 1999 | KMemoryAttribute old_attr; | ||
| 2000 | size_t num_allocator_blocks; | ||
| 2001 | constexpr KMemoryAttribute AttributeTestMask = | ||
| 2002 | ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared); | ||
| 2003 | const KMemoryState state_test_mask = | ||
| 2004 | (True(mask & KMemoryAttribute::Uncached) ? KMemoryState::FlagCanChangeAttribute | ||
| 2005 | : KMemoryState::None) | | ||
| 2006 | (True(mask & KMemoryAttribute::PermissionLocked) ? KMemoryState::FlagCanPermissionLock | ||
| 2007 | : KMemoryState::None); | ||
| 2008 | R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), | ||
| 2009 | std::addressof(old_attr), std::addressof(num_allocator_blocks), | ||
| 2010 | addr, size, state_test_mask, state_test_mask, | ||
| 2011 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2012 | AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask)); | ||
| 2013 | |||
| 2014 | // Create an update allocator. | ||
| 2015 | Result allocator_result; | ||
| 2016 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2017 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2018 | R_TRY(allocator_result); | ||
| 2019 | |||
| 2020 | // We're going to perform an update, so create a helper. | ||
| 2021 | KScopedPageTableUpdater updater(this); | ||
| 2022 | |||
| 2023 | // If we need to, perform a change attribute operation. | ||
| 2024 | if (True(mask & KMemoryAttribute::Uncached)) { | ||
| 2025 | // Determine the new attribute. | ||
| 2026 | const KMemoryAttribute new_attr = | ||
| 2027 | static_cast<KMemoryAttribute>(((old_attr & ~mask) | (attr & mask))); | ||
| 2028 | |||
| 2029 | // Perform operation. | ||
| 2030 | const KPageProperties properties = {old_perm, false, | ||
| 2031 | True(new_attr & KMemoryAttribute::Uncached), | ||
| 2032 | DisableMergeAttribute::None}; | ||
| 2033 | R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, 0, false, properties, | ||
| 2034 | OperationType::ChangePermissionsAndRefreshAndFlush, false)); | ||
| 2035 | } | ||
| 2036 | |||
| 2037 | // Update the blocks. | ||
| 2038 | m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages, mask, attr); | ||
| 2039 | |||
| 2040 | R_SUCCEED(); | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) { | ||
| 2044 | // Lock the physical memory mutex. | ||
| 2045 | KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock); | ||
| 2046 | |||
| 2047 | // Try to perform a reduction in heap, instead of an extension. | ||
| 2048 | KProcessAddress cur_address; | ||
| 2049 | size_t allocation_size; | ||
| 2050 | { | ||
| 2051 | // Lock the table. | ||
| 2052 | KScopedLightLock lk(m_general_lock); | ||
| 2053 | |||
| 2054 | // Validate that setting heap size is possible at all. | ||
| 2055 | R_UNLESS(!m_is_kernel, ResultOutOfMemory); | ||
| 2056 | R_UNLESS(size <= static_cast<size_t>(m_heap_region_end - m_heap_region_start), | ||
| 2057 | ResultOutOfMemory); | ||
| 2058 | R_UNLESS(size <= m_max_heap_size, ResultOutOfMemory); | ||
| 2059 | |||
| 2060 | if (size < static_cast<size_t>(m_current_heap_end - m_heap_region_start)) { | ||
| 2061 | // The size being requested is less than the current size, so we need to free the end of | ||
| 2062 | // the heap. | ||
| 2063 | |||
| 2064 | // Validate memory state. | ||
| 2065 | size_t num_allocator_blocks; | ||
| 2066 | R_TRY(this->CheckMemoryState( | ||
| 2067 | std::addressof(num_allocator_blocks), m_heap_region_start + size, | ||
| 2068 | (m_current_heap_end - m_heap_region_start) - size, KMemoryState::All, | ||
| 2069 | KMemoryState::Normal, KMemoryPermission::All, KMemoryPermission::UserReadWrite, | ||
| 2070 | KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 2071 | |||
| 2072 | // Create an update allocator. | ||
| 2073 | Result allocator_result; | ||
| 2074 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2075 | m_memory_block_slab_manager, | ||
| 2076 | num_allocator_blocks); | ||
| 2077 | R_TRY(allocator_result); | ||
| 2078 | |||
| 2079 | // We're going to perform an update, so create a helper. | ||
| 2080 | KScopedPageTableUpdater updater(this); | ||
| 2081 | |||
| 2082 | // Unmap the end of the heap. | ||
| 2083 | const size_t num_pages = ((m_current_heap_end - m_heap_region_start) - size) / PageSize; | ||
| 2084 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 2085 | DisableMergeAttribute::None}; | ||
| 2086 | R_TRY(this->Operate(updater.GetPageList(), m_heap_region_start + size, num_pages, 0, | ||
| 2087 | false, unmap_properties, OperationType::Unmap, false)); | ||
| 2088 | |||
| 2089 | // Release the memory from the resource limit. | ||
| 2090 | m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, | ||
| 2091 | num_pages * PageSize); | ||
| 2092 | |||
| 2093 | // Apply the memory block update. | ||
| 2094 | m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, | ||
| 2095 | num_pages, KMemoryState::Free, KMemoryPermission::None, | ||
| 2096 | KMemoryAttribute::None, | ||
| 2097 | KMemoryBlockDisableMergeAttribute::None, | ||
| 2098 | size == 0 ? KMemoryBlockDisableMergeAttribute::Normal | ||
| 2099 | : KMemoryBlockDisableMergeAttribute::None); | ||
| 2100 | |||
| 2101 | // Update the current heap end. | ||
| 2102 | m_current_heap_end = m_heap_region_start + size; | ||
| 2103 | |||
| 2104 | // Set the output. | ||
| 2105 | *out = m_heap_region_start; | ||
| 2106 | R_SUCCEED(); | ||
| 2107 | } else if (size == static_cast<size_t>(m_current_heap_end - m_heap_region_start)) { | ||
| 2108 | // The size requested is exactly the current size. | ||
| 2109 | *out = m_heap_region_start; | ||
| 2110 | R_SUCCEED(); | ||
| 2111 | } else { | ||
| 2112 | // We have to allocate memory. Determine how much to allocate and where while the table | ||
| 2113 | // is locked. | ||
| 2114 | cur_address = m_current_heap_end; | ||
| 2115 | allocation_size = size - (m_current_heap_end - m_heap_region_start); | ||
| 2116 | } | ||
| 2117 | } | ||
| 2118 | |||
| 2119 | // Reserve memory for the heap extension. | ||
| 2120 | KScopedResourceReservation memory_reservation( | ||
| 2121 | m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, allocation_size); | ||
| 2122 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | ||
| 2123 | |||
| 2124 | // Allocate pages for the heap extension. | ||
| 2125 | KPageGroup pg(m_kernel, m_block_info_manager); | ||
| 2126 | R_TRY(m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), allocation_size / PageSize, | ||
| 2127 | m_allocate_option)); | ||
| 2128 | |||
| 2129 | // Close the opened pages when we're done with them. | ||
| 2130 | // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed | ||
| 2131 | // automatically. | ||
| 2132 | SCOPE_EXIT({ pg.Close(); }); | ||
| 2133 | |||
| 2134 | // Clear all the newly allocated pages. | ||
| 2135 | for (const auto& it : pg) { | ||
| 2136 | std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()), m_heap_fill_value, | ||
| 2137 | it.GetSize()); | ||
| 2138 | } | ||
| 2139 | |||
| 2140 | // Map the pages. | ||
| 2141 | { | ||
| 2142 | // Lock the table. | ||
| 2143 | KScopedLightLock lk(m_general_lock); | ||
| 2144 | |||
| 2145 | // Ensure that the heap hasn't changed since we began executing. | ||
| 2146 | ASSERT(cur_address == m_current_heap_end); | ||
| 2147 | |||
| 2148 | // Check the memory state. | ||
| 2149 | size_t num_allocator_blocks; | ||
| 2150 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), m_current_heap_end, | ||
| 2151 | allocation_size, KMemoryState::All, KMemoryState::Free, | ||
| 2152 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2153 | KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 2154 | |||
| 2155 | // Create an update allocator. | ||
| 2156 | Result allocator_result; | ||
| 2157 | KMemoryBlockManagerUpdateAllocator allocator( | ||
| 2158 | std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2159 | R_TRY(allocator_result); | ||
| 2160 | |||
| 2161 | // We're going to perform an update, so create a helper. | ||
| 2162 | KScopedPageTableUpdater updater(this); | ||
| 2163 | |||
| 2164 | // Map the pages. | ||
| 2165 | const size_t num_pages = allocation_size / PageSize; | ||
| 2166 | const KPageProperties map_properties = {KMemoryPermission::UserReadWrite, false, false, | ||
| 2167 | (m_current_heap_end == m_heap_region_start) | ||
| 2168 | ? DisableMergeAttribute::DisableHead | ||
| 2169 | : DisableMergeAttribute::None}; | ||
| 2170 | R_TRY(this->Operate(updater.GetPageList(), m_current_heap_end, num_pages, pg, | ||
| 2171 | map_properties, OperationType::MapGroup, false)); | ||
| 2172 | |||
| 2173 | // We succeeded, so commit our memory reservation. | ||
| 2174 | memory_reservation.Commit(); | ||
| 2175 | |||
| 2176 | // Apply the memory block update. | ||
| 2177 | m_memory_block_manager.Update( | ||
| 2178 | std::addressof(allocator), m_current_heap_end, num_pages, KMemoryState::Normal, | ||
| 2179 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 2180 | m_heap_region_start == m_current_heap_end ? KMemoryBlockDisableMergeAttribute::Normal | ||
| 2181 | : KMemoryBlockDisableMergeAttribute::None, | ||
| 2182 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2183 | |||
| 2184 | // Update the current heap end. | ||
| 2185 | m_current_heap_end = m_heap_region_start + size; | ||
| 2186 | |||
| 2187 | // Set the output. | ||
| 2188 | *out = m_heap_region_start; | ||
| 2189 | R_SUCCEED(); | ||
| 2190 | } | ||
| 2191 | } | ||
| 2192 | |||
| 2193 | Result KPageTableBase::SetMaxHeapSize(size_t size) { | ||
| 2194 | // Lock the table. | ||
| 2195 | KScopedLightLock lk(m_general_lock); | ||
| 2196 | |||
| 2197 | // Only process page tables are allowed to set heap size. | ||
| 2198 | ASSERT(!this->IsKernel()); | ||
| 2199 | |||
| 2200 | m_max_heap_size = size; | ||
| 2201 | |||
| 2202 | R_SUCCEED(); | ||
| 2203 | } | ||
| 2204 | |||
| 2205 | Result KPageTableBase::QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info, | ||
| 2206 | KProcessAddress addr) const { | ||
| 2207 | // If the address is invalid, create a fake block. | ||
| 2208 | if (!this->Contains(addr, 1)) { | ||
| 2209 | *out_info = { | ||
| 2210 | .m_address = GetInteger(m_address_space_end), | ||
| 2211 | .m_size = 0 - GetInteger(m_address_space_end), | ||
| 2212 | .m_state = static_cast<KMemoryState>(Svc::MemoryState::Inaccessible), | ||
| 2213 | .m_device_disable_merge_left_count = 0, | ||
| 2214 | .m_device_disable_merge_right_count = 0, | ||
| 2215 | .m_ipc_lock_count = 0, | ||
| 2216 | .m_device_use_count = 0, | ||
| 2217 | .m_ipc_disable_merge_count = 0, | ||
| 2218 | .m_permission = KMemoryPermission::None, | ||
| 2219 | .m_attribute = KMemoryAttribute::None, | ||
| 2220 | .m_original_permission = KMemoryPermission::None, | ||
| 2221 | .m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute::None, | ||
| 2222 | }; | ||
| 2223 | out_page_info->flags = 0; | ||
| 2224 | |||
| 2225 | R_SUCCEED(); | ||
| 2226 | } | ||
| 2227 | |||
| 2228 | // Otherwise, lock the table and query. | ||
| 2229 | KScopedLightLock lk(m_general_lock); | ||
| 2230 | R_RETURN(this->QueryInfoImpl(out_info, out_page_info, addr)); | ||
| 2231 | } | ||
| 2232 | |||
| 2233 | Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, | ||
| 2234 | KProcessAddress address) const { | ||
| 2235 | // Lock the table. | ||
| 2236 | KScopedLightLock lk(m_general_lock); | ||
| 2237 | |||
| 2238 | // Align the address down to page size. | ||
| 2239 | address = Common::AlignDown(GetInteger(address), PageSize); | ||
| 2240 | |||
| 2241 | // Verify that we can query the address. | ||
| 2242 | KMemoryInfo info; | ||
| 2243 | Svc::PageInfo page_info; | ||
| 2244 | R_TRY(this->QueryInfoImpl(std::addressof(info), std::addressof(page_info), address)); | ||
| 2245 | |||
| 2246 | // Check the memory state. | ||
| 2247 | R_TRY(this->CheckMemoryState(info, KMemoryState::FlagCanQueryPhysical, | ||
| 2248 | KMemoryState::FlagCanQueryPhysical, | ||
| 2249 | KMemoryPermission::UserReadExecute, KMemoryPermission::UserRead, | ||
| 2250 | KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 2251 | |||
| 2252 | // Prepare to traverse. | ||
| 2253 | KPhysicalAddress phys_addr; | ||
| 2254 | size_t phys_size; | ||
| 2255 | |||
| 2256 | KProcessAddress virt_addr = info.GetAddress(); | ||
| 2257 | KProcessAddress end_addr = info.GetEndAddress(); | ||
| 2258 | |||
| 2259 | // Perform traversal. | ||
| 2260 | { | ||
| 2261 | // Begin traversal. | ||
| 2262 | TraversalContext context; | ||
| 2263 | TraversalEntry next_entry; | ||
| 2264 | bool traverse_valid = | ||
| 2265 | m_impl->BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr); | ||
| 2266 | R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); | ||
| 2267 | |||
| 2268 | // Set tracking variables. | ||
| 2269 | phys_addr = next_entry.phys_addr; | ||
| 2270 | phys_size = next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1)); | ||
| 2271 | |||
| 2272 | // Iterate. | ||
| 2273 | while (true) { | ||
| 2274 | // Continue the traversal. | ||
| 2275 | traverse_valid = | ||
| 2276 | m_impl->ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 2277 | if (!traverse_valid) { | ||
| 2278 | break; | ||
| 2279 | } | ||
| 2280 | |||
| 2281 | if (next_entry.phys_addr != (phys_addr + phys_size)) { | ||
| 2282 | // Check if we're done. | ||
| 2283 | if (virt_addr <= address && address <= virt_addr + phys_size - 1) { | ||
| 2284 | break; | ||
| 2285 | } | ||
| 2286 | |||
| 2287 | // Advance. | ||
| 2288 | phys_addr = next_entry.phys_addr; | ||
| 2289 | virt_addr += next_entry.block_size; | ||
| 2290 | phys_size = | ||
| 2291 | next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1)); | ||
| 2292 | } else { | ||
| 2293 | phys_size += next_entry.block_size; | ||
| 2294 | } | ||
| 2295 | |||
| 2296 | // Check if we're done. | ||
| 2297 | if (end_addr < virt_addr + phys_size) { | ||
| 2298 | break; | ||
| 2299 | } | ||
| 2300 | } | ||
| 2301 | ASSERT(virt_addr <= address && address <= virt_addr + phys_size - 1); | ||
| 2302 | |||
| 2303 | // Ensure we use the right size. | ||
| 2304 | if (end_addr < virt_addr + phys_size) { | ||
| 2305 | phys_size = end_addr - virt_addr; | ||
| 2306 | } | ||
| 2307 | } | ||
| 2308 | |||
| 2309 | // Set the output. | ||
| 2310 | out->physical_address = GetInteger(phys_addr); | ||
| 2311 | out->virtual_address = GetInteger(virt_addr); | ||
| 2312 | out->size = phys_size; | ||
| 2313 | R_SUCCEED(); | ||
| 2314 | } | ||
| 2315 | |||
| 2316 | Result KPageTableBase::MapIoImpl(KProcessAddress* out, PageLinkedList* page_list, | ||
| 2317 | KPhysicalAddress phys_addr, size_t size, KMemoryState state, | ||
| 2318 | KMemoryPermission perm) { | ||
| 2319 | // Check pre-conditions. | ||
| 2320 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 2321 | ASSERT(Common::IsAligned(GetInteger(phys_addr), PageSize)); | ||
| 2322 | ASSERT(Common::IsAligned(size, PageSize)); | ||
| 2323 | ASSERT(size > 0); | ||
| 2324 | |||
| 2325 | R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress); | ||
| 2326 | const size_t num_pages = size / PageSize; | ||
| 2327 | const KPhysicalAddress last = phys_addr + size - 1; | ||
| 2328 | |||
| 2329 | // Get region extents. | ||
| 2330 | const KProcessAddress region_start = m_kernel_map_region_start; | ||
| 2331 | const size_t region_size = m_kernel_map_region_end - m_kernel_map_region_start; | ||
| 2332 | const size_t region_num_pages = region_size / PageSize; | ||
| 2333 | |||
| 2334 | ASSERT(this->CanContain(region_start, region_size, state)); | ||
| 2335 | |||
| 2336 | // Locate the memory region. | ||
| 2337 | const KMemoryRegion* region = KMemoryLayout::Find(m_kernel.MemoryLayout(), phys_addr); | ||
| 2338 | R_UNLESS(region != nullptr, ResultInvalidAddress); | ||
| 2339 | |||
| 2340 | ASSERT(region->Contains(GetInteger(phys_addr))); | ||
| 2341 | |||
| 2342 | // Ensure that the region is mappable. | ||
| 2343 | const bool is_rw = perm == KMemoryPermission::UserReadWrite; | ||
| 2344 | while (true) { | ||
| 2345 | // Check that the region exists. | ||
| 2346 | R_UNLESS(region != nullptr, ResultInvalidAddress); | ||
| 2347 | |||
| 2348 | // Check the region attributes. | ||
| 2349 | R_UNLESS(!region->IsDerivedFrom(KMemoryRegionType_Dram), ResultInvalidAddress); | ||
| 2350 | R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_UserReadOnly) || !is_rw, | ||
| 2351 | ResultInvalidAddress); | ||
| 2352 | R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_NoUserMap), ResultInvalidAddress); | ||
| 2353 | |||
| 2354 | // Check if we're done. | ||
| 2355 | if (GetInteger(last) <= region->GetLastAddress()) { | ||
| 2356 | break; | ||
| 2357 | } | ||
| 2358 | |||
| 2359 | // Advance. | ||
| 2360 | region = region->GetNext(); | ||
| 2361 | }; | ||
| 2362 | |||
| 2363 | // Select an address to map at. | ||
| 2364 | KProcessAddress addr = 0; | ||
| 2365 | { | ||
| 2366 | const size_t alignment = 4_KiB; | ||
| 2367 | const KPhysicalAddress aligned_phys = | ||
| 2368 | Common::AlignUp(GetInteger(phys_addr), alignment) + alignment - 1; | ||
| 2369 | R_UNLESS(aligned_phys > phys_addr, ResultInvalidAddress); | ||
| 2370 | |||
| 2371 | const KPhysicalAddress last_aligned_paddr = | ||
| 2372 | Common::AlignDown(GetInteger(last) + 1, alignment) - 1; | ||
| 2373 | R_UNLESS((last_aligned_paddr <= last && aligned_phys <= last_aligned_paddr), | ||
| 2374 | ResultInvalidAddress); | ||
| 2375 | |||
| 2376 | addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, | ||
| 2377 | this->GetNumGuardPages()); | ||
| 2378 | R_UNLESS(addr != 0, ResultOutOfMemory); | ||
| 2379 | } | ||
| 2380 | |||
| 2381 | // Check that we can map IO here. | ||
| 2382 | ASSERT(this->CanContain(addr, size, state)); | ||
| 2383 | R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, | ||
| 2384 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2385 | KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 2386 | |||
| 2387 | // Perform mapping operation. | ||
| 2388 | const KPageProperties properties = {perm, state == KMemoryState::IoRegister, false, | ||
| 2389 | DisableMergeAttribute::DisableHead}; | ||
| 2390 | R_TRY(this->Operate(page_list, addr, num_pages, phys_addr, true, properties, OperationType::Map, | ||
| 2391 | false)); | ||
| 2392 | |||
| 2393 | // Set the output address. | ||
| 2394 | *out = addr; | ||
| 2395 | |||
| 2396 | R_SUCCEED(); | ||
| 2397 | } | ||
| 2398 | |||
| 2399 | Result KPageTableBase::MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { | ||
| 2400 | // Lock the table. | ||
| 2401 | KScopedLightLock lk(m_general_lock); | ||
| 2402 | |||
| 2403 | // Create an update allocator. | ||
| 2404 | Result allocator_result; | ||
| 2405 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2406 | m_memory_block_slab_manager); | ||
| 2407 | R_TRY(allocator_result); | ||
| 2408 | |||
| 2409 | // We're going to perform an update, so create a helper. | ||
| 2410 | KScopedPageTableUpdater updater(this); | ||
| 2411 | |||
| 2412 | // Map the io memory. | ||
| 2413 | KProcessAddress addr; | ||
| 2414 | R_TRY(this->MapIoImpl(std::addressof(addr), updater.GetPageList(), phys_addr, size, | ||
| 2415 | KMemoryState::IoRegister, perm)); | ||
| 2416 | |||
| 2417 | // Update the blocks. | ||
| 2418 | m_memory_block_manager.Update(std::addressof(allocator), addr, size / PageSize, | ||
| 2419 | KMemoryState::IoRegister, perm, KMemoryAttribute::Locked, | ||
| 2420 | KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2421 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2422 | |||
| 2423 | // We successfully mapped the pages. | ||
| 2424 | R_SUCCEED(); | ||
| 2425 | } | ||
| 2426 | |||
| 2427 | Result KPageTableBase::MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, | ||
| 2428 | size_t size, Svc::MemoryMapping mapping, | ||
| 2429 | Svc::MemoryPermission svc_perm) { | ||
| 2430 | const size_t num_pages = size / PageSize; | ||
| 2431 | |||
| 2432 | // Lock the table. | ||
| 2433 | KScopedLightLock lk(m_general_lock); | ||
| 2434 | |||
| 2435 | // Validate the memory state. | ||
| 2436 | size_t num_allocator_blocks; | ||
| 2437 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), dst_address, size, | ||
| 2438 | KMemoryState::All, KMemoryState::None, KMemoryPermission::None, | ||
| 2439 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2440 | KMemoryAttribute::None)); | ||
| 2441 | |||
| 2442 | // Create an update allocator. | ||
| 2443 | Result allocator_result; | ||
| 2444 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2445 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2446 | R_TRY(allocator_result); | ||
| 2447 | |||
| 2448 | // We're going to perform an update, so create a helper. | ||
| 2449 | KScopedPageTableUpdater updater(this); | ||
| 2450 | |||
| 2451 | // Perform mapping operation. | ||
| 2452 | const KMemoryPermission perm = ConvertToKMemoryPermission(svc_perm); | ||
| 2453 | const KPageProperties properties = {perm, mapping == Svc::MemoryMapping::IoRegister, | ||
| 2454 | mapping == Svc::MemoryMapping::Uncached, | ||
| 2455 | DisableMergeAttribute::DisableHead}; | ||
| 2456 | R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, phys_addr, true, properties, | ||
| 2457 | OperationType::Map, false)); | ||
| 2458 | |||
| 2459 | // Update the blocks. | ||
| 2460 | const auto state = | ||
| 2461 | mapping == Svc::MemoryMapping::Memory ? KMemoryState::IoMemory : KMemoryState::IoRegister; | ||
| 2462 | m_memory_block_manager.Update( | ||
| 2463 | std::addressof(allocator), dst_address, num_pages, state, perm, KMemoryAttribute::Locked, | ||
| 2464 | KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None); | ||
| 2465 | |||
| 2466 | // We successfully mapped the pages. | ||
| 2467 | R_SUCCEED(); | ||
| 2468 | } | ||
| 2469 | |||
| 2470 | Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, | ||
| 2471 | size_t size, Svc::MemoryMapping mapping) { | ||
| 2472 | const size_t num_pages = size / PageSize; | ||
| 2473 | |||
| 2474 | // Lock the table. | ||
| 2475 | KScopedLightLock lk(m_general_lock); | ||
| 2476 | |||
| 2477 | // Validate the memory state. | ||
| 2478 | KMemoryState old_state; | ||
| 2479 | KMemoryPermission old_perm; | ||
| 2480 | KMemoryAttribute old_attr; | ||
| 2481 | size_t num_allocator_blocks; | ||
| 2482 | R_TRY(this->CheckMemoryState( | ||
| 2483 | std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr), | ||
| 2484 | std::addressof(num_allocator_blocks), dst_address, size, KMemoryState::All, | ||
| 2485 | mapping == Svc::MemoryMapping::Memory ? KMemoryState::IoMemory : KMemoryState::IoRegister, | ||
| 2486 | KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, | ||
| 2487 | KMemoryAttribute::Locked)); | ||
| 2488 | |||
| 2489 | // Validate that the region being unmapped corresponds to the physical range described. | ||
| 2490 | { | ||
| 2491 | // Get the impl. | ||
| 2492 | auto& impl = this->GetImpl(); | ||
| 2493 | |||
| 2494 | // Begin traversal. | ||
| 2495 | TraversalContext context; | ||
| 2496 | TraversalEntry next_entry; | ||
| 2497 | ASSERT( | ||
| 2498 | impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address)); | ||
| 2499 | |||
| 2500 | // Check that the physical region matches. | ||
| 2501 | R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion); | ||
| 2502 | |||
| 2503 | // Iterate. | ||
| 2504 | for (size_t checked_size = | ||
| 2505 | next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1)); | ||
| 2506 | checked_size < size; checked_size += next_entry.block_size) { | ||
| 2507 | // Continue the traversal. | ||
| 2508 | ASSERT(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))); | ||
| 2509 | |||
| 2510 | // Check that the physical region matches. | ||
| 2511 | R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion); | ||
| 2512 | } | ||
| 2513 | } | ||
| 2514 | |||
| 2515 | // Create an update allocator. | ||
| 2516 | Result allocator_result; | ||
| 2517 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2518 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2519 | R_TRY(allocator_result); | ||
| 2520 | |||
| 2521 | // We're going to perform an update, so create a helper. | ||
| 2522 | KScopedPageTableUpdater updater(this); | ||
| 2523 | |||
| 2524 | // If the region being unmapped is Memory, synchronize. | ||
| 2525 | if (mapping == Svc::MemoryMapping::Memory) { | ||
| 2526 | // Change the region to be uncached. | ||
| 2527 | const KPageProperties properties = {old_perm, false, true, DisableMergeAttribute::None}; | ||
| 2528 | R_ASSERT(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false, properties, | ||
| 2529 | OperationType::ChangePermissionsAndRefresh, false)); | ||
| 2530 | |||
| 2531 | // Temporarily unlock ourselves, so that other operations can occur while we flush the | ||
| 2532 | // region. | ||
| 2533 | m_general_lock.Unlock(); | ||
| 2534 | SCOPE_EXIT({ m_general_lock.Lock(); }); | ||
| 2535 | |||
| 2536 | // Flush the region. | ||
| 2537 | R_ASSERT(FlushDataCache(dst_address, size)); | ||
| 2538 | } | ||
| 2539 | |||
| 2540 | // Perform the unmap. | ||
| 2541 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 2542 | DisableMergeAttribute::None}; | ||
| 2543 | R_ASSERT(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false, | ||
| 2544 | unmap_properties, OperationType::Unmap, false)); | ||
| 2545 | |||
| 2546 | // Update the blocks. | ||
| 2547 | m_memory_block_manager.Update(std::addressof(allocator), dst_address, num_pages, | ||
| 2548 | KMemoryState::Free, KMemoryPermission::None, | ||
| 2549 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | ||
| 2550 | KMemoryBlockDisableMergeAttribute::Normal); | ||
| 2551 | |||
| 2552 | R_SUCCEED(); | ||
| 2553 | } | ||
| 2554 | |||
| 2555 | Result KPageTableBase::MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { | ||
| 2556 | ASSERT(Common::IsAligned(GetInteger(phys_addr), PageSize)); | ||
| 2557 | ASSERT(Common::IsAligned(size, PageSize)); | ||
| 2558 | ASSERT(size > 0); | ||
| 2559 | R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress); | ||
| 2560 | const size_t num_pages = size / PageSize; | ||
| 2561 | const KPhysicalAddress last = phys_addr + size - 1; | ||
| 2562 | |||
| 2563 | // Get region extents. | ||
| 2564 | const KProcessAddress region_start = this->GetRegionAddress(KMemoryState::Static); | ||
| 2565 | const size_t region_size = this->GetRegionSize(KMemoryState::Static); | ||
| 2566 | const size_t region_num_pages = region_size / PageSize; | ||
| 2567 | |||
| 2568 | // Locate the memory region. | ||
| 2569 | const KMemoryRegion* region = KMemoryLayout::Find(m_kernel.MemoryLayout(), phys_addr); | ||
| 2570 | R_UNLESS(region != nullptr, ResultInvalidAddress); | ||
| 2571 | |||
| 2572 | ASSERT(region->Contains(GetInteger(phys_addr))); | ||
| 2573 | R_UNLESS(GetInteger(last) <= region->GetLastAddress(), ResultInvalidAddress); | ||
| 2574 | |||
| 2575 | // Check the region attributes. | ||
| 2576 | const bool is_rw = perm == KMemoryPermission::UserReadWrite; | ||
| 2577 | R_UNLESS(region->IsDerivedFrom(KMemoryRegionType_Dram), ResultInvalidAddress); | ||
| 2578 | R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_NoUserMap), ResultInvalidAddress); | ||
| 2579 | R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_UserReadOnly) || !is_rw, | ||
| 2580 | ResultInvalidAddress); | ||
| 2581 | |||
| 2582 | // Lock the table. | ||
| 2583 | KScopedLightLock lk(m_general_lock); | ||
| 2584 | |||
| 2585 | // Select an address to map at. | ||
| 2586 | KProcessAddress addr = 0; | ||
| 2587 | { | ||
| 2588 | const size_t alignment = 4_KiB; | ||
| 2589 | const KPhysicalAddress aligned_phys = | ||
| 2590 | Common::AlignUp(GetInteger(phys_addr), alignment) + alignment - 1; | ||
| 2591 | R_UNLESS(aligned_phys > phys_addr, ResultInvalidAddress); | ||
| 2592 | |||
| 2593 | const KPhysicalAddress last_aligned_paddr = | ||
| 2594 | Common::AlignDown(GetInteger(last) + 1, alignment) - 1; | ||
| 2595 | R_UNLESS((last_aligned_paddr <= last && aligned_phys <= last_aligned_paddr), | ||
| 2596 | ResultInvalidAddress); | ||
| 2597 | |||
| 2598 | addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, | ||
| 2599 | this->GetNumGuardPages()); | ||
| 2600 | R_UNLESS(addr != 0, ResultOutOfMemory); | ||
| 2601 | } | ||
| 2602 | |||
| 2603 | // Check that we can map static here. | ||
| 2604 | ASSERT(this->CanContain(addr, size, KMemoryState::Static)); | ||
| 2605 | R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, | ||
| 2606 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 2607 | KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 2608 | |||
| 2609 | // Create an update allocator. | ||
| 2610 | Result allocator_result; | ||
| 2611 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2612 | m_memory_block_slab_manager); | ||
| 2613 | R_TRY(allocator_result); | ||
| 2614 | |||
| 2615 | // We're going to perform an update, so create a helper. | ||
| 2616 | KScopedPageTableUpdater updater(this); | ||
| 2617 | |||
| 2618 | // Perform mapping operation. | ||
| 2619 | const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead}; | ||
| 2620 | R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties, | ||
| 2621 | OperationType::Map, false)); | ||
| 2622 | |||
| 2623 | // Update the blocks. | ||
| 2624 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, KMemoryState::Static, | ||
| 2625 | perm, KMemoryAttribute::None, | ||
| 2626 | KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2627 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2628 | |||
| 2629 | // We successfully mapped the pages. | ||
| 2630 | R_SUCCEED(); | ||
| 2631 | } | ||
| 2632 | |||
| 2633 | Result KPageTableBase::MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) { | ||
| 2634 | // Get the memory region. | ||
| 2635 | const KMemoryRegion* region = | ||
| 2636 | m_kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(region_type); | ||
| 2637 | R_UNLESS(region != nullptr, ResultOutOfRange); | ||
| 2638 | |||
| 2639 | // Check that the region is valid. | ||
| 2640 | ASSERT(region->GetEndAddress() != 0); | ||
| 2641 | |||
| 2642 | // Map the region. | ||
| 2643 | R_TRY_CATCH(this->MapStatic(region->GetAddress(), region->GetSize(), perm)){ | ||
| 2644 | R_CONVERT(ResultInvalidAddress, ResultOutOfRange)} R_END_TRY_CATCH; | ||
| 2645 | |||
| 2646 | R_SUCCEED(); | ||
| 2647 | } | ||
| 2648 | |||
| 2649 | Result KPageTableBase::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, | ||
| 2650 | KPhysicalAddress phys_addr, bool is_pa_valid, | ||
| 2651 | KProcessAddress region_start, size_t region_num_pages, | ||
| 2652 | KMemoryState state, KMemoryPermission perm) { | ||
| 2653 | ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); | ||
| 2654 | |||
| 2655 | // Ensure this is a valid map request. | ||
| 2656 | R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), | ||
| 2657 | ResultInvalidCurrentMemory); | ||
| 2658 | R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); | ||
| 2659 | |||
| 2660 | // Lock the table. | ||
| 2661 | KScopedLightLock lk(m_general_lock); | ||
| 2662 | |||
| 2663 | // Find a random address to map at. | ||
| 2664 | KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, | ||
| 2665 | 0, this->GetNumGuardPages()); | ||
| 2666 | R_UNLESS(addr != 0, ResultOutOfMemory); | ||
| 2667 | ASSERT(Common::IsAligned(GetInteger(addr), alignment)); | ||
| 2668 | ASSERT(this->CanContain(addr, num_pages * PageSize, state)); | ||
| 2669 | R_ASSERT(this->CheckMemoryState( | ||
| 2670 | addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, | ||
| 2671 | KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 2672 | |||
| 2673 | // Create an update allocator. | ||
| 2674 | Result allocator_result; | ||
| 2675 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2676 | m_memory_block_slab_manager); | ||
| 2677 | R_TRY(allocator_result); | ||
| 2678 | |||
| 2679 | // We're going to perform an update, so create a helper. | ||
| 2680 | KScopedPageTableUpdater updater(this); | ||
| 2681 | |||
| 2682 | // Perform mapping operation. | ||
| 2683 | if (is_pa_valid) { | ||
| 2684 | const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead}; | ||
| 2685 | R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties, | ||
| 2686 | OperationType::Map, false)); | ||
| 2687 | } else { | ||
| 2688 | R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm)); | ||
| 2689 | } | ||
| 2690 | |||
| 2691 | // Update the blocks. | ||
| 2692 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, | ||
| 2693 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2694 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2695 | |||
| 2696 | // We successfully mapped the pages. | ||
| 2697 | *out_addr = addr; | ||
| 2698 | R_SUCCEED(); | ||
| 2699 | } | ||
| 2700 | |||
| 2701 | Result KPageTableBase::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, | ||
| 2702 | KMemoryPermission perm) { | ||
| 2703 | // Check that the map is in range. | ||
| 2704 | const size_t size = num_pages * PageSize; | ||
| 2705 | R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); | ||
| 2706 | |||
| 2707 | // Lock the table. | ||
| 2708 | KScopedLightLock lk(m_general_lock); | ||
| 2709 | |||
| 2710 | // Check the memory state. | ||
| 2711 | size_t num_allocator_blocks; | ||
| 2712 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | ||
| 2713 | KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, | ||
| 2714 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2715 | KMemoryAttribute::None)); | ||
| 2716 | |||
| 2717 | // Create an update allocator. | ||
| 2718 | Result allocator_result; | ||
| 2719 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2720 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2721 | R_TRY(allocator_result); | ||
| 2722 | |||
| 2723 | // We're going to perform an update, so create a helper. | ||
| 2724 | KScopedPageTableUpdater updater(this); | ||
| 2725 | |||
| 2726 | // Map the pages. | ||
| 2727 | R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm)); | ||
| 2728 | |||
| 2729 | // Update the blocks. | ||
| 2730 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, | ||
| 2731 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2732 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2733 | |||
| 2734 | R_SUCCEED(); | ||
| 2735 | } | ||
| 2736 | |||
| 2737 | Result KPageTableBase::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) { | ||
| 2738 | // Check that the unmap is in range. | ||
| 2739 | const size_t size = num_pages * PageSize; | ||
| 2740 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 2741 | |||
| 2742 | // Lock the table. | ||
| 2743 | KScopedLightLock lk(m_general_lock); | ||
| 2744 | |||
| 2745 | // Check the memory state. | ||
| 2746 | size_t num_allocator_blocks; | ||
| 2747 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | ||
| 2748 | KMemoryState::All, state, KMemoryPermission::None, | ||
| 2749 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 2750 | KMemoryAttribute::None)); | ||
| 2751 | |||
| 2752 | // Create an update allocator. | ||
| 2753 | Result allocator_result; | ||
| 2754 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2755 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2756 | R_TRY(allocator_result); | ||
| 2757 | |||
| 2758 | // We're going to perform an update, so create a helper. | ||
| 2759 | KScopedPageTableUpdater updater(this); | ||
| 2760 | |||
| 2761 | // Perform the unmap. | ||
| 2762 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 2763 | DisableMergeAttribute::None}; | ||
| 2764 | R_TRY(this->Operate(updater.GetPageList(), address, num_pages, 0, false, unmap_properties, | ||
| 2765 | OperationType::Unmap, false)); | ||
| 2766 | |||
| 2767 | // Update the blocks. | ||
| 2768 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, | ||
| 2769 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2770 | KMemoryBlockDisableMergeAttribute::None, | ||
| 2771 | KMemoryBlockDisableMergeAttribute::Normal); | ||
| 2772 | |||
| 2773 | R_SUCCEED(); | ||
| 2774 | } | ||
| 2775 | |||
| 2776 | Result KPageTableBase::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg, | ||
| 2777 | KProcessAddress region_start, size_t region_num_pages, | ||
| 2778 | KMemoryState state, KMemoryPermission perm) { | ||
| 2779 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 2780 | |||
| 2781 | // Ensure this is a valid map request. | ||
| 2782 | const size_t num_pages = pg.GetNumPages(); | ||
| 2783 | R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), | ||
| 2784 | ResultInvalidCurrentMemory); | ||
| 2785 | R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); | ||
| 2786 | |||
| 2787 | // Lock the table. | ||
| 2788 | KScopedLightLock lk(m_general_lock); | ||
| 2789 | |||
| 2790 | // Find a random address to map at. | ||
| 2791 | KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize, | ||
| 2792 | 0, this->GetNumGuardPages()); | ||
| 2793 | R_UNLESS(addr != 0, ResultOutOfMemory); | ||
| 2794 | ASSERT(this->CanContain(addr, num_pages * PageSize, state)); | ||
| 2795 | R_ASSERT(this->CheckMemoryState( | ||
| 2796 | addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, | ||
| 2797 | KMemoryPermission::None, KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 2798 | |||
| 2799 | // Create an update allocator. | ||
| 2800 | Result allocator_result; | ||
| 2801 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2802 | m_memory_block_slab_manager); | ||
| 2803 | R_TRY(allocator_result); | ||
| 2804 | |||
| 2805 | // We're going to perform an update, so create a helper. | ||
| 2806 | KScopedPageTableUpdater updater(this); | ||
| 2807 | |||
| 2808 | // Perform mapping operation. | ||
| 2809 | const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead}; | ||
| 2810 | R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); | ||
| 2811 | |||
| 2812 | // Update the blocks. | ||
| 2813 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, | ||
| 2814 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2815 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2816 | |||
| 2817 | // We successfully mapped the pages. | ||
| 2818 | *out_addr = addr; | ||
| 2819 | R_SUCCEED(); | ||
| 2820 | } | ||
| 2821 | |||
| 2822 | Result KPageTableBase::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state, | ||
| 2823 | KMemoryPermission perm) { | ||
| 2824 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 2825 | |||
| 2826 | // Ensure this is a valid map request. | ||
| 2827 | const size_t num_pages = pg.GetNumPages(); | ||
| 2828 | const size_t size = num_pages * PageSize; | ||
| 2829 | R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); | ||
| 2830 | |||
| 2831 | // Lock the table. | ||
| 2832 | KScopedLightLock lk(m_general_lock); | ||
| 2833 | |||
| 2834 | // Check if state allows us to map. | ||
| 2835 | size_t num_allocator_blocks; | ||
| 2836 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size, | ||
| 2837 | KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, | ||
| 2838 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2839 | KMemoryAttribute::None)); | ||
| 2840 | |||
| 2841 | // Create an update allocator. | ||
| 2842 | Result allocator_result; | ||
| 2843 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2844 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2845 | R_TRY(allocator_result); | ||
| 2846 | |||
| 2847 | // We're going to perform an update, so create a helper. | ||
| 2848 | KScopedPageTableUpdater updater(this); | ||
| 2849 | |||
| 2850 | // Perform mapping operation. | ||
| 2851 | const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead}; | ||
| 2852 | R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); | ||
| 2853 | |||
| 2854 | // Update the blocks. | ||
| 2855 | m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, | ||
| 2856 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, | ||
| 2857 | KMemoryBlockDisableMergeAttribute::None); | ||
| 2858 | |||
| 2859 | // We successfully mapped the pages. | ||
| 2860 | R_SUCCEED(); | ||
| 2861 | } | ||
| 2862 | |||
| 2863 | Result KPageTableBase::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, | ||
| 2864 | KMemoryState state) { | ||
| 2865 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 2866 | |||
| 2867 | // Ensure this is a valid unmap request. | ||
| 2868 | const size_t num_pages = pg.GetNumPages(); | ||
| 2869 | const size_t size = num_pages * PageSize; | ||
| 2870 | R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); | ||
| 2871 | |||
| 2872 | // Lock the table. | ||
| 2873 | KScopedLightLock lk(m_general_lock); | ||
| 2874 | |||
| 2875 | // Check if state allows us to unmap. | ||
| 2876 | size_t num_allocator_blocks; | ||
| 2877 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | ||
| 2878 | KMemoryState::All, state, KMemoryPermission::None, | ||
| 2879 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 2880 | KMemoryAttribute::None)); | ||
| 2881 | |||
| 2882 | // Check that the page group is valid. | ||
| 2883 | R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory); | ||
| 2884 | |||
| 2885 | // Create an update allocator. | ||
| 2886 | Result allocator_result; | ||
| 2887 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 2888 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 2889 | R_TRY(allocator_result); | ||
| 2890 | |||
| 2891 | // We're going to perform an update, so create a helper. | ||
| 2892 | KScopedPageTableUpdater updater(this); | ||
| 2893 | |||
| 2894 | // Perform unmapping operation. | ||
| 2895 | const KPageProperties properties = {KMemoryPermission::None, false, false, | ||
| 2896 | DisableMergeAttribute::None}; | ||
| 2897 | R_TRY(this->Operate(updater.GetPageList(), address, num_pages, 0, false, properties, | ||
| 2898 | OperationType::Unmap, false)); | ||
| 2899 | |||
| 2900 | // Update the blocks. | ||
| 2901 | m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, | ||
| 2902 | KMemoryPermission::None, KMemoryAttribute::None, | ||
| 2903 | KMemoryBlockDisableMergeAttribute::None, | ||
| 2904 | KMemoryBlockDisableMergeAttribute::Normal); | ||
| 2905 | |||
| 2906 | R_SUCCEED(); | ||
| 2907 | } | ||
| 2908 | |||
| 2909 | Result KPageTableBase::MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, | ||
| 2910 | size_t num_pages, KMemoryState state_mask, | ||
| 2911 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 2912 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 2913 | KMemoryAttribute attr) { | ||
| 2914 | // Ensure that the page group isn't null. | ||
| 2915 | ASSERT(out != nullptr); | ||
| 2916 | |||
| 2917 | // Make sure that the region we're mapping is valid for the table. | ||
| 2918 | const size_t size = num_pages * PageSize; | ||
| 2919 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 2920 | |||
| 2921 | // Lock the table. | ||
| 2922 | KScopedLightLock lk(m_general_lock); | ||
| 2923 | |||
| 2924 | // Check if state allows us to create the group. | ||
| 2925 | R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted, | ||
| 2926 | state | KMemoryState::FlagReferenceCounted, perm_mask, perm, | ||
| 2927 | attr_mask, attr)); | ||
| 2928 | |||
| 2929 | // Create a new page group for the region. | ||
| 2930 | R_TRY(this->MakePageGroup(*out, address, num_pages)); | ||
| 2931 | |||
| 2932 | // Open a new reference to the pages in the group. | ||
| 2933 | out->Open(); | ||
| 2934 | |||
| 2935 | R_SUCCEED(); | ||
| 2936 | } | ||
| 2937 | |||
| 2938 | Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_t size) { | ||
| 2939 | // Check that the region is in range. | ||
| 2940 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 2941 | |||
| 2942 | // Lock the table. | ||
| 2943 | KScopedLightLock lk(m_general_lock); | ||
| 2944 | |||
| 2945 | // Check the memory state. | ||
| 2946 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 2947 | address, size, KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted, | ||
| 2948 | KMemoryPermission::UserReadWrite, KMemoryPermission::UserReadWrite, | ||
| 2949 | KMemoryAttribute::Uncached, KMemoryAttribute::None)); | ||
| 2950 | |||
| 2951 | // Get the impl. | ||
| 2952 | auto& impl = this->GetImpl(); | ||
| 2953 | |||
| 2954 | // Begin traversal. | ||
| 2955 | TraversalContext context; | ||
| 2956 | TraversalEntry next_entry; | ||
| 2957 | bool traverse_valid = | ||
| 2958 | impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), address); | ||
| 2959 | R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); | ||
| 2960 | |||
| 2961 | // Prepare tracking variables. | ||
| 2962 | KPhysicalAddress cur_addr = next_entry.phys_addr; | ||
| 2963 | size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1)); | ||
| 2964 | size_t tot_size = cur_size; | ||
| 2965 | |||
| 2966 | // Iterate. | ||
| 2967 | while (tot_size < size) { | ||
| 2968 | // Continue the traversal. | ||
| 2969 | traverse_valid = | ||
| 2970 | impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 2971 | R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); | ||
| 2972 | |||
| 2973 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 2974 | // Check that the pages are linearly mapped. | ||
| 2975 | R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); | ||
| 2976 | |||
| 2977 | // Invalidate the block. | ||
| 2978 | if (cur_size > 0) { | ||
| 2979 | // NOTE: Nintendo does not check the result of invalidation. | ||
| 2980 | InvalidateDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size); | ||
| 2981 | } | ||
| 2982 | |||
| 2983 | // Advance. | ||
| 2984 | cur_addr = next_entry.phys_addr; | ||
| 2985 | cur_size = next_entry.block_size; | ||
| 2986 | } else { | ||
| 2987 | cur_size += next_entry.block_size; | ||
| 2988 | } | ||
| 2989 | |||
| 2990 | tot_size += next_entry.block_size; | ||
| 2991 | } | ||
| 2992 | |||
| 2993 | // Ensure we use the right size for the last block. | ||
| 2994 | if (tot_size > size) { | ||
| 2995 | cur_size -= (tot_size - size); | ||
| 2996 | } | ||
| 2997 | |||
| 2998 | // Check that the last block is linearly mapped. | ||
| 2999 | R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); | ||
| 3000 | |||
| 3001 | // Invalidate the last block. | ||
| 3002 | if (cur_size > 0) { | ||
| 3003 | // NOTE: Nintendo does not check the result of invalidation. | ||
| 3004 | InvalidateDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size); | ||
| 3005 | } | ||
| 3006 | |||
| 3007 | R_SUCCEED(); | ||
| 3008 | } | ||
| 3009 | |||
| 3010 | Result KPageTableBase::InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size) { | ||
| 3011 | // Check pre-condition: this is being called on the current process. | ||
| 3012 | ASSERT(this == std::addressof(GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable())); | ||
| 3013 | |||
| 3014 | // Check that the region is in range. | ||
| 3015 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 3016 | |||
| 3017 | // Lock the table. | ||
| 3018 | KScopedLightLock lk(m_general_lock); | ||
| 3019 | |||
| 3020 | // Check the memory state. | ||
| 3021 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 3022 | address, size, KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted, | ||
| 3023 | KMemoryPermission::UserReadWrite, KMemoryPermission::UserReadWrite, | ||
| 3024 | KMemoryAttribute::Uncached, KMemoryAttribute::None)); | ||
| 3025 | |||
| 3026 | // Invalidate the data cache. | ||
| 3027 | R_RETURN(InvalidateDataCache(address, size)); | ||
| 3028 | } | ||
| 3029 | |||
| 3030 | Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 3031 | size_t size) { | ||
| 3032 | // Lightly validate the region is in range. | ||
| 3033 | R_UNLESS(this->Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 3034 | |||
| 3035 | // Lock the table. | ||
| 3036 | KScopedLightLock lk(m_general_lock); | ||
| 3037 | |||
| 3038 | // Require that the memory either be user readable or debuggable. | ||
| 3039 | const bool can_read = R_SUCCEEDED(this->CheckMemoryStateContiguous( | ||
| 3040 | src_address, size, KMemoryState::None, KMemoryState::None, KMemoryPermission::UserRead, | ||
| 3041 | KMemoryPermission::UserRead, KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 3042 | if (!can_read) { | ||
| 3043 | const bool can_debug = R_SUCCEEDED(this->CheckMemoryStateContiguous( | ||
| 3044 | src_address, size, KMemoryState::FlagCanDebug, KMemoryState::FlagCanDebug, | ||
| 3045 | KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, | ||
| 3046 | KMemoryAttribute::None)); | ||
| 3047 | R_UNLESS(can_debug, ResultInvalidCurrentMemory); | ||
| 3048 | } | ||
| 3049 | |||
| 3050 | // Get the impl. | ||
| 3051 | auto& impl = this->GetImpl(); | ||
| 3052 | auto& dst_memory = GetCurrentMemory(m_system.Kernel()); | ||
| 3053 | |||
| 3054 | // Begin traversal. | ||
| 3055 | TraversalContext context; | ||
| 3056 | TraversalEntry next_entry; | ||
| 3057 | bool traverse_valid = | ||
| 3058 | impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_address); | ||
| 3059 | R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); | ||
| 3060 | |||
| 3061 | // Prepare tracking variables. | ||
| 3062 | KPhysicalAddress cur_addr = next_entry.phys_addr; | ||
| 3063 | size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1)); | ||
| 3064 | size_t tot_size = cur_size; | ||
| 3065 | |||
| 3066 | auto PerformCopy = [&]() -> Result { | ||
| 3067 | // Ensure the address is linear mapped. | ||
| 3068 | R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); | ||
| 3069 | |||
| 3070 | // Copy as much aligned data as we can. | ||
| 3071 | if (cur_size >= sizeof(u32)) { | ||
| 3072 | const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32)); | ||
| 3073 | const void* copy_src = GetLinearMappedVirtualPointer(m_kernel, cur_addr); | ||
| 3074 | FlushDataCache(copy_src, copy_size); | ||
| 3075 | R_UNLESS(dst_memory.WriteBlock(dst_address, copy_src, copy_size), ResultInvalidPointer); | ||
| 3076 | |||
| 3077 | dst_address += copy_size; | ||
| 3078 | cur_addr += copy_size; | ||
| 3079 | cur_size -= copy_size; | ||
| 3080 | } | ||
| 3081 | |||
| 3082 | // Copy remaining data. | ||
| 3083 | if (cur_size > 0) { | ||
| 3084 | const void* copy_src = GetLinearMappedVirtualPointer(m_kernel, cur_addr); | ||
| 3085 | FlushDataCache(copy_src, cur_size); | ||
| 3086 | R_UNLESS(dst_memory.WriteBlock(dst_address, copy_src, cur_size), ResultInvalidPointer); | ||
| 3087 | } | ||
| 3088 | |||
| 3089 | R_SUCCEED(); | ||
| 3090 | }; | ||
| 3091 | |||
| 3092 | // Iterate. | ||
| 3093 | while (tot_size < size) { | ||
| 3094 | // Continue the traversal. | ||
| 3095 | traverse_valid = | ||
| 3096 | impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 3097 | ASSERT(traverse_valid); | ||
| 3098 | |||
| 3099 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 3100 | // Perform copy. | ||
| 3101 | R_TRY(PerformCopy()); | ||
| 3102 | |||
| 3103 | // Advance. | ||
| 3104 | dst_address += cur_size; | ||
| 3105 | |||
| 3106 | cur_addr = next_entry.phys_addr; | ||
| 3107 | cur_size = next_entry.block_size; | ||
| 3108 | } else { | ||
| 3109 | cur_size += next_entry.block_size; | ||
| 3110 | } | ||
| 3111 | |||
| 3112 | tot_size += next_entry.block_size; | ||
| 3113 | } | ||
| 3114 | |||
| 3115 | // Ensure we use the right size for the last block. | ||
| 3116 | if (tot_size > size) { | ||
| 3117 | cur_size -= (tot_size - size); | ||
| 3118 | } | ||
| 3119 | |||
| 3120 | // Perform copy for the last block. | ||
| 3121 | R_TRY(PerformCopy()); | ||
| 3122 | |||
| 3123 | R_SUCCEED(); | ||
| 3124 | } | ||
| 3125 | |||
| 3126 | Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 3127 | size_t size) { | ||
| 3128 | // Lightly validate the region is in range. | ||
| 3129 | R_UNLESS(this->Contains(dst_address, size), ResultInvalidCurrentMemory); | ||
| 3130 | |||
| 3131 | // Lock the table. | ||
| 3132 | KScopedLightLock lk(m_general_lock); | ||
| 3133 | |||
| 3134 | // Require that the memory either be user writable or debuggable. | ||
| 3135 | const bool can_read = R_SUCCEEDED(this->CheckMemoryStateContiguous( | ||
| 3136 | dst_address, size, KMemoryState::None, KMemoryState::None, KMemoryPermission::UserReadWrite, | ||
| 3137 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 3138 | if (!can_read) { | ||
| 3139 | const bool can_debug = R_SUCCEEDED(this->CheckMemoryStateContiguous( | ||
| 3140 | dst_address, size, KMemoryState::FlagCanDebug, KMemoryState::FlagCanDebug, | ||
| 3141 | KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::None, | ||
| 3142 | KMemoryAttribute::None)); | ||
| 3143 | R_UNLESS(can_debug, ResultInvalidCurrentMemory); | ||
| 3144 | } | ||
| 3145 | |||
| 3146 | // Get the impl. | ||
| 3147 | auto& impl = this->GetImpl(); | ||
| 3148 | auto& src_memory = GetCurrentMemory(m_system.Kernel()); | ||
| 3149 | |||
| 3150 | // Begin traversal. | ||
| 3151 | TraversalContext context; | ||
| 3152 | TraversalEntry next_entry; | ||
| 3153 | bool traverse_valid = | ||
| 3154 | impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address); | ||
| 3155 | R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); | ||
| 3156 | |||
| 3157 | // Prepare tracking variables. | ||
| 3158 | KPhysicalAddress cur_addr = next_entry.phys_addr; | ||
| 3159 | size_t cur_size = next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1)); | ||
| 3160 | size_t tot_size = cur_size; | ||
| 3161 | |||
| 3162 | auto PerformCopy = [&]() -> Result { | ||
| 3163 | // Ensure the address is linear mapped. | ||
| 3164 | R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); | ||
| 3165 | |||
| 3166 | // Copy as much aligned data as we can. | ||
| 3167 | if (cur_size >= sizeof(u32)) { | ||
| 3168 | const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32)); | ||
| 3169 | void* copy_dst = GetLinearMappedVirtualPointer(m_kernel, cur_addr); | ||
| 3170 | R_UNLESS(src_memory.ReadBlock(src_address, copy_dst, copy_size), | ||
| 3171 | ResultInvalidCurrentMemory); | ||
| 3172 | |||
| 3173 | StoreDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), copy_size); | ||
| 3174 | |||
| 3175 | src_address += copy_size; | ||
| 3176 | cur_addr += copy_size; | ||
| 3177 | cur_size -= copy_size; | ||
| 3178 | } | ||
| 3179 | |||
| 3180 | // Copy remaining data. | ||
| 3181 | if (cur_size > 0) { | ||
| 3182 | void* copy_dst = GetLinearMappedVirtualPointer(m_kernel, cur_addr); | ||
| 3183 | R_UNLESS(src_memory.ReadBlock(src_address, copy_dst, cur_size), | ||
| 3184 | ResultInvalidCurrentMemory); | ||
| 3185 | |||
| 3186 | StoreDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size); | ||
| 3187 | } | ||
| 3188 | |||
| 3189 | R_SUCCEED(); | ||
| 3190 | }; | ||
| 3191 | |||
| 3192 | // Iterate. | ||
| 3193 | while (tot_size < size) { | ||
| 3194 | // Continue the traversal. | ||
| 3195 | traverse_valid = | ||
| 3196 | impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 3197 | ASSERT(traverse_valid); | ||
| 3198 | |||
| 3199 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 3200 | // Perform copy. | ||
| 3201 | R_TRY(PerformCopy()); | ||
| 3202 | |||
| 3203 | // Advance. | ||
| 3204 | src_address += cur_size; | ||
| 3205 | |||
| 3206 | cur_addr = next_entry.phys_addr; | ||
| 3207 | cur_size = next_entry.block_size; | ||
| 3208 | } else { | ||
| 3209 | cur_size += next_entry.block_size; | ||
| 3210 | } | ||
| 3211 | |||
| 3212 | tot_size += next_entry.block_size; | ||
| 3213 | } | ||
| 3214 | |||
| 3215 | // Ensure we use the right size for the last block. | ||
| 3216 | if (tot_size > size) { | ||
| 3217 | cur_size -= (tot_size - size); | ||
| 3218 | } | ||
| 3219 | |||
| 3220 | // Perform copy for the last block. | ||
| 3221 | R_TRY(PerformCopy()); | ||
| 3222 | |||
| 3223 | // Invalidate the instruction cache, as this svc allows modifying executable pages. | ||
| 3224 | InvalidateInstructionCache(m_system, dst_address, size); | ||
| 3225 | |||
| 3226 | R_SUCCEED(); | ||
| 3227 | } | ||
| 3228 | |||
| 3229 | Result KPageTableBase::ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddress phys_addr, | ||
| 3230 | size_t size, KMemoryState state) { | ||
| 3231 | // Check pre-conditions. | ||
| 3232 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 3233 | |||
| 3234 | // Determine the mapping extents. | ||
| 3235 | const KPhysicalAddress map_start = Common::AlignDown(GetInteger(phys_addr), PageSize); | ||
| 3236 | const KPhysicalAddress map_end = Common::AlignUp(GetInteger(phys_addr) + size, PageSize); | ||
| 3237 | const size_t map_size = map_end - map_start; | ||
| 3238 | |||
| 3239 | // Get the memory reference to write into. | ||
| 3240 | auto& dst_memory = GetCurrentMemory(m_kernel); | ||
| 3241 | |||
| 3242 | // We're going to perform an update, so create a helper. | ||
| 3243 | KScopedPageTableUpdater updater(this); | ||
| 3244 | |||
| 3245 | // Temporarily map the io memory. | ||
| 3246 | KProcessAddress io_addr; | ||
| 3247 | R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size, | ||
| 3248 | state, KMemoryPermission::UserRead)); | ||
| 3249 | |||
| 3250 | // Ensure we unmap the io memory when we're done with it. | ||
| 3251 | const KPageProperties unmap_properties = | ||
| 3252 | KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None}; | ||
| 3253 | SCOPE_EXIT({ | ||
| 3254 | R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false, | ||
| 3255 | unmap_properties, OperationType::Unmap, true)); | ||
| 3256 | }); | ||
| 3257 | |||
| 3258 | // Read the memory. | ||
| 3259 | const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); | ||
| 3260 | dst_memory.CopyBlock(dst_addr, read_addr, size); | ||
| 3261 | |||
| 3262 | R_SUCCEED(); | ||
| 3263 | } | ||
| 3264 | |||
| 3265 | Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAddress src_addr, | ||
| 3266 | size_t size, KMemoryState state) { | ||
| 3267 | // Check pre-conditions. | ||
| 3268 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 3269 | |||
| 3270 | // Determine the mapping extents. | ||
| 3271 | const KPhysicalAddress map_start = Common::AlignDown(GetInteger(phys_addr), PageSize); | ||
| 3272 | const KPhysicalAddress map_end = Common::AlignUp(GetInteger(phys_addr) + size, PageSize); | ||
| 3273 | const size_t map_size = map_end - map_start; | ||
| 3274 | |||
| 3275 | // Get the memory reference to read from. | ||
| 3276 | auto& src_memory = GetCurrentMemory(m_kernel); | ||
| 3277 | |||
| 3278 | // We're going to perform an update, so create a helper. | ||
| 3279 | KScopedPageTableUpdater updater(this); | ||
| 3280 | |||
| 3281 | // Temporarily map the io memory. | ||
| 3282 | KProcessAddress io_addr; | ||
| 3283 | R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size, | ||
| 3284 | state, KMemoryPermission::UserReadWrite)); | ||
| 3285 | |||
| 3286 | // Ensure we unmap the io memory when we're done with it. | ||
| 3287 | const KPageProperties unmap_properties = | ||
| 3288 | KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None}; | ||
| 3289 | SCOPE_EXIT({ | ||
| 3290 | R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false, | ||
| 3291 | unmap_properties, OperationType::Unmap, true)); | ||
| 3292 | }); | ||
| 3293 | |||
| 3294 | // Write the memory. | ||
| 3295 | const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); | ||
| 3296 | R_UNLESS(src_memory.CopyBlock(write_addr, src_addr, size), ResultInvalidPointer); | ||
| 3297 | |||
| 3298 | R_SUCCEED(); | ||
| 3299 | } | ||
| 3300 | |||
| 3301 | Result KPageTableBase::ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 3302 | size_t size, KMemoryState state) { | ||
| 3303 | // Lightly validate the range before doing anything else. | ||
| 3304 | R_UNLESS(this->Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 3305 | |||
| 3306 | // We need to lock both this table, and the current process's table, so set up some aliases. | ||
| 3307 | KPageTableBase& src_page_table = *this; | ||
| 3308 | KPageTableBase& dst_page_table = GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable(); | ||
| 3309 | |||
| 3310 | // Acquire the table locks. | ||
| 3311 | KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); | ||
| 3312 | |||
| 3313 | // Check that the desired range is readable io memory. | ||
| 3314 | R_TRY(this->CheckMemoryStateContiguous(src_address, size, KMemoryState::All, state, | ||
| 3315 | KMemoryPermission::UserRead, KMemoryPermission::UserRead, | ||
| 3316 | KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 3317 | |||
| 3318 | // Read the memory. | ||
| 3319 | KProcessAddress dst = dst_address; | ||
| 3320 | const KProcessAddress last_address = src_address + size - 1; | ||
| 3321 | while (src_address <= last_address) { | ||
| 3322 | // Get the current physical address. | ||
| 3323 | KPhysicalAddress phys_addr; | ||
| 3324 | ASSERT(src_page_table.GetPhysicalAddressLocked(std::addressof(phys_addr), src_address)); | ||
| 3325 | |||
| 3326 | // Determine the current read size. | ||
| 3327 | const size_t cur_size = | ||
| 3328 | std::min<size_t>(last_address - src_address + 1, | ||
| 3329 | Common::AlignDown(GetInteger(src_address) + PageSize, PageSize) - | ||
| 3330 | GetInteger(src_address)); | ||
| 3331 | |||
| 3332 | // Read. | ||
| 3333 | R_TRY(dst_page_table.ReadIoMemoryImpl(dst, phys_addr, cur_size, state)); | ||
| 3334 | |||
| 3335 | // Advance. | ||
| 3336 | src_address += cur_size; | ||
| 3337 | dst += cur_size; | ||
| 3338 | } | ||
| 3339 | |||
| 3340 | R_SUCCEED(); | ||
| 3341 | } | ||
| 3342 | |||
| 3343 | Result KPageTableBase::WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, | ||
| 3344 | size_t size, KMemoryState state) { | ||
| 3345 | // Lightly validate the range before doing anything else. | ||
| 3346 | R_UNLESS(this->Contains(dst_address, size), ResultInvalidCurrentMemory); | ||
| 3347 | |||
| 3348 | // We need to lock both this table, and the current process's table, so set up some aliases. | ||
| 3349 | KPageTableBase& src_page_table = *this; | ||
| 3350 | KPageTableBase& dst_page_table = GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable(); | ||
| 3351 | |||
| 3352 | // Acquire the table locks. | ||
| 3353 | KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); | ||
| 3354 | |||
| 3355 | // Check that the desired range is writable io memory. | ||
| 3356 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 3357 | dst_address, size, KMemoryState::All, state, KMemoryPermission::UserReadWrite, | ||
| 3358 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, KMemoryAttribute::None)); | ||
| 3359 | |||
| 3360 | // Read the memory. | ||
| 3361 | KProcessAddress src = src_address; | ||
| 3362 | const KProcessAddress last_address = dst_address + size - 1; | ||
| 3363 | while (dst_address <= last_address) { | ||
| 3364 | // Get the current physical address. | ||
| 3365 | KPhysicalAddress phys_addr; | ||
| 3366 | ASSERT(src_page_table.GetPhysicalAddressLocked(std::addressof(phys_addr), dst_address)); | ||
| 3367 | |||
| 3368 | // Determine the current read size. | ||
| 3369 | const size_t cur_size = | ||
| 3370 | std::min<size_t>(last_address - dst_address + 1, | ||
| 3371 | Common::AlignDown(GetInteger(dst_address) + PageSize, PageSize) - | ||
| 3372 | GetInteger(dst_address)); | ||
| 3373 | |||
| 3374 | // Read. | ||
| 3375 | R_TRY(dst_page_table.WriteIoMemoryImpl(phys_addr, src, cur_size, state)); | ||
| 3376 | |||
| 3377 | // Advance. | ||
| 3378 | dst_address += cur_size; | ||
| 3379 | src += cur_size; | ||
| 3380 | } | ||
| 3381 | |||
| 3382 | R_SUCCEED(); | ||
| 3383 | } | ||
| 3384 | |||
| 3385 | Result KPageTableBase::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, | ||
| 3386 | size_t size, KMemoryPermission perm, | ||
| 3387 | bool is_aligned, bool check_heap) { | ||
| 3388 | // Lightly validate the range before doing anything else. | ||
| 3389 | const size_t num_pages = size / PageSize; | ||
| 3390 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 3391 | |||
| 3392 | // Lock the table. | ||
| 3393 | KScopedLightLock lk(m_general_lock); | ||
| 3394 | |||
| 3395 | // Check the memory state. | ||
| 3396 | const KMemoryState test_state = | ||
| 3397 | (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap) | | ||
| 3398 | (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None); | ||
| 3399 | size_t num_allocator_blocks; | ||
| 3400 | KMemoryState old_state; | ||
| 3401 | R_TRY(this->CheckMemoryState(std::addressof(old_state), nullptr, nullptr, | ||
| 3402 | std::addressof(num_allocator_blocks), address, size, test_state, | ||
| 3403 | test_state, perm, perm, | ||
| 3404 | KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked, | ||
| 3405 | KMemoryAttribute::None, KMemoryAttribute::DeviceShared)); | ||
| 3406 | |||
| 3407 | // Create an update allocator. | ||
| 3408 | Result allocator_result; | ||
| 3409 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 3410 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 3411 | R_TRY(allocator_result); | ||
| 3412 | |||
| 3413 | // Update the memory blocks. | ||
| 3414 | m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, | ||
| 3415 | &KMemoryBlock::ShareToDevice, KMemoryPermission::None); | ||
| 3416 | |||
| 3417 | // Set whether the locked memory was io. | ||
| 3418 | *out_is_io = | ||
| 3419 | static_cast<Svc::MemoryState>(old_state & KMemoryState::Mask) == Svc::MemoryState::Io; | ||
| 3420 | |||
| 3421 | R_SUCCEED(); | ||
| 3422 | } | ||
| 3423 | |||
| 3424 | Result KPageTableBase::LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, | ||
| 3425 | bool check_heap) { | ||
| 3426 | // Lightly validate the range before doing anything else. | ||
| 3427 | const size_t num_pages = size / PageSize; | ||
| 3428 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 3429 | |||
| 3430 | // Lock the table. | ||
| 3431 | KScopedLightLock lk(m_general_lock); | ||
| 3432 | |||
| 3433 | // Check the memory state. | ||
| 3434 | const KMemoryState test_state = | ||
| 3435 | KMemoryState::FlagCanDeviceMap | | ||
| 3436 | (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None); | ||
| 3437 | size_t num_allocator_blocks; | ||
| 3438 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 3439 | std::addressof(num_allocator_blocks), address, size, test_state, test_state, | ||
| 3440 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 3441 | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); | ||
| 3442 | |||
| 3443 | // Create an update allocator. | ||
| 3444 | Result allocator_result; | ||
| 3445 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 3446 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 3447 | R_TRY(allocator_result); | ||
| 3448 | |||
| 3449 | // Update the memory blocks. | ||
| 3450 | const KMemoryBlockManager::MemoryBlockLockFunction lock_func = | ||
| 3451 | m_enable_device_address_space_merge | ||
| 3452 | ? &KMemoryBlock::UpdateDeviceDisableMergeStateForShare | ||
| 3453 | : &KMemoryBlock::UpdateDeviceDisableMergeStateForShareRight; | ||
| 3454 | m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, lock_func, | ||
| 3455 | KMemoryPermission::None); | ||
| 3456 | |||
| 3457 | R_SUCCEED(); | ||
| 3458 | } | ||
| 3459 | |||
| 3460 | Result KPageTableBase::UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) { | ||
| 3461 | // Lightly validate the range before doing anything else. | ||
| 3462 | const size_t num_pages = size / PageSize; | ||
| 3463 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 3464 | |||
| 3465 | // Lock the table. | ||
| 3466 | KScopedLightLock lk(m_general_lock); | ||
| 3467 | |||
| 3468 | // Check the memory state. | ||
| 3469 | size_t num_allocator_blocks; | ||
| 3470 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 3471 | std::addressof(num_allocator_blocks), address, size, KMemoryState::FlagCanDeviceMap, | ||
| 3472 | KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None, | ||
| 3473 | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); | ||
| 3474 | |||
| 3475 | // Create an update allocator. | ||
| 3476 | Result allocator_result; | ||
| 3477 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 3478 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 3479 | R_TRY(allocator_result); | ||
| 3480 | |||
| 3481 | // Update the memory blocks. | ||
| 3482 | m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, | ||
| 3483 | &KMemoryBlock::UnshareToDevice, KMemoryPermission::None); | ||
| 3484 | |||
| 3485 | R_SUCCEED(); | ||
| 3486 | } | ||
| 3487 | |||
| 3488 | Result KPageTableBase::UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size) { | ||
| 3489 | // Lightly validate the range before doing anything else. | ||
| 3490 | const size_t num_pages = size / PageSize; | ||
| 3491 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 3492 | |||
| 3493 | // Lock the table. | ||
| 3494 | KScopedLightLock lk(m_general_lock); | ||
| 3495 | |||
| 3496 | // Check memory state. | ||
| 3497 | size_t allocator_num_blocks = 0; | ||
| 3498 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 3499 | std::addressof(allocator_num_blocks), address, size, KMemoryState::FlagCanDeviceMap, | ||
| 3500 | KMemoryState::FlagCanDeviceMap, KMemoryPermission::None, KMemoryPermission::None, | ||
| 3501 | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); | ||
| 3502 | |||
| 3503 | // Create an update allocator for the region. | ||
| 3504 | Result allocator_result; | ||
| 3505 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 3506 | m_memory_block_slab_manager, allocator_num_blocks); | ||
| 3507 | R_TRY(allocator_result); | ||
| 3508 | |||
| 3509 | // Update the memory blocks. | ||
| 3510 | m_memory_block_manager.UpdateLock( | ||
| 3511 | std::addressof(allocator), address, num_pages, | ||
| 3512 | m_enable_device_address_space_merge | ||
| 3513 | ? &KMemoryBlock::UpdateDeviceDisableMergeStateForUnshare | ||
| 3514 | : &KMemoryBlock::UpdateDeviceDisableMergeStateForUnshareRight, | ||
| 3515 | KMemoryPermission::None); | ||
| 3516 | |||
| 3517 | R_SUCCEED(); | ||
| 3518 | } | ||
| 3519 | |||
| 3520 | Result KPageTableBase::OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out, | ||
| 3521 | KProcessAddress address, size_t size, | ||
| 3522 | KMemoryPermission perm, | ||
| 3523 | bool is_aligned) { | ||
| 3524 | // Lock the table. | ||
| 3525 | KScopedLightLock lk(m_general_lock); | ||
| 3526 | |||
| 3527 | // Get the range. | ||
| 3528 | const KMemoryState test_state = | ||
| 3529 | (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap); | ||
| 3530 | R_TRY(this->GetContiguousMemoryRangeWithState( | ||
| 3531 | out, address, size, test_state, test_state, perm, perm, | ||
| 3532 | KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked, KMemoryAttribute::None)); | ||
| 3533 | |||
| 3534 | // We got the range, so open it. | ||
| 3535 | out->Open(); | ||
| 3536 | |||
| 3537 | R_SUCCEED(); | ||
| 3538 | } | ||
| 3539 | |||
| 3540 | Result KPageTableBase::OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange* out, | ||
| 3541 | KProcessAddress address, | ||
| 3542 | size_t size) { | ||
| 3543 | // Lock the table. | ||
| 3544 | KScopedLightLock lk(m_general_lock); | ||
| 3545 | |||
| 3546 | // Get the range. | ||
| 3547 | R_TRY(this->GetContiguousMemoryRangeWithState( | ||
| 3548 | out, address, size, KMemoryState::FlagCanDeviceMap, KMemoryState::FlagCanDeviceMap, | ||
| 3549 | KMemoryPermission::None, KMemoryPermission::None, | ||
| 3550 | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); | ||
| 3551 | |||
| 3552 | // We got the range, so open it. | ||
| 3553 | out->Open(); | ||
| 3554 | |||
| 3555 | R_SUCCEED(); | ||
| 3556 | } | ||
| 3557 | |||
| 3558 | Result KPageTableBase::LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, | ||
| 3559 | size_t size) { | ||
| 3560 | R_RETURN(this->LockMemoryAndOpen( | ||
| 3561 | nullptr, out, address, size, KMemoryState::FlagCanIpcUserBuffer, | ||
| 3562 | KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::All, | ||
| 3563 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None, | ||
| 3564 | static_cast<KMemoryPermission>(KMemoryPermission::NotMapped | | ||
| 3565 | KMemoryPermission::KernelReadWrite), | ||
| 3566 | KMemoryAttribute::Locked)); | ||
| 3567 | } | ||
| 3568 | |||
| 3569 | Result KPageTableBase::UnlockForIpcUserBuffer(KProcessAddress address, size_t size) { | ||
| 3570 | R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanIpcUserBuffer, | ||
| 3571 | KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::None, | ||
| 3572 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 3573 | KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, | ||
| 3574 | KMemoryAttribute::Locked, nullptr)); | ||
| 3575 | } | ||
| 3576 | |||
| 3577 | Result KPageTableBase::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, | ||
| 3578 | KMemoryPermission perm) { | ||
| 3579 | R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer, | ||
| 3580 | KMemoryState::FlagCanTransfer, KMemoryPermission::All, | ||
| 3581 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, | ||
| 3582 | KMemoryAttribute::None, perm, KMemoryAttribute::Locked)); | ||
| 3583 | } | ||
| 3584 | |||
| 3585 | Result KPageTableBase::UnlockForTransferMemory(KProcessAddress address, size_t size, | ||
| 3586 | const KPageGroup& pg) { | ||
| 3587 | R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer, | ||
| 3588 | KMemoryState::FlagCanTransfer, KMemoryPermission::None, | ||
| 3589 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 3590 | KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, | ||
| 3591 | KMemoryAttribute::Locked, std::addressof(pg))); | ||
| 3592 | } | ||
| 3593 | |||
| 3594 | Result KPageTableBase::LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size) { | ||
| 3595 | R_RETURN(this->LockMemoryAndOpen( | ||
| 3596 | out, nullptr, address, size, KMemoryState::FlagCanCodeMemory, | ||
| 3597 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite, | ||
| 3598 | KMemoryAttribute::All, KMemoryAttribute::None, | ||
| 3599 | static_cast<KMemoryPermission>(KMemoryPermission::NotMapped | | ||
| 3600 | KMemoryPermission::KernelReadWrite), | ||
| 3601 | KMemoryAttribute::Locked)); | ||
| 3602 | } | ||
| 3603 | |||
| 3604 | Result KPageTableBase::UnlockForCodeMemory(KProcessAddress address, size_t size, | ||
| 3605 | const KPageGroup& pg) { | ||
| 3606 | R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanCodeMemory, | ||
| 3607 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, | ||
| 3608 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 3609 | KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, | ||
| 3610 | KMemoryAttribute::Locked, std::addressof(pg))); | ||
| 3611 | } | ||
| 3612 | |||
| 3613 | Result KPageTableBase::OpenMemoryRangeForProcessCacheOperation(MemoryRange* out, | ||
| 3614 | KProcessAddress address, | ||
| 3615 | size_t size) { | ||
| 3616 | // Lock the table. | ||
| 3617 | KScopedLightLock lk(m_general_lock); | ||
| 3618 | |||
| 3619 | // Get the range. | ||
| 3620 | R_TRY(this->GetContiguousMemoryRangeWithState( | ||
| 3621 | out, address, size, KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted, | ||
| 3622 | KMemoryPermission::UserRead, KMemoryPermission::UserRead, KMemoryAttribute::Uncached, | ||
| 3623 | KMemoryAttribute::None)); | ||
| 3624 | |||
| 3625 | // We got the range, so open it. | ||
| 3626 | out->Open(); | ||
| 3627 | |||
| 3628 | R_SUCCEED(); | ||
| 3629 | } | ||
| 3630 | |||
| 3631 | Result KPageTableBase::CopyMemoryFromLinearToUser( | ||
| 3632 | KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, KMemoryState src_state_mask, | ||
| 3633 | KMemoryState src_state, KMemoryPermission src_test_perm, KMemoryAttribute src_attr_mask, | ||
| 3634 | KMemoryAttribute src_attr) { | ||
| 3635 | // Lightly validate the range before doing anything else. | ||
| 3636 | R_UNLESS(this->Contains(src_addr, size), ResultInvalidCurrentMemory); | ||
| 3637 | |||
| 3638 | // Get the destination memory reference. | ||
| 3639 | auto& dst_memory = GetCurrentMemory(m_kernel); | ||
| 3640 | |||
| 3641 | // Copy the memory. | ||
| 3642 | { | ||
| 3643 | // Lock the table. | ||
| 3644 | KScopedLightLock lk(m_general_lock); | ||
| 3645 | |||
| 3646 | // Check memory state. | ||
| 3647 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 3648 | src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm, | ||
| 3649 | src_attr_mask | KMemoryAttribute::Uncached, src_attr)); | ||
| 3650 | |||
| 3651 | auto& impl = this->GetImpl(); | ||
| 3652 | |||
| 3653 | // Begin traversal. | ||
| 3654 | TraversalContext context; | ||
| 3655 | TraversalEntry next_entry; | ||
| 3656 | bool traverse_valid = | ||
| 3657 | impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr); | ||
| 3658 | ASSERT(traverse_valid); | ||
| 3659 | |||
| 3660 | // Prepare tracking variables. | ||
| 3661 | KPhysicalAddress cur_addr = next_entry.phys_addr; | ||
| 3662 | size_t cur_size = | ||
| 3663 | next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1)); | ||
| 3664 | size_t tot_size = cur_size; | ||
| 3665 | |||
| 3666 | auto PerformCopy = [&]() -> Result { | ||
| 3667 | // Ensure the address is linear mapped. | ||
| 3668 | R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); | ||
| 3669 | |||
| 3670 | // Copy as much aligned data as we can. | ||
| 3671 | if (cur_size >= sizeof(u32)) { | ||
| 3672 | const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32)); | ||
| 3673 | R_UNLESS(dst_memory.WriteBlock(dst_addr, | ||
| 3674 | GetLinearMappedVirtualPointer(m_kernel, cur_addr), | ||
| 3675 | copy_size), | ||
| 3676 | ResultInvalidCurrentMemory); | ||
| 3677 | |||
| 3678 | dst_addr += copy_size; | ||
| 3679 | cur_addr += copy_size; | ||
| 3680 | cur_size -= copy_size; | ||
| 3681 | } | ||
| 3682 | |||
| 3683 | // Copy remaining data. | ||
| 3684 | if (cur_size > 0) { | ||
| 3685 | R_UNLESS(dst_memory.WriteBlock( | ||
| 3686 | dst_addr, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size), | ||
| 3687 | ResultInvalidCurrentMemory); | ||
| 3688 | } | ||
| 3689 | |||
| 3690 | R_SUCCEED(); | ||
| 3691 | }; | ||
| 3692 | |||
| 3693 | // Iterate. | ||
| 3694 | while (tot_size < size) { | ||
| 3695 | // Continue the traversal. | ||
| 3696 | traverse_valid = | ||
| 3697 | impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 3698 | ASSERT(traverse_valid); | ||
| 3699 | |||
| 3700 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 3701 | // Perform copy. | ||
| 3702 | R_TRY(PerformCopy()); | ||
| 3703 | |||
| 3704 | // Advance. | ||
| 3705 | dst_addr += cur_size; | ||
| 3706 | |||
| 3707 | cur_addr = next_entry.phys_addr; | ||
| 3708 | cur_size = next_entry.block_size; | ||
| 3709 | } else { | ||
| 3710 | cur_size += next_entry.block_size; | ||
| 3711 | } | ||
| 3712 | |||
| 3713 | tot_size += next_entry.block_size; | ||
| 3714 | } | ||
| 3715 | |||
| 3716 | // Ensure we use the right size for the last block. | ||
| 3717 | if (tot_size > size) { | ||
| 3718 | cur_size -= (tot_size - size); | ||
| 3719 | } | ||
| 3720 | |||
| 3721 | // Perform copy for the last block. | ||
| 3722 | R_TRY(PerformCopy()); | ||
| 3723 | } | ||
| 3724 | |||
| 3725 | R_SUCCEED(); | ||
| 3726 | } | ||
| 3727 | |||
| 3728 | Result KPageTableBase::CopyMemoryFromLinearToKernel( | ||
| 3729 | void* buffer, size_t size, KProcessAddress src_addr, KMemoryState src_state_mask, | ||
| 3730 | KMemoryState src_state, KMemoryPermission src_test_perm, KMemoryAttribute src_attr_mask, | ||
| 3731 | KMemoryAttribute src_attr) { | ||
| 3732 | // Lightly validate the range before doing anything else. | ||
| 3733 | R_UNLESS(this->Contains(src_addr, size), ResultInvalidCurrentMemory); | ||
| 3734 | |||
| 3735 | // Copy the memory. | ||
| 3736 | { | ||
| 3737 | // Lock the table. | ||
| 3738 | KScopedLightLock lk(m_general_lock); | ||
| 3739 | |||
| 3740 | // Check memory state. | ||
| 3741 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 3742 | src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm, | ||
| 3743 | src_attr_mask | KMemoryAttribute::Uncached, src_attr)); | ||
| 3744 | |||
| 3745 | auto& impl = this->GetImpl(); | ||
| 3746 | |||
| 3747 | // Begin traversal. | ||
| 3748 | TraversalContext context; | ||
| 3749 | TraversalEntry next_entry; | ||
| 3750 | bool traverse_valid = | ||
| 3751 | impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr); | ||
| 3752 | ASSERT(traverse_valid); | ||
| 3753 | |||
| 3754 | // Prepare tracking variables. | ||
| 3755 | KPhysicalAddress cur_addr = next_entry.phys_addr; | ||
| 3756 | size_t cur_size = | ||
| 3757 | next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1)); | ||
| 3758 | size_t tot_size = cur_size; | ||
| 3759 | |||
| 3760 | auto PerformCopy = [&]() -> Result { | ||
| 3761 | // Ensure the address is linear mapped. | ||
| 3762 | R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); | ||
| 3763 | |||
| 3764 | // Copy the data. | ||
| 3765 | std::memcpy(buffer, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size); | ||
| 3766 | |||
| 3767 | R_SUCCEED(); | ||
| 3768 | }; | ||
| 3769 | |||
| 3770 | // Iterate. | ||
| 3771 | while (tot_size < size) { | ||
| 3772 | // Continue the traversal. | ||
| 3773 | traverse_valid = | ||
| 3774 | impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 3775 | ASSERT(traverse_valid); | ||
| 3776 | |||
| 3777 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 3778 | // Perform copy. | ||
| 3779 | R_TRY(PerformCopy()); | ||
| 3780 | |||
| 3781 | // Advance. | ||
| 3782 | buffer = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(buffer) + cur_size); | ||
| 3783 | |||
| 3784 | cur_addr = next_entry.phys_addr; | ||
| 3785 | cur_size = next_entry.block_size; | ||
| 3786 | } else { | ||
| 3787 | cur_size += next_entry.block_size; | ||
| 3788 | } | ||
| 3789 | |||
| 3790 | tot_size += next_entry.block_size; | ||
| 3791 | } | ||
| 3792 | |||
| 3793 | // Ensure we use the right size for the last block. | ||
| 3794 | if (tot_size > size) { | ||
| 3795 | cur_size -= (tot_size - size); | ||
| 3796 | } | ||
| 3797 | |||
| 3798 | // Perform copy for the last block. | ||
| 3799 | R_TRY(PerformCopy()); | ||
| 3800 | } | ||
| 3801 | |||
| 3802 | R_SUCCEED(); | ||
| 3803 | } | ||
| 3804 | |||
| 3805 | Result KPageTableBase::CopyMemoryFromUserToLinear( | ||
| 3806 | KProcessAddress dst_addr, size_t size, KMemoryState dst_state_mask, KMemoryState dst_state, | ||
| 3807 | KMemoryPermission dst_test_perm, KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, | ||
| 3808 | KProcessAddress src_addr) { | ||
| 3809 | // Lightly validate the range before doing anything else. | ||
| 3810 | R_UNLESS(this->Contains(dst_addr, size), ResultInvalidCurrentMemory); | ||
| 3811 | |||
| 3812 | // Get the source memory reference. | ||
| 3813 | auto& src_memory = GetCurrentMemory(m_kernel); | ||
| 3814 | |||
| 3815 | // Copy the memory. | ||
| 3816 | { | ||
| 3817 | // Lock the table. | ||
| 3818 | KScopedLightLock lk(m_general_lock); | ||
| 3819 | |||
| 3820 | // Check memory state. | ||
| 3821 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 3822 | dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_test_perm, | ||
| 3823 | dst_attr_mask | KMemoryAttribute::Uncached, dst_attr)); | ||
| 3824 | |||
| 3825 | auto& impl = this->GetImpl(); | ||
| 3826 | |||
| 3827 | // Begin traversal. | ||
| 3828 | TraversalContext context; | ||
| 3829 | TraversalEntry next_entry; | ||
| 3830 | bool traverse_valid = | ||
| 3831 | impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr); | ||
| 3832 | ASSERT(traverse_valid); | ||
| 3833 | |||
| 3834 | // Prepare tracking variables. | ||
| 3835 | KPhysicalAddress cur_addr = next_entry.phys_addr; | ||
| 3836 | size_t cur_size = | ||
| 3837 | next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1)); | ||
| 3838 | size_t tot_size = cur_size; | ||
| 3839 | |||
| 3840 | auto PerformCopy = [&]() -> Result { | ||
| 3841 | // Ensure the address is linear mapped. | ||
| 3842 | R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); | ||
| 3843 | |||
| 3844 | // Copy as much aligned data as we can. | ||
| 3845 | if (cur_size >= sizeof(u32)) { | ||
| 3846 | const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32)); | ||
| 3847 | R_UNLESS(src_memory.ReadBlock(src_addr, | ||
| 3848 | GetLinearMappedVirtualPointer(m_kernel, cur_addr), | ||
| 3849 | copy_size), | ||
| 3850 | ResultInvalidCurrentMemory); | ||
| 3851 | src_addr += copy_size; | ||
| 3852 | cur_addr += copy_size; | ||
| 3853 | cur_size -= copy_size; | ||
| 3854 | } | ||
| 3855 | |||
| 3856 | // Copy remaining data. | ||
| 3857 | if (cur_size > 0) { | ||
| 3858 | R_UNLESS(src_memory.ReadBlock( | ||
| 3859 | src_addr, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size), | ||
| 3860 | ResultInvalidCurrentMemory); | ||
| 3861 | } | ||
| 3862 | |||
| 3863 | R_SUCCEED(); | ||
| 3864 | }; | ||
| 3865 | |||
| 3866 | // Iterate. | ||
| 3867 | while (tot_size < size) { | ||
| 3868 | // Continue the traversal. | ||
| 3869 | traverse_valid = | ||
| 3870 | impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 3871 | ASSERT(traverse_valid); | ||
| 3872 | |||
| 3873 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 3874 | // Perform copy. | ||
| 3875 | R_TRY(PerformCopy()); | ||
| 3876 | |||
| 3877 | // Advance. | ||
| 3878 | src_addr += cur_size; | ||
| 3879 | |||
| 3880 | cur_addr = next_entry.phys_addr; | ||
| 3881 | cur_size = next_entry.block_size; | ||
| 3882 | } else { | ||
| 3883 | cur_size += next_entry.block_size; | ||
| 3884 | } | ||
| 3885 | |||
| 3886 | tot_size += next_entry.block_size; | ||
| 3887 | } | ||
| 3888 | |||
| 3889 | // Ensure we use the right size for the last block. | ||
| 3890 | if (tot_size > size) { | ||
| 3891 | cur_size -= (tot_size - size); | ||
| 3892 | } | ||
| 3893 | |||
| 3894 | // Perform copy for the last block. | ||
| 3895 | R_TRY(PerformCopy()); | ||
| 3896 | } | ||
| 3897 | |||
| 3898 | R_SUCCEED(); | ||
| 3899 | } | ||
| 3900 | |||
| 3901 | Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size, | ||
| 3902 | KMemoryState dst_state_mask, | ||
| 3903 | KMemoryState dst_state, | ||
| 3904 | KMemoryPermission dst_test_perm, | ||
| 3905 | KMemoryAttribute dst_attr_mask, | ||
| 3906 | KMemoryAttribute dst_attr, void* buffer) { | ||
| 3907 | // Lightly validate the range before doing anything else. | ||
| 3908 | R_UNLESS(this->Contains(dst_addr, size), ResultInvalidCurrentMemory); | ||
| 3909 | |||
| 3910 | // Copy the memory. | ||
| 3911 | { | ||
| 3912 | // Lock the table. | ||
| 3913 | KScopedLightLock lk(m_general_lock); | ||
| 3914 | |||
| 3915 | // Check memory state. | ||
| 3916 | R_TRY(this->CheckMemoryStateContiguous( | ||
| 3917 | dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_test_perm, | ||
| 3918 | dst_attr_mask | KMemoryAttribute::Uncached, dst_attr)); | ||
| 3919 | |||
| 3920 | auto& impl = this->GetImpl(); | ||
| 3921 | |||
| 3922 | // Begin traversal. | ||
| 3923 | TraversalContext context; | ||
| 3924 | TraversalEntry next_entry; | ||
| 3925 | bool traverse_valid = | ||
| 3926 | impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr); | ||
| 3927 | ASSERT(traverse_valid); | ||
| 3928 | |||
| 3929 | // Prepare tracking variables. | ||
| 3930 | KPhysicalAddress cur_addr = next_entry.phys_addr; | ||
| 3931 | size_t cur_size = | ||
| 3932 | next_entry.block_size - (GetInteger(cur_addr) & (next_entry.block_size - 1)); | ||
| 3933 | size_t tot_size = cur_size; | ||
| 3934 | |||
| 3935 | auto PerformCopy = [&]() -> Result { | ||
| 3936 | // Ensure the address is linear mapped. | ||
| 3937 | R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory); | ||
| 3938 | |||
| 3939 | // Copy the data. | ||
| 3940 | std::memcpy(GetLinearMappedVirtualPointer(m_kernel, cur_addr), buffer, cur_size); | ||
| 3941 | |||
| 3942 | R_SUCCEED(); | ||
| 3943 | }; | ||
| 3944 | |||
| 3945 | // Iterate. | ||
| 3946 | while (tot_size < size) { | ||
| 3947 | // Continue the traversal. | ||
| 3948 | traverse_valid = | ||
| 3949 | impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 3950 | ASSERT(traverse_valid); | ||
| 3951 | |||
| 3952 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 3953 | // Perform copy. | ||
| 3954 | R_TRY(PerformCopy()); | ||
| 3955 | |||
| 3956 | // Advance. | ||
| 3957 | buffer = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(buffer) + cur_size); | ||
| 3958 | |||
| 3959 | cur_addr = next_entry.phys_addr; | ||
| 3960 | cur_size = next_entry.block_size; | ||
| 3961 | } else { | ||
| 3962 | cur_size += next_entry.block_size; | ||
| 3963 | } | ||
| 3964 | |||
| 3965 | tot_size += next_entry.block_size; | ||
| 3966 | } | ||
| 3967 | |||
| 3968 | // Ensure we use the right size for the last block. | ||
| 3969 | if (tot_size > size) { | ||
| 3970 | cur_size -= (tot_size - size); | ||
| 3971 | } | ||
| 3972 | |||
| 3973 | // Perform copy for the last block. | ||
| 3974 | R_TRY(PerformCopy()); | ||
| 3975 | } | ||
| 3976 | |||
| 3977 | R_SUCCEED(); | ||
| 3978 | } | ||
| 3979 | |||
| 3980 | Result KPageTableBase::CopyMemoryFromHeapToHeap( | ||
| 3981 | KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size, | ||
| 3982 | KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm, | ||
| 3983 | KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr, | ||
| 3984 | KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm, | ||
| 3985 | KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) { | ||
| 3986 | // For convenience, alias this. | ||
| 3987 | KPageTableBase& src_page_table = *this; | ||
| 3988 | |||
| 3989 | // Lightly validate the ranges before doing anything else. | ||
| 3990 | R_UNLESS(src_page_table.Contains(src_addr, size), ResultInvalidCurrentMemory); | ||
| 3991 | R_UNLESS(dst_page_table.Contains(dst_addr, size), ResultInvalidCurrentMemory); | ||
| 3992 | |||
| 3993 | // Copy the memory. | ||
| 3994 | { | ||
| 3995 | // Acquire the table locks. | ||
| 3996 | KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); | ||
| 3997 | |||
| 3998 | // Check memory state. | ||
| 3999 | R_TRY(src_page_table.CheckMemoryStateContiguous( | ||
| 4000 | src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm, | ||
| 4001 | src_attr_mask | KMemoryAttribute::Uncached, src_attr)); | ||
| 4002 | R_TRY(dst_page_table.CheckMemoryStateContiguous( | ||
| 4003 | dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_test_perm, | ||
| 4004 | dst_attr_mask | KMemoryAttribute::Uncached, dst_attr)); | ||
| 4005 | |||
| 4006 | // Get implementations. | ||
| 4007 | auto& src_impl = src_page_table.GetImpl(); | ||
| 4008 | auto& dst_impl = dst_page_table.GetImpl(); | ||
| 4009 | |||
| 4010 | // Prepare for traversal. | ||
| 4011 | TraversalContext src_context; | ||
| 4012 | TraversalContext dst_context; | ||
| 4013 | TraversalEntry src_next_entry; | ||
| 4014 | TraversalEntry dst_next_entry; | ||
| 4015 | bool traverse_valid; | ||
| 4016 | |||
| 4017 | // Begin traversal. | ||
| 4018 | traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry), | ||
| 4019 | std::addressof(src_context), src_addr); | ||
| 4020 | ASSERT(traverse_valid); | ||
| 4021 | traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry), | ||
| 4022 | std::addressof(dst_context), dst_addr); | ||
| 4023 | ASSERT(traverse_valid); | ||
| 4024 | |||
| 4025 | // Prepare tracking variables. | ||
| 4026 | KPhysicalAddress cur_src_block_addr = src_next_entry.phys_addr; | ||
| 4027 | KPhysicalAddress cur_dst_block_addr = dst_next_entry.phys_addr; | ||
| 4028 | size_t cur_src_size = src_next_entry.block_size - | ||
| 4029 | (GetInteger(cur_src_block_addr) & (src_next_entry.block_size - 1)); | ||
| 4030 | size_t cur_dst_size = dst_next_entry.block_size - | ||
| 4031 | (GetInteger(cur_dst_block_addr) & (dst_next_entry.block_size - 1)); | ||
| 4032 | |||
| 4033 | // Adjust the initial block sizes. | ||
| 4034 | src_next_entry.block_size = cur_src_size; | ||
| 4035 | dst_next_entry.block_size = cur_dst_size; | ||
| 4036 | |||
| 4037 | // Before we get any crazier, succeed if there's nothing to do. | ||
| 4038 | R_SUCCEED_IF(size == 0); | ||
| 4039 | |||
| 4040 | // We're going to manage dual traversal via an offset against the total size. | ||
| 4041 | KPhysicalAddress cur_src_addr = cur_src_block_addr; | ||
| 4042 | KPhysicalAddress cur_dst_addr = cur_dst_block_addr; | ||
| 4043 | size_t cur_min_size = std::min<size_t>(cur_src_size, cur_dst_size); | ||
| 4044 | |||
| 4045 | // Iterate. | ||
| 4046 | size_t ofs = 0; | ||
| 4047 | while (ofs < size) { | ||
| 4048 | // Determine how much we can copy this iteration. | ||
| 4049 | const size_t cur_copy_size = std::min<size_t>(cur_min_size, size - ofs); | ||
| 4050 | |||
| 4051 | // If we need to advance the traversals, do so. | ||
| 4052 | bool updated_src = false, updated_dst = false, skip_copy = false; | ||
| 4053 | if (ofs + cur_copy_size != size) { | ||
| 4054 | if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) { | ||
| 4055 | // Continue the src traversal. | ||
| 4056 | traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry), | ||
| 4057 | std::addressof(src_context)); | ||
| 4058 | ASSERT(traverse_valid); | ||
| 4059 | |||
| 4060 | // Update source. | ||
| 4061 | updated_src = cur_src_addr + cur_min_size != src_next_entry.phys_addr; | ||
| 4062 | } | ||
| 4063 | |||
| 4064 | if (cur_dst_addr + cur_min_size == | ||
| 4065 | dst_next_entry.phys_addr + dst_next_entry.block_size) { | ||
| 4066 | // Continue the dst traversal. | ||
| 4067 | traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry), | ||
| 4068 | std::addressof(dst_context)); | ||
| 4069 | ASSERT(traverse_valid); | ||
| 4070 | |||
| 4071 | // Update destination. | ||
| 4072 | updated_dst = cur_dst_addr + cur_min_size != dst_next_entry.phys_addr; | ||
| 4073 | } | ||
| 4074 | |||
| 4075 | // If we didn't update either of source/destination, skip the copy this iteration. | ||
| 4076 | if (!updated_src && !updated_dst) { | ||
| 4077 | skip_copy = true; | ||
| 4078 | |||
| 4079 | // Update the source block address. | ||
| 4080 | cur_src_block_addr = src_next_entry.phys_addr; | ||
| 4081 | } | ||
| 4082 | } | ||
| 4083 | |||
| 4084 | // Do the copy, unless we're skipping it. | ||
| 4085 | if (!skip_copy) { | ||
| 4086 | // We need both ends of the copy to be heap blocks. | ||
| 4087 | R_UNLESS(IsHeapPhysicalAddress(cur_src_addr), ResultInvalidCurrentMemory); | ||
| 4088 | R_UNLESS(IsHeapPhysicalAddress(cur_dst_addr), ResultInvalidCurrentMemory); | ||
| 4089 | |||
| 4090 | // Copy the data. | ||
| 4091 | std::memcpy(GetHeapVirtualPointer(m_kernel, cur_dst_addr), | ||
| 4092 | GetHeapVirtualPointer(m_kernel, cur_src_addr), cur_copy_size); | ||
| 4093 | |||
| 4094 | // Update. | ||
| 4095 | cur_src_block_addr = src_next_entry.phys_addr; | ||
| 4096 | cur_src_addr = updated_src ? cur_src_block_addr : cur_src_addr + cur_copy_size; | ||
| 4097 | cur_dst_block_addr = dst_next_entry.phys_addr; | ||
| 4098 | cur_dst_addr = updated_dst ? cur_dst_block_addr : cur_dst_addr + cur_copy_size; | ||
| 4099 | |||
| 4100 | // Advance offset. | ||
| 4101 | ofs += cur_copy_size; | ||
| 4102 | } | ||
| 4103 | |||
| 4104 | // Update min size. | ||
| 4105 | cur_src_size = src_next_entry.block_size; | ||
| 4106 | cur_dst_size = dst_next_entry.block_size; | ||
| 4107 | cur_min_size = std::min<size_t>(cur_src_block_addr - cur_src_addr + cur_src_size, | ||
| 4108 | cur_dst_block_addr - cur_dst_addr + cur_dst_size); | ||
| 4109 | } | ||
| 4110 | } | ||
| 4111 | |||
| 4112 | R_SUCCEED(); | ||
| 4113 | } | ||
| 4114 | |||
| 4115 | Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination( | ||
| 4116 | KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size, | ||
| 4117 | KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm, | ||
| 4118 | KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr, | ||
| 4119 | KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm, | ||
| 4120 | KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) { | ||
| 4121 | // For convenience, alias this. | ||
| 4122 | KPageTableBase& src_page_table = *this; | ||
| 4123 | |||
| 4124 | // Lightly validate the ranges before doing anything else. | ||
| 4125 | R_UNLESS(src_page_table.Contains(src_addr, size), ResultInvalidCurrentMemory); | ||
| 4126 | R_UNLESS(dst_page_table.Contains(dst_addr, size), ResultInvalidCurrentMemory); | ||
| 4127 | |||
| 4128 | // Copy the memory. | ||
| 4129 | { | ||
| 4130 | // Acquire the table locks. | ||
| 4131 | KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); | ||
| 4132 | |||
| 4133 | // Check memory state for source. | ||
| 4134 | R_TRY(src_page_table.CheckMemoryStateContiguous( | ||
| 4135 | src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm, | ||
| 4136 | src_attr_mask | KMemoryAttribute::Uncached, src_attr)); | ||
| 4137 | |||
| 4138 | // Destination state is intentionally unchecked. | ||
| 4139 | |||
| 4140 | // Get implementations. | ||
| 4141 | auto& src_impl = src_page_table.GetImpl(); | ||
| 4142 | auto& dst_impl = dst_page_table.GetImpl(); | ||
| 4143 | |||
| 4144 | // Prepare for traversal. | ||
| 4145 | TraversalContext src_context; | ||
| 4146 | TraversalContext dst_context; | ||
| 4147 | TraversalEntry src_next_entry; | ||
| 4148 | TraversalEntry dst_next_entry; | ||
| 4149 | bool traverse_valid; | ||
| 4150 | |||
| 4151 | // Begin traversal. | ||
| 4152 | traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry), | ||
| 4153 | std::addressof(src_context), src_addr); | ||
| 4154 | ASSERT(traverse_valid); | ||
| 4155 | traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry), | ||
| 4156 | std::addressof(dst_context), dst_addr); | ||
| 4157 | ASSERT(traverse_valid); | ||
| 4158 | |||
| 4159 | // Prepare tracking variables. | ||
| 4160 | KPhysicalAddress cur_src_block_addr = src_next_entry.phys_addr; | ||
| 4161 | KPhysicalAddress cur_dst_block_addr = dst_next_entry.phys_addr; | ||
| 4162 | size_t cur_src_size = src_next_entry.block_size - | ||
| 4163 | (GetInteger(cur_src_block_addr) & (src_next_entry.block_size - 1)); | ||
| 4164 | size_t cur_dst_size = dst_next_entry.block_size - | ||
| 4165 | (GetInteger(cur_dst_block_addr) & (dst_next_entry.block_size - 1)); | ||
| 4166 | |||
| 4167 | // Adjust the initial block sizes. | ||
| 4168 | src_next_entry.block_size = cur_src_size; | ||
| 4169 | dst_next_entry.block_size = cur_dst_size; | ||
| 4170 | |||
| 4171 | // Before we get any crazier, succeed if there's nothing to do. | ||
| 4172 | R_SUCCEED_IF(size == 0); | ||
| 4173 | |||
| 4174 | // We're going to manage dual traversal via an offset against the total size. | ||
| 4175 | KPhysicalAddress cur_src_addr = cur_src_block_addr; | ||
| 4176 | KPhysicalAddress cur_dst_addr = cur_dst_block_addr; | ||
| 4177 | size_t cur_min_size = std::min<size_t>(cur_src_size, cur_dst_size); | ||
| 4178 | |||
| 4179 | // Iterate. | ||
| 4180 | size_t ofs = 0; | ||
| 4181 | while (ofs < size) { | ||
| 4182 | // Determine how much we can copy this iteration. | ||
| 4183 | const size_t cur_copy_size = std::min<size_t>(cur_min_size, size - ofs); | ||
| 4184 | |||
| 4185 | // If we need to advance the traversals, do so. | ||
| 4186 | bool updated_src = false, updated_dst = false, skip_copy = false; | ||
| 4187 | if (ofs + cur_copy_size != size) { | ||
| 4188 | if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) { | ||
| 4189 | // Continue the src traversal. | ||
| 4190 | traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry), | ||
| 4191 | std::addressof(src_context)); | ||
| 4192 | ASSERT(traverse_valid); | ||
| 4193 | |||
| 4194 | // Update source. | ||
| 4195 | updated_src = cur_src_addr + cur_min_size != src_next_entry.phys_addr; | ||
| 4196 | } | ||
| 4197 | |||
| 4198 | if (cur_dst_addr + cur_min_size == | ||
| 4199 | dst_next_entry.phys_addr + dst_next_entry.block_size) { | ||
| 4200 | // Continue the dst traversal. | ||
| 4201 | traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry), | ||
| 4202 | std::addressof(dst_context)); | ||
| 4203 | ASSERT(traverse_valid); | ||
| 4204 | |||
| 4205 | // Update destination. | ||
| 4206 | updated_dst = cur_dst_addr + cur_min_size != dst_next_entry.phys_addr; | ||
| 4207 | } | ||
| 4208 | |||
| 4209 | // If we didn't update either of source/destination, skip the copy this iteration. | ||
| 4210 | if (!updated_src && !updated_dst) { | ||
| 4211 | skip_copy = true; | ||
| 4212 | |||
| 4213 | // Update the source block address. | ||
| 4214 | cur_src_block_addr = src_next_entry.phys_addr; | ||
| 4215 | } | ||
| 4216 | } | ||
| 4217 | |||
| 4218 | // Do the copy, unless we're skipping it. | ||
| 4219 | if (!skip_copy) { | ||
| 4220 | // We need both ends of the copy to be heap blocks. | ||
| 4221 | R_UNLESS(IsHeapPhysicalAddress(cur_src_addr), ResultInvalidCurrentMemory); | ||
| 4222 | R_UNLESS(IsHeapPhysicalAddress(cur_dst_addr), ResultInvalidCurrentMemory); | ||
| 4223 | |||
| 4224 | // Copy the data. | ||
| 4225 | std::memcpy(GetHeapVirtualPointer(m_kernel, cur_dst_addr), | ||
| 4226 | GetHeapVirtualPointer(m_kernel, cur_src_addr), cur_copy_size); | ||
| 4227 | |||
| 4228 | // Update. | ||
| 4229 | cur_src_block_addr = src_next_entry.phys_addr; | ||
| 4230 | cur_src_addr = updated_src ? cur_src_block_addr : cur_src_addr + cur_copy_size; | ||
| 4231 | cur_dst_block_addr = dst_next_entry.phys_addr; | ||
| 4232 | cur_dst_addr = updated_dst ? cur_dst_block_addr : cur_dst_addr + cur_copy_size; | ||
| 4233 | |||
| 4234 | // Advance offset. | ||
| 4235 | ofs += cur_copy_size; | ||
| 4236 | } | ||
| 4237 | |||
| 4238 | // Update min size. | ||
| 4239 | cur_src_size = src_next_entry.block_size; | ||
| 4240 | cur_dst_size = dst_next_entry.block_size; | ||
| 4241 | cur_min_size = std::min<size_t>(cur_src_block_addr - cur_src_addr + cur_src_size, | ||
| 4242 | cur_dst_block_addr - cur_dst_addr + cur_dst_size); | ||
| 4243 | } | ||
| 4244 | } | ||
| 4245 | |||
| 4246 | R_SUCCEED(); | ||
| 4247 | } | ||
| 4248 | |||
| 4249 | Result KPageTableBase::SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed, | ||
| 4250 | KProcessAddress address, size_t size, | ||
| 4251 | KMemoryPermission test_perm, KMemoryState dst_state) { | ||
| 4252 | // Validate pre-conditions. | ||
| 4253 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 4254 | ASSERT(test_perm == KMemoryPermission::UserReadWrite || | ||
| 4255 | test_perm == KMemoryPermission::UserRead); | ||
| 4256 | |||
| 4257 | // Check that the address is in range. | ||
| 4258 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 4259 | |||
| 4260 | // Get the source permission. | ||
| 4261 | const auto src_perm = static_cast<KMemoryPermission>( | ||
| 4262 | (test_perm == KMemoryPermission::UserReadWrite) | ||
| 4263 | ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped | ||
| 4264 | : KMemoryPermission::UserRead); | ||
| 4265 | |||
| 4266 | // Get aligned extents. | ||
| 4267 | const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(address), PageSize); | ||
| 4268 | const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(address) + size, PageSize); | ||
| 4269 | const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(address), PageSize); | ||
| 4270 | const KProcessAddress mapping_src_end = Common::AlignDown(GetInteger(address) + size, PageSize); | ||
| 4271 | |||
| 4272 | const auto aligned_src_last = GetInteger(aligned_src_end) - 1; | ||
| 4273 | const auto mapping_src_last = GetInteger(mapping_src_end) - 1; | ||
| 4274 | |||
| 4275 | // Get the test state and attribute mask. | ||
| 4276 | KMemoryState test_state; | ||
| 4277 | KMemoryAttribute test_attr_mask; | ||
| 4278 | switch (dst_state) { | ||
| 4279 | case KMemoryState::Ipc: | ||
| 4280 | test_state = KMemoryState::FlagCanUseIpc; | ||
| 4281 | test_attr_mask = | ||
| 4282 | KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked; | ||
| 4283 | break; | ||
| 4284 | case KMemoryState::NonSecureIpc: | ||
| 4285 | test_state = KMemoryState::FlagCanUseNonSecureIpc; | ||
| 4286 | test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; | ||
| 4287 | break; | ||
| 4288 | case KMemoryState::NonDeviceIpc: | ||
| 4289 | test_state = KMemoryState::FlagCanUseNonDeviceIpc; | ||
| 4290 | test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; | ||
| 4291 | break; | ||
| 4292 | default: | ||
| 4293 | R_THROW(ResultInvalidCombination); | ||
| 4294 | } | ||
| 4295 | |||
| 4296 | // Ensure that on failure, we roll back appropriately. | ||
| 4297 | size_t mapped_size = 0; | ||
| 4298 | ON_RESULT_FAILURE { | ||
| 4299 | if (mapped_size > 0) { | ||
| 4300 | this->CleanupForIpcClientOnServerSetupFailure(page_list, mapping_src_start, mapped_size, | ||
| 4301 | src_perm); | ||
| 4302 | } | ||
| 4303 | }; | ||
| 4304 | |||
| 4305 | size_t blocks_needed = 0; | ||
| 4306 | |||
| 4307 | // Iterate, mapping as needed. | ||
| 4308 | KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(aligned_src_start); | ||
| 4309 | while (true) { | ||
| 4310 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 4311 | |||
| 4312 | // Validate the current block. | ||
| 4313 | R_TRY(this->CheckMemoryState(info, test_state, test_state, test_perm, test_perm, | ||
| 4314 | test_attr_mask, KMemoryAttribute::None)); | ||
| 4315 | |||
| 4316 | if (mapping_src_start < mapping_src_end && | ||
| 4317 | GetInteger(mapping_src_start) < info.GetEndAddress() && | ||
| 4318 | info.GetAddress() < GetInteger(mapping_src_end)) { | ||
| 4319 | const auto cur_start = info.GetAddress() >= GetInteger(mapping_src_start) | ||
| 4320 | ? info.GetAddress() | ||
| 4321 | : GetInteger(mapping_src_start); | ||
| 4322 | const auto cur_end = mapping_src_last >= info.GetLastAddress() | ||
| 4323 | ? info.GetEndAddress() | ||
| 4324 | : GetInteger(mapping_src_end); | ||
| 4325 | const size_t cur_size = cur_end - cur_start; | ||
| 4326 | |||
| 4327 | if (info.GetAddress() < GetInteger(mapping_src_start)) { | ||
| 4328 | ++blocks_needed; | ||
| 4329 | } | ||
| 4330 | if (mapping_src_last < info.GetLastAddress()) { | ||
| 4331 | ++blocks_needed; | ||
| 4332 | } | ||
| 4333 | |||
| 4334 | // Set the permissions on the block, if we need to. | ||
| 4335 | if ((info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != src_perm) { | ||
| 4336 | const DisableMergeAttribute head_body_attr = | ||
| 4337 | (GetInteger(mapping_src_start) >= info.GetAddress()) | ||
| 4338 | ? DisableMergeAttribute::DisableHeadAndBody | ||
| 4339 | : DisableMergeAttribute::None; | ||
| 4340 | const DisableMergeAttribute tail_attr = (cur_end == GetInteger(mapping_src_end)) | ||
| 4341 | ? DisableMergeAttribute::DisableTail | ||
| 4342 | : DisableMergeAttribute::None; | ||
| 4343 | const KPageProperties properties = { | ||
| 4344 | src_perm, false, false, | ||
| 4345 | static_cast<DisableMergeAttribute>(head_body_attr | tail_attr)}; | ||
| 4346 | R_TRY(this->Operate(page_list, cur_start, cur_size / PageSize, 0, false, properties, | ||
| 4347 | OperationType::ChangePermissions, false)); | ||
| 4348 | } | ||
| 4349 | |||
| 4350 | // Note that we mapped this part. | ||
| 4351 | mapped_size += cur_size; | ||
| 4352 | } | ||
| 4353 | |||
| 4354 | // If the block is at the end, we're done. | ||
| 4355 | if (aligned_src_last <= info.GetLastAddress()) { | ||
| 4356 | break; | ||
| 4357 | } | ||
| 4358 | |||
| 4359 | // Advance. | ||
| 4360 | ++it; | ||
| 4361 | ASSERT(it != m_memory_block_manager.end()); | ||
| 4362 | } | ||
| 4363 | |||
| 4364 | if (out_blocks_needed != nullptr) { | ||
| 4365 | ASSERT(blocks_needed <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); | ||
| 4366 | *out_blocks_needed = blocks_needed; | ||
| 4367 | } | ||
| 4368 | |||
| 4369 | R_SUCCEED(); | ||
| 4370 | } | ||
| 4371 | |||
| 4372 | Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, | ||
| 4373 | KProcessAddress src_addr, KMemoryPermission test_perm, | ||
| 4374 | KMemoryState dst_state, KPageTableBase& src_page_table, | ||
| 4375 | bool send) { | ||
| 4376 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 4377 | ASSERT(src_page_table.IsLockedByCurrentThread()); | ||
| 4378 | |||
| 4379 | // Check that we can theoretically map. | ||
| 4380 | const KProcessAddress region_start = m_alias_region_start; | ||
| 4381 | const size_t region_size = m_alias_region_end - m_alias_region_start; | ||
| 4382 | R_UNLESS(size < region_size, ResultOutOfAddressSpace); | ||
| 4383 | |||
| 4384 | // Get aligned source extents. | ||
| 4385 | const KProcessAddress src_start = src_addr; | ||
| 4386 | const KProcessAddress src_end = src_addr + size; | ||
| 4387 | const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(src_start), PageSize); | ||
| 4388 | const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(src_start) + size, PageSize); | ||
| 4389 | const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(src_start), PageSize); | ||
| 4390 | const KProcessAddress mapping_src_end = | ||
| 4391 | Common::AlignDown(GetInteger(src_start) + size, PageSize); | ||
| 4392 | const size_t aligned_src_size = aligned_src_end - aligned_src_start; | ||
| 4393 | const size_t mapping_src_size = | ||
| 4394 | (mapping_src_start < mapping_src_end) ? (mapping_src_end - mapping_src_start) : 0; | ||
| 4395 | |||
| 4396 | // Select a random address to map at. | ||
| 4397 | KProcessAddress dst_addr = 0; | ||
| 4398 | { | ||
| 4399 | const size_t alignment = 4_KiB; | ||
| 4400 | const size_t offset = GetInteger(aligned_src_start) & (alignment - 1); | ||
| 4401 | |||
| 4402 | dst_addr = | ||
| 4403 | this->FindFreeArea(region_start, region_size / PageSize, aligned_src_size / PageSize, | ||
| 4404 | alignment, offset, this->GetNumGuardPages()); | ||
| 4405 | R_UNLESS(dst_addr != 0, ResultOutOfAddressSpace); | ||
| 4406 | } | ||
| 4407 | |||
| 4408 | // Check that we can perform the operation we're about to perform. | ||
| 4409 | ASSERT(this->CanContain(dst_addr, aligned_src_size, dst_state)); | ||
| 4410 | |||
| 4411 | // Create an update allocator. | ||
| 4412 | Result allocator_result; | ||
| 4413 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 4414 | m_memory_block_slab_manager); | ||
| 4415 | R_TRY(allocator_result); | ||
| 4416 | |||
| 4417 | // We're going to perform an update, so create a helper. | ||
| 4418 | KScopedPageTableUpdater updater(this); | ||
| 4419 | |||
| 4420 | // Reserve space for any partial pages we allocate. | ||
| 4421 | const size_t unmapped_size = aligned_src_size - mapping_src_size; | ||
| 4422 | KScopedResourceReservation memory_reservation( | ||
| 4423 | m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, unmapped_size); | ||
| 4424 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | ||
| 4425 | |||
| 4426 | // Ensure that we manage page references correctly. | ||
| 4427 | KPhysicalAddress start_partial_page = 0; | ||
| 4428 | KPhysicalAddress end_partial_page = 0; | ||
| 4429 | KProcessAddress cur_mapped_addr = dst_addr; | ||
| 4430 | |||
| 4431 | // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll | ||
| 4432 | // free on scope exit. | ||
| 4433 | SCOPE_EXIT({ | ||
| 4434 | if (start_partial_page != 0) { | ||
| 4435 | m_kernel.MemoryManager().Close(start_partial_page, 1); | ||
| 4436 | } | ||
| 4437 | if (end_partial_page != 0) { | ||
| 4438 | m_kernel.MemoryManager().Close(end_partial_page, 1); | ||
| 4439 | } | ||
| 4440 | }); | ||
| 4441 | |||
| 4442 | ON_RESULT_FAILURE { | ||
| 4443 | if (cur_mapped_addr != dst_addr) { | ||
| 4444 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 4445 | DisableMergeAttribute::None}; | ||
| 4446 | R_ASSERT(this->Operate(updater.GetPageList(), dst_addr, | ||
| 4447 | (cur_mapped_addr - dst_addr) / PageSize, 0, false, | ||
| 4448 | unmap_properties, OperationType::Unmap, true)); | ||
| 4449 | } | ||
| 4450 | }; | ||
| 4451 | |||
| 4452 | // Allocate the start page as needed. | ||
| 4453 | if (aligned_src_start < mapping_src_start) { | ||
| 4454 | start_partial_page = | ||
| 4455 | m_kernel.MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option); | ||
| 4456 | R_UNLESS(start_partial_page != 0, ResultOutOfMemory); | ||
| 4457 | } | ||
| 4458 | |||
| 4459 | // Allocate the end page as needed. | ||
| 4460 | if (mapping_src_end < aligned_src_end && | ||
| 4461 | (aligned_src_start < mapping_src_end || aligned_src_start == mapping_src_start)) { | ||
| 4462 | end_partial_page = | ||
| 4463 | m_kernel.MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option); | ||
| 4464 | R_UNLESS(end_partial_page != 0, ResultOutOfMemory); | ||
| 4465 | } | ||
| 4466 | |||
| 4467 | // Get the implementation. | ||
| 4468 | auto& src_impl = src_page_table.GetImpl(); | ||
| 4469 | |||
| 4470 | // Get the fill value for partial pages. | ||
| 4471 | const auto fill_val = m_ipc_fill_value; | ||
| 4472 | |||
| 4473 | // Begin traversal. | ||
| 4474 | TraversalContext context; | ||
| 4475 | TraversalEntry next_entry; | ||
| 4476 | bool traverse_valid = src_impl.BeginTraversal(std::addressof(next_entry), | ||
| 4477 | std::addressof(context), aligned_src_start); | ||
| 4478 | ASSERT(traverse_valid); | ||
| 4479 | |||
| 4480 | // Prepare tracking variables. | ||
| 4481 | KPhysicalAddress cur_block_addr = next_entry.phys_addr; | ||
| 4482 | size_t cur_block_size = | ||
| 4483 | next_entry.block_size - (GetInteger(cur_block_addr) & (next_entry.block_size - 1)); | ||
| 4484 | size_t tot_block_size = cur_block_size; | ||
| 4485 | |||
| 4486 | // Map the start page, if we have one. | ||
| 4487 | if (start_partial_page != 0) { | ||
| 4488 | // Ensure the page holds correct data. | ||
| 4489 | u8* const start_partial_virt = GetHeapVirtualPointer(m_kernel, start_partial_page); | ||
| 4490 | if (send) { | ||
| 4491 | const size_t partial_offset = src_start - aligned_src_start; | ||
| 4492 | size_t copy_size, clear_size; | ||
| 4493 | if (src_end < mapping_src_start) { | ||
| 4494 | copy_size = size; | ||
| 4495 | clear_size = mapping_src_start - src_end; | ||
| 4496 | } else { | ||
| 4497 | copy_size = mapping_src_start - src_start; | ||
| 4498 | clear_size = 0; | ||
| 4499 | } | ||
| 4500 | |||
| 4501 | std::memset(start_partial_virt, fill_val, partial_offset); | ||
| 4502 | std::memcpy(start_partial_virt + partial_offset, | ||
| 4503 | GetHeapVirtualPointer(m_kernel, cur_block_addr) + partial_offset, | ||
| 4504 | copy_size); | ||
| 4505 | if (clear_size > 0) { | ||
| 4506 | std::memset(start_partial_virt + partial_offset + copy_size, fill_val, clear_size); | ||
| 4507 | } | ||
| 4508 | } else { | ||
| 4509 | std::memset(start_partial_virt, fill_val, PageSize); | ||
| 4510 | } | ||
| 4511 | |||
| 4512 | // Map the page. | ||
| 4513 | const KPageProperties start_map_properties = {test_perm, false, false, | ||
| 4514 | DisableMergeAttribute::DisableHead}; | ||
| 4515 | R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, 1, start_partial_page, true, | ||
| 4516 | start_map_properties, OperationType::Map, false)); | ||
| 4517 | |||
| 4518 | // Update tracking extents. | ||
| 4519 | cur_mapped_addr += PageSize; | ||
| 4520 | cur_block_addr += PageSize; | ||
| 4521 | cur_block_size -= PageSize; | ||
| 4522 | |||
| 4523 | // If the block's size was one page, we may need to continue traversal. | ||
| 4524 | if (cur_block_size == 0 && aligned_src_size > PageSize) { | ||
| 4525 | traverse_valid = | ||
| 4526 | src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 4527 | ASSERT(traverse_valid); | ||
| 4528 | |||
| 4529 | cur_block_addr = next_entry.phys_addr; | ||
| 4530 | cur_block_size = next_entry.block_size; | ||
| 4531 | tot_block_size += next_entry.block_size; | ||
| 4532 | } | ||
| 4533 | } | ||
| 4534 | |||
| 4535 | // Map the remaining pages. | ||
| 4536 | while (aligned_src_start + tot_block_size < mapping_src_end) { | ||
| 4537 | // Continue the traversal. | ||
| 4538 | traverse_valid = | ||
| 4539 | src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 4540 | ASSERT(traverse_valid); | ||
| 4541 | |||
| 4542 | // Process the block. | ||
| 4543 | if (next_entry.phys_addr != cur_block_addr + cur_block_size) { | ||
| 4544 | // Map the block we've been processing so far. | ||
| 4545 | const KPageProperties map_properties = {test_perm, false, false, | ||
| 4546 | (cur_mapped_addr == dst_addr) | ||
| 4547 | ? DisableMergeAttribute::DisableHead | ||
| 4548 | : DisableMergeAttribute::None}; | ||
| 4549 | R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, cur_block_size / PageSize, | ||
| 4550 | cur_block_addr, true, map_properties, OperationType::Map, false)); | ||
| 4551 | |||
| 4552 | // Update tracking extents. | ||
| 4553 | cur_mapped_addr += cur_block_size; | ||
| 4554 | cur_block_addr = next_entry.phys_addr; | ||
| 4555 | cur_block_size = next_entry.block_size; | ||
| 4556 | } else { | ||
| 4557 | cur_block_size += next_entry.block_size; | ||
| 4558 | } | ||
| 4559 | tot_block_size += next_entry.block_size; | ||
| 4560 | } | ||
| 4561 | |||
| 4562 | // Handle the last direct-mapped page. | ||
| 4563 | if (const KProcessAddress mapped_block_end = | ||
| 4564 | aligned_src_start + tot_block_size - cur_block_size; | ||
| 4565 | mapped_block_end < mapping_src_end) { | ||
| 4566 | const size_t last_block_size = mapping_src_end - mapped_block_end; | ||
| 4567 | |||
| 4568 | // Map the last block. | ||
| 4569 | const KPageProperties map_properties = {test_perm, false, false, | ||
| 4570 | (cur_mapped_addr == dst_addr) | ||
| 4571 | ? DisableMergeAttribute::DisableHead | ||
| 4572 | : DisableMergeAttribute::None}; | ||
| 4573 | R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, last_block_size / PageSize, | ||
| 4574 | cur_block_addr, true, map_properties, OperationType::Map, false)); | ||
| 4575 | |||
| 4576 | // Update tracking extents. | ||
| 4577 | cur_mapped_addr += last_block_size; | ||
| 4578 | cur_block_addr += last_block_size; | ||
| 4579 | if (mapped_block_end + cur_block_size < aligned_src_end && | ||
| 4580 | cur_block_size == last_block_size) { | ||
| 4581 | traverse_valid = | ||
| 4582 | src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); | ||
| 4583 | ASSERT(traverse_valid); | ||
| 4584 | |||
| 4585 | cur_block_addr = next_entry.phys_addr; | ||
| 4586 | } | ||
| 4587 | } | ||
| 4588 | |||
| 4589 | // Map the end page, if we have one. | ||
| 4590 | if (end_partial_page != 0) { | ||
| 4591 | // Ensure the page holds correct data. | ||
| 4592 | u8* const end_partial_virt = GetHeapVirtualPointer(m_kernel, end_partial_page); | ||
| 4593 | if (send) { | ||
| 4594 | const size_t copy_size = src_end - mapping_src_end; | ||
| 4595 | std::memcpy(end_partial_virt, GetHeapVirtualPointer(m_kernel, cur_block_addr), | ||
| 4596 | copy_size); | ||
| 4597 | std::memset(end_partial_virt + copy_size, fill_val, PageSize - copy_size); | ||
| 4598 | } else { | ||
| 4599 | std::memset(end_partial_virt, fill_val, PageSize); | ||
| 4600 | } | ||
| 4601 | |||
| 4602 | // Map the page. | ||
| 4603 | const KPageProperties map_properties = {test_perm, false, false, | ||
| 4604 | (cur_mapped_addr == dst_addr) | ||
| 4605 | ? DisableMergeAttribute::DisableHead | ||
| 4606 | : DisableMergeAttribute::None}; | ||
| 4607 | R_TRY(this->Operate(updater.GetPageList(), cur_mapped_addr, 1, end_partial_page, true, | ||
| 4608 | map_properties, OperationType::Map, false)); | ||
| 4609 | } | ||
| 4610 | |||
| 4611 | // Update memory blocks to reflect our changes | ||
| 4612 | m_memory_block_manager.Update(std::addressof(allocator), dst_addr, aligned_src_size / PageSize, | ||
| 4613 | dst_state, test_perm, KMemoryAttribute::None, | ||
| 4614 | KMemoryBlockDisableMergeAttribute::Normal, | ||
| 4615 | KMemoryBlockDisableMergeAttribute::None); | ||
| 4616 | |||
| 4617 | // Set the output address. | ||
| 4618 | *out_addr = dst_addr + (src_start - aligned_src_start); | ||
| 4619 | |||
| 4620 | // We succeeded. | ||
| 4621 | memory_reservation.Commit(); | ||
| 4622 | R_SUCCEED(); | ||
| 4623 | } | ||
| 4624 | |||
| 4625 | Result KPageTableBase::SetupForIpc(KProcessAddress* out_dst_addr, size_t size, | ||
| 4626 | KProcessAddress src_addr, KPageTableBase& src_page_table, | ||
| 4627 | KMemoryPermission test_perm, KMemoryState dst_state, bool send) { | ||
| 4628 | // For convenience, alias this. | ||
| 4629 | KPageTableBase& dst_page_table = *this; | ||
| 4630 | |||
| 4631 | // Acquire the table locks. | ||
| 4632 | KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); | ||
| 4633 | |||
| 4634 | // We're going to perform an update, so create a helper. | ||
| 4635 | KScopedPageTableUpdater updater(std::addressof(src_page_table)); | ||
| 4636 | |||
| 4637 | // Perform client setup. | ||
| 4638 | size_t num_allocator_blocks; | ||
| 4639 | R_TRY(src_page_table.SetupForIpcClient(updater.GetPageList(), | ||
| 4640 | std::addressof(num_allocator_blocks), src_addr, size, | ||
| 4641 | test_perm, dst_state)); | ||
| 4642 | |||
| 4643 | // Create an update allocator. | ||
| 4644 | Result allocator_result; | ||
| 4645 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 4646 | src_page_table.m_memory_block_slab_manager, | ||
| 4647 | num_allocator_blocks); | ||
| 4648 | R_TRY(allocator_result); | ||
| 4649 | |||
| 4650 | // Get the mapped extents. | ||
| 4651 | const KProcessAddress src_map_start = Common::AlignUp(GetInteger(src_addr), PageSize); | ||
| 4652 | const KProcessAddress src_map_end = Common::AlignDown(GetInteger(src_addr) + size, PageSize); | ||
| 4653 | const size_t src_map_size = src_map_end - src_map_start; | ||
| 4654 | |||
| 4655 | // Ensure that we clean up appropriately if we fail after this. | ||
| 4656 | const auto src_perm = static_cast<KMemoryPermission>( | ||
| 4657 | (test_perm == KMemoryPermission::UserReadWrite) | ||
| 4658 | ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped | ||
| 4659 | : KMemoryPermission::UserRead); | ||
| 4660 | ON_RESULT_FAILURE { | ||
| 4661 | if (src_map_end > src_map_start) { | ||
| 4662 | src_page_table.CleanupForIpcClientOnServerSetupFailure( | ||
| 4663 | updater.GetPageList(), src_map_start, src_map_size, src_perm); | ||
| 4664 | } | ||
| 4665 | }; | ||
| 4666 | |||
| 4667 | // Perform server setup. | ||
| 4668 | R_TRY(dst_page_table.SetupForIpcServer(out_dst_addr, size, src_addr, test_perm, dst_state, | ||
| 4669 | src_page_table, send)); | ||
| 4670 | |||
| 4671 | // If anything was mapped, ipc-lock the pages. | ||
| 4672 | if (src_map_start < src_map_end) { | ||
| 4673 | // Get the source permission. | ||
| 4674 | src_page_table.m_memory_block_manager.UpdateLock(std::addressof(allocator), src_map_start, | ||
| 4675 | (src_map_end - src_map_start) / PageSize, | ||
| 4676 | &KMemoryBlock::LockForIpc, src_perm); | ||
| 4677 | } | ||
| 4678 | |||
| 4679 | R_SUCCEED(); | ||
| 4680 | } | ||
| 4681 | |||
| 4682 | Result KPageTableBase::CleanupForIpcServer(KProcessAddress address, size_t size, | ||
| 4683 | KMemoryState dst_state) { | ||
| 4684 | // Validate the address. | ||
| 4685 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 4686 | |||
| 4687 | // Lock the table. | ||
| 4688 | KScopedLightLock lk(m_general_lock); | ||
| 4689 | |||
| 4690 | // Validate the memory state. | ||
| 4691 | size_t num_allocator_blocks; | ||
| 4692 | R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, | ||
| 4693 | KMemoryState::All, dst_state, KMemoryPermission::UserRead, | ||
| 4694 | KMemoryPermission::UserRead, KMemoryAttribute::All, | ||
| 4695 | KMemoryAttribute::None)); | ||
| 4696 | |||
| 4697 | // Create an update allocator. | ||
| 4698 | Result allocator_result; | ||
| 4699 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 4700 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 4701 | R_TRY(allocator_result); | ||
| 4702 | |||
| 4703 | // We're going to perform an update, so create a helper. | ||
| 4704 | KScopedPageTableUpdater updater(this); | ||
| 4705 | |||
| 4706 | // Get aligned extents. | ||
| 4707 | const KProcessAddress aligned_start = Common::AlignDown(GetInteger(address), PageSize); | ||
| 4708 | const KProcessAddress aligned_end = Common::AlignUp(GetInteger(address) + size, PageSize); | ||
| 4709 | const size_t aligned_size = aligned_end - aligned_start; | ||
| 4710 | const size_t aligned_num_pages = aligned_size / PageSize; | ||
| 4711 | |||
| 4712 | // Unmap the pages. | ||
| 4713 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 4714 | DisableMergeAttribute::None}; | ||
| 4715 | R_TRY(this->Operate(updater.GetPageList(), aligned_start, aligned_num_pages, 0, false, | ||
| 4716 | unmap_properties, OperationType::Unmap, false)); | ||
| 4717 | |||
| 4718 | // Update memory blocks. | ||
| 4719 | m_memory_block_manager.Update(std::addressof(allocator), aligned_start, aligned_num_pages, | ||
| 4720 | KMemoryState::None, KMemoryPermission::None, | ||
| 4721 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | ||
| 4722 | KMemoryBlockDisableMergeAttribute::Normal); | ||
| 4723 | |||
| 4724 | // Release from the resource limit as relevant. | ||
| 4725 | const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize); | ||
| 4726 | const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize); | ||
| 4727 | const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; | ||
| 4728 | m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, | ||
| 4729 | aligned_size - mapping_size); | ||
| 4730 | |||
| 4731 | R_SUCCEED(); | ||
| 4732 | } | ||
| 4733 | |||
| 4734 | Result KPageTableBase::CleanupForIpcClient(KProcessAddress address, size_t size, | ||
| 4735 | KMemoryState dst_state) { | ||
| 4736 | // Validate the address. | ||
| 4737 | R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); | ||
| 4738 | |||
| 4739 | // Get aligned source extents. | ||
| 4740 | const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize); | ||
| 4741 | const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize); | ||
| 4742 | const KProcessAddress mapping_last = mapping_end - 1; | ||
| 4743 | const size_t mapping_size = (mapping_start < mapping_end) ? (mapping_end - mapping_start) : 0; | ||
| 4744 | |||
| 4745 | // If nothing was mapped, we're actually done immediately. | ||
| 4746 | R_SUCCEED_IF(mapping_size == 0); | ||
| 4747 | |||
| 4748 | // Get the test state and attribute mask. | ||
| 4749 | KMemoryState test_state; | ||
| 4750 | KMemoryAttribute test_attr_mask; | ||
| 4751 | switch (dst_state) { | ||
| 4752 | case KMemoryState::Ipc: | ||
| 4753 | test_state = KMemoryState::FlagCanUseIpc; | ||
| 4754 | test_attr_mask = | ||
| 4755 | KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked; | ||
| 4756 | break; | ||
| 4757 | case KMemoryState::NonSecureIpc: | ||
| 4758 | test_state = KMemoryState::FlagCanUseNonSecureIpc; | ||
| 4759 | test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; | ||
| 4760 | break; | ||
| 4761 | case KMemoryState::NonDeviceIpc: | ||
| 4762 | test_state = KMemoryState::FlagCanUseNonDeviceIpc; | ||
| 4763 | test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked; | ||
| 4764 | break; | ||
| 4765 | default: | ||
| 4766 | R_THROW(ResultInvalidCombination); | ||
| 4767 | } | ||
| 4768 | |||
| 4769 | // Lock the table. | ||
| 4770 | // NOTE: Nintendo does this *after* creating the updater below, but this does not follow | ||
| 4771 | // convention elsewhere in KPageTableBase. | ||
| 4772 | KScopedLightLock lk(m_general_lock); | ||
| 4773 | |||
| 4774 | // We're going to perform an update, so create a helper. | ||
| 4775 | KScopedPageTableUpdater updater(this); | ||
| 4776 | |||
| 4777 | // Ensure that on failure, we roll back appropriately. | ||
| 4778 | size_t mapped_size = 0; | ||
| 4779 | ON_RESULT_FAILURE { | ||
| 4780 | if (mapped_size > 0) { | ||
| 4781 | // Determine where the mapping ends. | ||
| 4782 | const auto mapped_end = GetInteger(mapping_start) + mapped_size; | ||
| 4783 | const auto mapped_last = mapped_end - 1; | ||
| 4784 | |||
| 4785 | // Get current and next iterators. | ||
| 4786 | KMemoryBlockManager::const_iterator start_it = | ||
| 4787 | m_memory_block_manager.FindIterator(mapping_start); | ||
| 4788 | KMemoryBlockManager::const_iterator next_it = start_it; | ||
| 4789 | ++next_it; | ||
| 4790 | |||
| 4791 | // Get the current block info. | ||
| 4792 | KMemoryInfo cur_info = start_it->GetMemoryInfo(); | ||
| 4793 | |||
| 4794 | // Create tracking variables. | ||
| 4795 | KProcessAddress cur_address = cur_info.GetAddress(); | ||
| 4796 | size_t cur_size = cur_info.GetSize(); | ||
| 4797 | bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission(); | ||
| 4798 | bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1; | ||
| 4799 | bool first = cur_info.GetIpcDisableMergeCount() == 1 && | ||
| 4800 | False(cur_info.GetDisableMergeAttribute() & | ||
| 4801 | KMemoryBlockDisableMergeAttribute::Locked); | ||
| 4802 | |||
| 4803 | while ((GetInteger(cur_address) + cur_size - 1) < mapped_last) { | ||
| 4804 | // Check that we have a next block. | ||
| 4805 | ASSERT(next_it != m_memory_block_manager.end()); | ||
| 4806 | |||
| 4807 | // Get the next info. | ||
| 4808 | const KMemoryInfo next_info = next_it->GetMemoryInfo(); | ||
| 4809 | |||
| 4810 | // Check if we can consolidate the next block's permission set with the current one. | ||
| 4811 | const bool next_perm_eq = | ||
| 4812 | next_info.GetPermission() == next_info.GetOriginalPermission(); | ||
| 4813 | const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1; | ||
| 4814 | if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm && | ||
| 4815 | cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) { | ||
| 4816 | // We can consolidate the reprotection for the current and next block into a | ||
| 4817 | // single call. | ||
| 4818 | cur_size += next_info.GetSize(); | ||
| 4819 | } else { | ||
| 4820 | // We have to operate on the current block. | ||
| 4821 | if ((cur_needs_set_perm || first) && !cur_perm_eq) { | ||
| 4822 | const KPageProperties properties = { | ||
| 4823 | cur_info.GetPermission(), false, false, | ||
| 4824 | first ? DisableMergeAttribute::EnableAndMergeHeadBodyTail | ||
| 4825 | : DisableMergeAttribute::None}; | ||
| 4826 | R_ASSERT(this->Operate(updater.GetPageList(), cur_address, | ||
| 4827 | cur_size / PageSize, 0, false, properties, | ||
| 4828 | OperationType::ChangePermissions, true)); | ||
| 4829 | } | ||
| 4830 | |||
| 4831 | // Advance. | ||
| 4832 | cur_address = next_info.GetAddress(); | ||
| 4833 | cur_size = next_info.GetSize(); | ||
| 4834 | first = false; | ||
| 4835 | } | ||
| 4836 | |||
| 4837 | // Advance. | ||
| 4838 | cur_info = next_info; | ||
| 4839 | cur_perm_eq = next_perm_eq; | ||
| 4840 | cur_needs_set_perm = next_needs_set_perm; | ||
| 4841 | ++next_it; | ||
| 4842 | } | ||
| 4843 | |||
| 4844 | // Process the last block. | ||
| 4845 | if ((first || cur_needs_set_perm) && !cur_perm_eq) { | ||
| 4846 | const KPageProperties properties = { | ||
| 4847 | cur_info.GetPermission(), false, false, | ||
| 4848 | first ? DisableMergeAttribute::EnableAndMergeHeadBodyTail | ||
| 4849 | : DisableMergeAttribute::None}; | ||
| 4850 | R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, 0, | ||
| 4851 | false, properties, OperationType::ChangePermissions, true)); | ||
| 4852 | } | ||
| 4853 | } | ||
| 4854 | }; | ||
| 4855 | |||
| 4856 | // Iterate, reprotecting as needed. | ||
| 4857 | { | ||
| 4858 | // Get current and next iterators. | ||
| 4859 | KMemoryBlockManager::const_iterator start_it = | ||
| 4860 | m_memory_block_manager.FindIterator(mapping_start); | ||
| 4861 | KMemoryBlockManager::const_iterator next_it = start_it; | ||
| 4862 | ++next_it; | ||
| 4863 | |||
| 4864 | // Validate the current block. | ||
| 4865 | KMemoryInfo cur_info = start_it->GetMemoryInfo(); | ||
| 4866 | R_ASSERT(this->CheckMemoryState( | ||
| 4867 | cur_info, test_state, test_state, KMemoryPermission::None, KMemoryPermission::None, | ||
| 4868 | test_attr_mask | KMemoryAttribute::IpcLocked, KMemoryAttribute::IpcLocked)); | ||
| 4869 | |||
| 4870 | // Create tracking variables. | ||
| 4871 | KProcessAddress cur_address = cur_info.GetAddress(); | ||
| 4872 | size_t cur_size = cur_info.GetSize(); | ||
| 4873 | bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission(); | ||
| 4874 | bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1; | ||
| 4875 | bool first = | ||
| 4876 | cur_info.GetIpcDisableMergeCount() == 1 && | ||
| 4877 | False(cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked); | ||
| 4878 | |||
| 4879 | while ((cur_address + cur_size - 1) < mapping_last) { | ||
| 4880 | // Check that we have a next block. | ||
| 4881 | ASSERT(next_it != m_memory_block_manager.end()); | ||
| 4882 | |||
| 4883 | // Get the next info. | ||
| 4884 | const KMemoryInfo next_info = next_it->GetMemoryInfo(); | ||
| 4885 | |||
| 4886 | // Validate the next block. | ||
| 4887 | R_ASSERT(this->CheckMemoryState( | ||
| 4888 | next_info, test_state, test_state, KMemoryPermission::None, KMemoryPermission::None, | ||
| 4889 | test_attr_mask | KMemoryAttribute::IpcLocked, KMemoryAttribute::IpcLocked)); | ||
| 4890 | |||
| 4891 | // Check if we can consolidate the next block's permission set with the current one. | ||
| 4892 | const bool next_perm_eq = | ||
| 4893 | next_info.GetPermission() == next_info.GetOriginalPermission(); | ||
| 4894 | const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1; | ||
| 4895 | if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm && | ||
| 4896 | cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) { | ||
| 4897 | // We can consolidate the reprotection for the current and next block into a single | ||
| 4898 | // call. | ||
| 4899 | cur_size += next_info.GetSize(); | ||
| 4900 | } else { | ||
| 4901 | // We have to operate on the current block. | ||
| 4902 | if ((cur_needs_set_perm || first) && !cur_perm_eq) { | ||
| 4903 | const KPageProperties properties = { | ||
| 4904 | cur_needs_set_perm ? cur_info.GetOriginalPermission() | ||
| 4905 | : cur_info.GetPermission(), | ||
| 4906 | false, false, | ||
| 4907 | first ? DisableMergeAttribute::EnableHeadAndBody | ||
| 4908 | : DisableMergeAttribute::None}; | ||
| 4909 | R_TRY(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, 0, | ||
| 4910 | false, properties, OperationType::ChangePermissions, | ||
| 4911 | false)); | ||
| 4912 | } | ||
| 4913 | |||
| 4914 | // Mark that we mapped the block. | ||
| 4915 | mapped_size += cur_size; | ||
| 4916 | |||
| 4917 | // Advance. | ||
| 4918 | cur_address = next_info.GetAddress(); | ||
| 4919 | cur_size = next_info.GetSize(); | ||
| 4920 | first = false; | ||
| 4921 | } | ||
| 4922 | |||
| 4923 | // Advance. | ||
| 4924 | cur_info = next_info; | ||
| 4925 | cur_perm_eq = next_perm_eq; | ||
| 4926 | cur_needs_set_perm = next_needs_set_perm; | ||
| 4927 | ++next_it; | ||
| 4928 | } | ||
| 4929 | |||
| 4930 | // Process the last block. | ||
| 4931 | const auto lock_count = | ||
| 4932 | cur_info.GetIpcLockCount() + | ||
| 4933 | (next_it != m_memory_block_manager.end() | ||
| 4934 | ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount()) | ||
| 4935 | : 0); | ||
| 4936 | if ((first || cur_needs_set_perm || (lock_count == 1)) && !cur_perm_eq) { | ||
| 4937 | const DisableMergeAttribute head_body_attr = | ||
| 4938 | first ? DisableMergeAttribute::EnableHeadAndBody : DisableMergeAttribute::None; | ||
| 4939 | const DisableMergeAttribute tail_attr = | ||
| 4940 | lock_count == 1 ? DisableMergeAttribute::EnableTail : DisableMergeAttribute::None; | ||
| 4941 | const KPageProperties properties = { | ||
| 4942 | cur_needs_set_perm ? cur_info.GetOriginalPermission() : cur_info.GetPermission(), | ||
| 4943 | false, false, static_cast<DisableMergeAttribute>(head_body_attr | tail_attr)}; | ||
| 4944 | R_TRY(this->Operate(updater.GetPageList(), cur_address, cur_size / PageSize, 0, false, | ||
| 4945 | properties, OperationType::ChangePermissions, false)); | ||
| 4946 | } | ||
| 4947 | } | ||
| 4948 | |||
| 4949 | // Create an update allocator. | ||
| 4950 | // NOTE: Guaranteed zero blocks needed here. | ||
| 4951 | Result allocator_result; | ||
| 4952 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 4953 | m_memory_block_slab_manager, 0); | ||
| 4954 | R_TRY(allocator_result); | ||
| 4955 | |||
| 4956 | // Unlock the pages. | ||
| 4957 | m_memory_block_manager.UpdateLock(std::addressof(allocator), mapping_start, | ||
| 4958 | mapping_size / PageSize, &KMemoryBlock::UnlockForIpc, | ||
| 4959 | KMemoryPermission::None); | ||
| 4960 | |||
| 4961 | R_SUCCEED(); | ||
| 4962 | } | ||
| 4963 | |||
| 4964 | void KPageTableBase::CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, | ||
| 4965 | KProcessAddress address, size_t size, | ||
| 4966 | KMemoryPermission prot_perm) { | ||
| 4967 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 4968 | ASSERT(Common::IsAligned(GetInteger(address), PageSize)); | ||
| 4969 | ASSERT(Common::IsAligned(size, PageSize)); | ||
| 4970 | |||
| 4971 | // Get the mapped extents. | ||
| 4972 | const KProcessAddress src_map_start = address; | ||
| 4973 | const KProcessAddress src_map_end = address + size; | ||
| 4974 | const KProcessAddress src_map_last = src_map_end - 1; | ||
| 4975 | |||
| 4976 | // This function is only invoked when there's something to do. | ||
| 4977 | ASSERT(src_map_end > src_map_start); | ||
| 4978 | |||
| 4979 | // Iterate over blocks, fixing permissions. | ||
| 4980 | KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(address); | ||
| 4981 | while (true) { | ||
| 4982 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 4983 | |||
| 4984 | const auto cur_start = info.GetAddress() >= GetInteger(src_map_start) | ||
| 4985 | ? info.GetAddress() | ||
| 4986 | : GetInteger(src_map_start); | ||
| 4987 | const auto cur_end = | ||
| 4988 | src_map_last <= info.GetLastAddress() ? src_map_end : info.GetEndAddress(); | ||
| 4989 | |||
| 4990 | // If we can, fix the protections on the block. | ||
| 4991 | if ((info.GetIpcLockCount() == 0 && | ||
| 4992 | (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) || | ||
| 4993 | (info.GetIpcLockCount() != 0 && | ||
| 4994 | (info.GetOriginalPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm)) { | ||
| 4995 | // Check if we actually need to fix the protections on the block. | ||
| 4996 | if (cur_end == src_map_end || info.GetAddress() <= GetInteger(src_map_start) || | ||
| 4997 | (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) { | ||
| 4998 | const bool start_nc = (info.GetAddress() == GetInteger(src_map_start)) | ||
| 4999 | ? (False(info.GetDisableMergeAttribute() & | ||
| 5000 | (KMemoryBlockDisableMergeAttribute::Locked | | ||
| 5001 | KMemoryBlockDisableMergeAttribute::IpcLeft))) | ||
| 5002 | : info.GetAddress() <= GetInteger(src_map_start); | ||
| 5003 | |||
| 5004 | const DisableMergeAttribute head_body_attr = | ||
| 5005 | start_nc ? DisableMergeAttribute::EnableHeadAndBody | ||
| 5006 | : DisableMergeAttribute::None; | ||
| 5007 | DisableMergeAttribute tail_attr; | ||
| 5008 | if (cur_end == src_map_end && info.GetEndAddress() == src_map_end) { | ||
| 5009 | auto next_it = it; | ||
| 5010 | ++next_it; | ||
| 5011 | |||
| 5012 | const auto lock_count = | ||
| 5013 | info.GetIpcLockCount() + | ||
| 5014 | (next_it != m_memory_block_manager.end() | ||
| 5015 | ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount()) | ||
| 5016 | : 0); | ||
| 5017 | tail_attr = lock_count == 0 ? DisableMergeAttribute::EnableTail | ||
| 5018 | : DisableMergeAttribute::None; | ||
| 5019 | } else { | ||
| 5020 | tail_attr = DisableMergeAttribute::None; | ||
| 5021 | } | ||
| 5022 | |||
| 5023 | const KPageProperties properties = { | ||
| 5024 | info.GetPermission(), false, false, | ||
| 5025 | static_cast<DisableMergeAttribute>(head_body_attr | tail_attr)}; | ||
| 5026 | R_ASSERT(this->Operate(page_list, cur_start, (cur_end - cur_start) / PageSize, 0, | ||
| 5027 | false, properties, OperationType::ChangePermissions, true)); | ||
| 5028 | } | ||
| 5029 | } | ||
| 5030 | |||
| 5031 | // If we're past the end of the region, we're done. | ||
| 5032 | if (src_map_last <= info.GetLastAddress()) { | ||
| 5033 | break; | ||
| 5034 | } | ||
| 5035 | |||
| 5036 | // Advance. | ||
| 5037 | ++it; | ||
| 5038 | ASSERT(it != m_memory_block_manager.end()); | ||
| 5039 | } | ||
| 5040 | } | ||
| 5041 | |||
| 5042 | Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) { | ||
| 5043 | // Lock the physical memory lock. | ||
| 5044 | KScopedLightLock phys_lk(m_map_physical_memory_lock); | ||
| 5045 | |||
| 5046 | // Calculate the last address for convenience. | ||
| 5047 | const KProcessAddress last_address = address + size - 1; | ||
| 5048 | |||
| 5049 | // Define iteration variables. | ||
| 5050 | KProcessAddress cur_address; | ||
| 5051 | size_t mapped_size; | ||
| 5052 | |||
| 5053 | // The entire mapping process can be retried. | ||
| 5054 | while (true) { | ||
| 5055 | // Check if the memory is already mapped. | ||
| 5056 | { | ||
| 5057 | // Lock the table. | ||
| 5058 | KScopedLightLock lk(m_general_lock); | ||
| 5059 | |||
| 5060 | // Iterate over the memory. | ||
| 5061 | cur_address = address; | ||
| 5062 | mapped_size = 0; | ||
| 5063 | |||
| 5064 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 5065 | while (true) { | ||
| 5066 | // Check that the iterator is valid. | ||
| 5067 | ASSERT(it != m_memory_block_manager.end()); | ||
| 5068 | |||
| 5069 | // Get the memory info. | ||
| 5070 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 5071 | |||
| 5072 | // Check if we're done. | ||
| 5073 | if (last_address <= info.GetLastAddress()) { | ||
| 5074 | if (info.GetState() != KMemoryState::Free) { | ||
| 5075 | mapped_size += (last_address + 1 - cur_address); | ||
| 5076 | } | ||
| 5077 | break; | ||
| 5078 | } | ||
| 5079 | |||
| 5080 | // Track the memory if it's mapped. | ||
| 5081 | if (info.GetState() != KMemoryState::Free) { | ||
| 5082 | mapped_size += KProcessAddress(info.GetEndAddress()) - cur_address; | ||
| 5083 | } | ||
| 5084 | |||
| 5085 | // Advance. | ||
| 5086 | cur_address = info.GetEndAddress(); | ||
| 5087 | ++it; | ||
| 5088 | } | ||
| 5089 | |||
| 5090 | // If the size mapped is the size requested, we've nothing to do. | ||
| 5091 | R_SUCCEED_IF(size == mapped_size); | ||
| 5092 | } | ||
| 5093 | |||
| 5094 | // Allocate and map the memory. | ||
| 5095 | { | ||
| 5096 | // Reserve the memory from the process resource limit. | ||
| 5097 | KScopedResourceReservation memory_reservation( | ||
| 5098 | m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, size - mapped_size); | ||
| 5099 | R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); | ||
| 5100 | |||
| 5101 | // Allocate pages for the new memory. | ||
| 5102 | KPageGroup pg(m_kernel, m_block_info_manager); | ||
| 5103 | R_TRY(m_kernel.MemoryManager().AllocateForProcess( | ||
| 5104 | std::addressof(pg), (size - mapped_size) / PageSize, m_allocate_option, | ||
| 5105 | GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value)); | ||
| 5106 | |||
| 5107 | // If we fail in the next bit (or retry), we need to cleanup the pages. | ||
| 5108 | auto pg_guard = SCOPE_GUARD({ | ||
| 5109 | pg.OpenFirst(); | ||
| 5110 | pg.Close(); | ||
| 5111 | }); | ||
| 5112 | |||
| 5113 | // Map the memory. | ||
| 5114 | { | ||
| 5115 | // Lock the table. | ||
| 5116 | KScopedLightLock lk(m_general_lock); | ||
| 5117 | |||
| 5118 | size_t num_allocator_blocks = 0; | ||
| 5119 | |||
| 5120 | // Verify that nobody has mapped memory since we first checked. | ||
| 5121 | { | ||
| 5122 | // Iterate over the memory. | ||
| 5123 | size_t checked_mapped_size = 0; | ||
| 5124 | cur_address = address; | ||
| 5125 | |||
| 5126 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 5127 | while (true) { | ||
| 5128 | // Check that the iterator is valid. | ||
| 5129 | ASSERT(it != m_memory_block_manager.end()); | ||
| 5130 | |||
| 5131 | // Get the memory info. | ||
| 5132 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 5133 | |||
| 5134 | const bool is_free = info.GetState() == KMemoryState::Free; | ||
| 5135 | if (is_free) { | ||
| 5136 | if (info.GetAddress() < GetInteger(address)) { | ||
| 5137 | ++num_allocator_blocks; | ||
| 5138 | } | ||
| 5139 | if (last_address < info.GetLastAddress()) { | ||
| 5140 | ++num_allocator_blocks; | ||
| 5141 | } | ||
| 5142 | } | ||
| 5143 | |||
| 5144 | // Check if we're done. | ||
| 5145 | if (last_address <= info.GetLastAddress()) { | ||
| 5146 | if (!is_free) { | ||
| 5147 | checked_mapped_size += (last_address + 1 - cur_address); | ||
| 5148 | } | ||
| 5149 | break; | ||
| 5150 | } | ||
| 5151 | |||
| 5152 | // Track the memory if it's mapped. | ||
| 5153 | if (!is_free) { | ||
| 5154 | checked_mapped_size += | ||
| 5155 | KProcessAddress(info.GetEndAddress()) - cur_address; | ||
| 5156 | } | ||
| 5157 | |||
| 5158 | // Advance. | ||
| 5159 | cur_address = info.GetEndAddress(); | ||
| 5160 | ++it; | ||
| 5161 | } | ||
| 5162 | |||
| 5163 | // If the size now isn't what it was before, somebody mapped or unmapped | ||
| 5164 | // concurrently. If this happened, retry. | ||
| 5165 | if (mapped_size != checked_mapped_size) { | ||
| 5166 | continue; | ||
| 5167 | } | ||
| 5168 | } | ||
| 5169 | |||
| 5170 | // Create an update allocator. | ||
| 5171 | ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); | ||
| 5172 | Result allocator_result; | ||
| 5173 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 5174 | m_memory_block_slab_manager, | ||
| 5175 | num_allocator_blocks); | ||
| 5176 | R_TRY(allocator_result); | ||
| 5177 | |||
| 5178 | // We're going to perform an update, so create a helper. | ||
| 5179 | KScopedPageTableUpdater updater(this); | ||
| 5180 | |||
| 5181 | // Prepare to iterate over the memory. | ||
| 5182 | auto pg_it = pg.begin(); | ||
| 5183 | KPhysicalAddress pg_phys_addr = pg_it->GetAddress(); | ||
| 5184 | size_t pg_pages = pg_it->GetNumPages(); | ||
| 5185 | |||
| 5186 | // Reset the current tracking address, and make sure we clean up on failure. | ||
| 5187 | pg_guard.Cancel(); | ||
| 5188 | cur_address = address; | ||
| 5189 | ON_RESULT_FAILURE { | ||
| 5190 | if (cur_address > address) { | ||
| 5191 | const KProcessAddress last_unmap_address = cur_address - 1; | ||
| 5192 | |||
| 5193 | // Iterate, unmapping the pages. | ||
| 5194 | cur_address = address; | ||
| 5195 | |||
| 5196 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 5197 | while (true) { | ||
| 5198 | // Check that the iterator is valid. | ||
| 5199 | ASSERT(it != m_memory_block_manager.end()); | ||
| 5200 | |||
| 5201 | // Get the memory info. | ||
| 5202 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 5203 | |||
| 5204 | // If the memory state is free, we mapped it and need to unmap it. | ||
| 5205 | if (info.GetState() == KMemoryState::Free) { | ||
| 5206 | // Determine the range to unmap. | ||
| 5207 | const KPageProperties unmap_properties = { | ||
| 5208 | KMemoryPermission::None, false, false, | ||
| 5209 | DisableMergeAttribute::None}; | ||
| 5210 | const size_t cur_pages = | ||
| 5211 | std::min(KProcessAddress(info.GetEndAddress()) - cur_address, | ||
| 5212 | last_unmap_address + 1 - cur_address) / | ||
| 5213 | PageSize; | ||
| 5214 | |||
| 5215 | // Unmap. | ||
| 5216 | R_ASSERT(this->Operate(updater.GetPageList(), cur_address, | ||
| 5217 | cur_pages, 0, false, unmap_properties, | ||
| 5218 | OperationType::Unmap, true)); | ||
| 5219 | } | ||
| 5220 | |||
| 5221 | // Check if we're done. | ||
| 5222 | if (last_unmap_address <= info.GetLastAddress()) { | ||
| 5223 | break; | ||
| 5224 | } | ||
| 5225 | |||
| 5226 | // Advance. | ||
| 5227 | cur_address = info.GetEndAddress(); | ||
| 5228 | ++it; | ||
| 5229 | } | ||
| 5230 | } | ||
| 5231 | |||
| 5232 | // Release any remaining unmapped memory. | ||
| 5233 | m_kernel.MemoryManager().OpenFirst(pg_phys_addr, pg_pages); | ||
| 5234 | m_kernel.MemoryManager().Close(pg_phys_addr, pg_pages); | ||
| 5235 | for (++pg_it; pg_it != pg.end(); ++pg_it) { | ||
| 5236 | m_kernel.MemoryManager().OpenFirst(pg_it->GetAddress(), | ||
| 5237 | pg_it->GetNumPages()); | ||
| 5238 | m_kernel.MemoryManager().Close(pg_it->GetAddress(), pg_it->GetNumPages()); | ||
| 5239 | } | ||
| 5240 | }; | ||
| 5241 | |||
| 5242 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 5243 | while (true) { | ||
| 5244 | // Check that the iterator is valid. | ||
| 5245 | ASSERT(it != m_memory_block_manager.end()); | ||
| 5246 | |||
| 5247 | // Get the memory info. | ||
| 5248 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 5249 | |||
| 5250 | // If it's unmapped, we need to map it. | ||
| 5251 | if (info.GetState() == KMemoryState::Free) { | ||
| 5252 | // Determine the range to map. | ||
| 5253 | const KPageProperties map_properties = { | ||
| 5254 | KMemoryPermission::UserReadWrite, false, false, | ||
| 5255 | cur_address == this->GetAliasRegionStart() | ||
| 5256 | ? DisableMergeAttribute::DisableHead | ||
| 5257 | : DisableMergeAttribute::None}; | ||
| 5258 | size_t map_pages = | ||
| 5259 | std::min(KProcessAddress(info.GetEndAddress()) - cur_address, | ||
| 5260 | last_address + 1 - cur_address) / | ||
| 5261 | PageSize; | ||
| 5262 | |||
| 5263 | // While we have pages to map, map them. | ||
| 5264 | { | ||
| 5265 | // Create a page group for the current mapping range. | ||
| 5266 | KPageGroup cur_pg(m_kernel, m_block_info_manager); | ||
| 5267 | { | ||
| 5268 | ON_RESULT_FAILURE_2 { | ||
| 5269 | cur_pg.OpenFirst(); | ||
| 5270 | cur_pg.Close(); | ||
| 5271 | }; | ||
| 5272 | |||
| 5273 | size_t remain_pages = map_pages; | ||
| 5274 | while (remain_pages > 0) { | ||
| 5275 | // Check if we're at the end of the physical block. | ||
| 5276 | if (pg_pages == 0) { | ||
| 5277 | // Ensure there are more pages to map. | ||
| 5278 | ASSERT(pg_it != pg.end()); | ||
| 5279 | |||
| 5280 | // Advance our physical block. | ||
| 5281 | ++pg_it; | ||
| 5282 | pg_phys_addr = pg_it->GetAddress(); | ||
| 5283 | pg_pages = pg_it->GetNumPages(); | ||
| 5284 | } | ||
| 5285 | |||
| 5286 | // Add whatever we can to the current block. | ||
| 5287 | const size_t cur_pages = std::min(pg_pages, remain_pages); | ||
| 5288 | R_TRY(cur_pg.AddBlock(pg_phys_addr + | ||
| 5289 | ((pg_pages - cur_pages) * PageSize), | ||
| 5290 | cur_pages)); | ||
| 5291 | |||
| 5292 | // Advance. | ||
| 5293 | remain_pages -= cur_pages; | ||
| 5294 | pg_pages -= cur_pages; | ||
| 5295 | } | ||
| 5296 | } | ||
| 5297 | |||
| 5298 | // Map the papges. | ||
| 5299 | R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages, | ||
| 5300 | cur_pg, map_properties, | ||
| 5301 | OperationType::MapFirstGroup, false)); | ||
| 5302 | } | ||
| 5303 | } | ||
| 5304 | |||
| 5305 | // Check if we're done. | ||
| 5306 | if (last_address <= info.GetLastAddress()) { | ||
| 5307 | break; | ||
| 5308 | } | ||
| 5309 | |||
| 5310 | // Advance. | ||
| 5311 | cur_address = info.GetEndAddress(); | ||
| 5312 | ++it; | ||
| 5313 | } | ||
| 5314 | |||
| 5315 | // We succeeded, so commit the memory reservation. | ||
| 5316 | memory_reservation.Commit(); | ||
| 5317 | |||
| 5318 | // Increase our tracked mapped size. | ||
| 5319 | m_mapped_physical_memory_size += (size - mapped_size); | ||
| 5320 | |||
| 5321 | // Update the relevant memory blocks. | ||
| 5322 | m_memory_block_manager.UpdateIfMatch( | ||
| 5323 | std::addressof(allocator), address, size / PageSize, KMemoryState::Free, | ||
| 5324 | KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal, | ||
| 5325 | KMemoryPermission::UserReadWrite, KMemoryAttribute::None, | ||
| 5326 | address == this->GetAliasRegionStart() | ||
| 5327 | ? KMemoryBlockDisableMergeAttribute::Normal | ||
| 5328 | : KMemoryBlockDisableMergeAttribute::None, | ||
| 5329 | KMemoryBlockDisableMergeAttribute::None); | ||
| 5330 | |||
| 5331 | R_SUCCEED(); | ||
| 5332 | } | ||
| 5333 | } | ||
| 5334 | } | ||
| 5335 | } | ||
| 5336 | |||
| 5337 | Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size) { | ||
| 5338 | // Lock the physical memory lock. | ||
| 5339 | KScopedLightLock phys_lk(m_map_physical_memory_lock); | ||
| 5340 | |||
| 5341 | // Lock the table. | ||
| 5342 | KScopedLightLock lk(m_general_lock); | ||
| 5343 | |||
| 5344 | // Calculate the last address for convenience. | ||
| 5345 | const KProcessAddress last_address = address + size - 1; | ||
| 5346 | |||
| 5347 | // Define iteration variables. | ||
| 5348 | KProcessAddress map_start_address = 0; | ||
| 5349 | KProcessAddress map_last_address = 0; | ||
| 5350 | |||
| 5351 | KProcessAddress cur_address; | ||
| 5352 | size_t mapped_size; | ||
| 5353 | size_t num_allocator_blocks = 0; | ||
| 5354 | |||
| 5355 | // Check if the memory is mapped. | ||
| 5356 | { | ||
| 5357 | // Iterate over the memory. | ||
| 5358 | cur_address = address; | ||
| 5359 | mapped_size = 0; | ||
| 5360 | |||
| 5361 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 5362 | while (true) { | ||
| 5363 | // Check that the iterator is valid. | ||
| 5364 | ASSERT(it != m_memory_block_manager.end()); | ||
| 5365 | |||
| 5366 | // Get the memory info. | ||
| 5367 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 5368 | |||
| 5369 | // Verify the memory's state. | ||
| 5370 | const bool is_normal = info.GetState() == KMemoryState::Normal && | ||
| 5371 | info.GetAttribute() == KMemoryAttribute::None; | ||
| 5372 | const bool is_free = info.GetState() == KMemoryState::Free; | ||
| 5373 | R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory); | ||
| 5374 | |||
| 5375 | if (is_normal) { | ||
| 5376 | R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory); | ||
| 5377 | |||
| 5378 | if (map_start_address == 0) { | ||
| 5379 | map_start_address = cur_address; | ||
| 5380 | } | ||
| 5381 | map_last_address = | ||
| 5382 | (last_address >= info.GetLastAddress()) ? info.GetLastAddress() : last_address; | ||
| 5383 | |||
| 5384 | if (info.GetAddress() < GetInteger(address)) { | ||
| 5385 | ++num_allocator_blocks; | ||
| 5386 | } | ||
| 5387 | if (last_address < info.GetLastAddress()) { | ||
| 5388 | ++num_allocator_blocks; | ||
| 5389 | } | ||
| 5390 | |||
| 5391 | mapped_size += (map_last_address + 1 - cur_address); | ||
| 5392 | } | ||
| 5393 | |||
| 5394 | // Check if we're done. | ||
| 5395 | if (last_address <= info.GetLastAddress()) { | ||
| 5396 | break; | ||
| 5397 | } | ||
| 5398 | |||
| 5399 | // Advance. | ||
| 5400 | cur_address = info.GetEndAddress(); | ||
| 5401 | ++it; | ||
| 5402 | } | ||
| 5403 | |||
| 5404 | // If there's nothing mapped, we've nothing to do. | ||
| 5405 | R_SUCCEED_IF(mapped_size == 0); | ||
| 5406 | } | ||
| 5407 | |||
| 5408 | // Create an update allocator. | ||
| 5409 | ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); | ||
| 5410 | Result allocator_result; | ||
| 5411 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 5412 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 5413 | R_TRY(allocator_result); | ||
| 5414 | |||
| 5415 | // We're going to perform an update, so create a helper. | ||
| 5416 | KScopedPageTableUpdater updater(this); | ||
| 5417 | |||
| 5418 | // Separate the mapping. | ||
| 5419 | const KPageProperties sep_properties = {KMemoryPermission::None, false, false, | ||
| 5420 | DisableMergeAttribute::None}; | ||
| 5421 | R_TRY(this->Operate(updater.GetPageList(), map_start_address, | ||
| 5422 | (map_last_address + 1 - map_start_address) / PageSize, 0, false, | ||
| 5423 | sep_properties, OperationType::Separate, false)); | ||
| 5424 | |||
| 5425 | // Reset the current tracking address, and make sure we clean up on failure. | ||
| 5426 | cur_address = address; | ||
| 5427 | |||
| 5428 | // Iterate over the memory, unmapping as we go. | ||
| 5429 | auto it = m_memory_block_manager.FindIterator(cur_address); | ||
| 5430 | |||
| 5431 | const auto clear_merge_attr = | ||
| 5432 | (it->GetState() == KMemoryState::Normal && | ||
| 5433 | it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address) | ||
| 5434 | ? KMemoryBlockDisableMergeAttribute::Normal | ||
| 5435 | : KMemoryBlockDisableMergeAttribute::None; | ||
| 5436 | |||
| 5437 | while (true) { | ||
| 5438 | // Check that the iterator is valid. | ||
| 5439 | ASSERT(it != m_memory_block_manager.end()); | ||
| 5440 | |||
| 5441 | // Get the memory info. | ||
| 5442 | const KMemoryInfo info = it->GetMemoryInfo(); | ||
| 5443 | |||
| 5444 | // If the memory state is normal, we need to unmap it. | ||
| 5445 | if (info.GetState() == KMemoryState::Normal) { | ||
| 5446 | // Determine the range to unmap. | ||
| 5447 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 5448 | DisableMergeAttribute::None}; | ||
| 5449 | const size_t cur_pages = std::min(KProcessAddress(info.GetEndAddress()) - cur_address, | ||
| 5450 | last_address + 1 - cur_address) / | ||
| 5451 | PageSize; | ||
| 5452 | |||
| 5453 | // Unmap. | ||
| 5454 | R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false, | ||
| 5455 | unmap_properties, OperationType::Unmap, false)); | ||
| 5456 | } | ||
| 5457 | |||
| 5458 | // Check if we're done. | ||
| 5459 | if (last_address <= info.GetLastAddress()) { | ||
| 5460 | break; | ||
| 5461 | } | ||
| 5462 | |||
| 5463 | // Advance. | ||
| 5464 | cur_address = info.GetEndAddress(); | ||
| 5465 | ++it; | ||
| 5466 | } | ||
| 5467 | |||
| 5468 | // Release the memory resource. | ||
| 5469 | m_mapped_physical_memory_size -= mapped_size; | ||
| 5470 | m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, mapped_size); | ||
| 5471 | |||
| 5472 | // Update memory blocks. | ||
| 5473 | m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, | ||
| 5474 | KMemoryState::Free, KMemoryPermission::None, | ||
| 5475 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | ||
| 5476 | clear_merge_attr); | ||
| 5477 | |||
| 5478 | // We succeeded. | ||
| 5479 | R_SUCCEED(); | ||
| 5480 | } | ||
| 5481 | |||
| 5482 | Result KPageTableBase::MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) { | ||
| 5483 | UNIMPLEMENTED(); | ||
| 5484 | R_THROW(ResultNotImplemented); | ||
| 5485 | } | ||
| 5486 | |||
| 5487 | Result KPageTableBase::UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) { | ||
| 5488 | UNIMPLEMENTED(); | ||
| 5489 | R_THROW(ResultNotImplemented); | ||
| 5490 | } | ||
| 5491 | |||
| 5492 | Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t size, | ||
| 5493 | KPageTableBase& src_page_table, | ||
| 5494 | KProcessAddress src_address) { | ||
| 5495 | // We need to lock both this table, and the current process's table, so set up an alias. | ||
| 5496 | KPageTableBase& dst_page_table = *this; | ||
| 5497 | |||
| 5498 | // Acquire the table locks. | ||
| 5499 | KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock); | ||
| 5500 | |||
| 5501 | // Check that the memory is mapped in the destination process. | ||
| 5502 | size_t num_allocator_blocks; | ||
| 5503 | R_TRY(dst_page_table.CheckMemoryState( | ||
| 5504 | std::addressof(num_allocator_blocks), dst_address, size, KMemoryState::All, | ||
| 5505 | KMemoryState::SharedCode, KMemoryPermission::UserReadWrite, | ||
| 5506 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)); | ||
| 5507 | |||
| 5508 | // Check that the memory is mapped in the source process. | ||
| 5509 | R_TRY(src_page_table.CheckMemoryState(src_address, size, KMemoryState::FlagCanMapProcess, | ||
| 5510 | KMemoryState::FlagCanMapProcess, KMemoryPermission::None, | ||
| 5511 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 5512 | KMemoryAttribute::None)); | ||
| 5513 | |||
| 5514 | // Validate that the memory ranges are compatible. | ||
| 5515 | { | ||
| 5516 | // Define a helper type. | ||
| 5517 | struct ContiguousRangeInfo { | ||
| 5518 | public: | ||
| 5519 | KPageTableBase& m_pt; | ||
| 5520 | TraversalContext m_context; | ||
| 5521 | TraversalEntry m_entry; | ||
| 5522 | KPhysicalAddress m_phys_addr; | ||
| 5523 | size_t m_cur_size; | ||
| 5524 | size_t m_remaining_size; | ||
| 5525 | |||
| 5526 | public: | ||
| 5527 | ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size) | ||
| 5528 | : m_pt(pt), m_remaining_size(size) { | ||
| 5529 | // Begin a traversal. | ||
| 5530 | ASSERT(m_pt.GetImpl().BeginTraversal(std::addressof(m_entry), | ||
| 5531 | std::addressof(m_context), address)); | ||
| 5532 | |||
| 5533 | // Setup tracking fields. | ||
| 5534 | m_phys_addr = m_entry.phys_addr; | ||
| 5535 | m_cur_size = std::min<size_t>( | ||
| 5536 | m_remaining_size, | ||
| 5537 | m_entry.block_size - (GetInteger(m_phys_addr) & (m_entry.block_size - 1))); | ||
| 5538 | |||
| 5539 | // Consume the whole contiguous block. | ||
| 5540 | this->DetermineContiguousBlockExtents(); | ||
| 5541 | } | ||
| 5542 | |||
| 5543 | void ContinueTraversal() { | ||
| 5544 | // Update our remaining size. | ||
| 5545 | m_remaining_size = m_remaining_size - m_cur_size; | ||
| 5546 | |||
| 5547 | // Update our tracking fields. | ||
| 5548 | if (m_remaining_size > 0) { | ||
| 5549 | m_phys_addr = m_entry.phys_addr; | ||
| 5550 | m_cur_size = std::min<size_t>(m_remaining_size, m_entry.block_size); | ||
| 5551 | |||
| 5552 | // Consume the whole contiguous block. | ||
| 5553 | this->DetermineContiguousBlockExtents(); | ||
| 5554 | } | ||
| 5555 | } | ||
| 5556 | |||
| 5557 | private: | ||
| 5558 | void DetermineContiguousBlockExtents() { | ||
| 5559 | // Continue traversing until we're not contiguous, or we have enough. | ||
| 5560 | while (m_cur_size < m_remaining_size) { | ||
| 5561 | ASSERT(m_pt.GetImpl().ContinueTraversal(std::addressof(m_entry), | ||
| 5562 | std::addressof(m_context))); | ||
| 5563 | |||
| 5564 | // If we're not contiguous, we're done. | ||
| 5565 | if (m_entry.phys_addr != m_phys_addr + m_cur_size) { | ||
| 5566 | break; | ||
| 5567 | } | ||
| 5568 | |||
| 5569 | // Update our current size. | ||
| 5570 | m_cur_size = std::min(m_remaining_size, m_cur_size + m_entry.block_size); | ||
| 5571 | } | ||
| 5572 | } | ||
| 5573 | }; | ||
| 5574 | |||
| 5575 | // Create ranges for both tables. | ||
| 5576 | ContiguousRangeInfo src_range(src_page_table, src_address, size); | ||
| 5577 | ContiguousRangeInfo dst_range(dst_page_table, dst_address, size); | ||
| 5578 | |||
| 5579 | // Validate the ranges. | ||
| 5580 | while (src_range.m_remaining_size > 0 && dst_range.m_remaining_size > 0) { | ||
| 5581 | R_UNLESS(src_range.m_phys_addr == dst_range.m_phys_addr, ResultInvalidMemoryRegion); | ||
| 5582 | R_UNLESS(src_range.m_cur_size == dst_range.m_cur_size, ResultInvalidMemoryRegion); | ||
| 5583 | |||
| 5584 | src_range.ContinueTraversal(); | ||
| 5585 | dst_range.ContinueTraversal(); | ||
| 5586 | } | ||
| 5587 | } | ||
| 5588 | |||
| 5589 | // We no longer need to hold our lock on the source page table. | ||
| 5590 | lk.TryUnlockHalf(src_page_table.m_general_lock); | ||
| 5591 | |||
| 5592 | // Create an update allocator. | ||
| 5593 | Result allocator_result; | ||
| 5594 | KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), | ||
| 5595 | m_memory_block_slab_manager, num_allocator_blocks); | ||
| 5596 | R_TRY(allocator_result); | ||
| 5597 | |||
| 5598 | // We're going to perform an update, so create a helper. | ||
| 5599 | KScopedPageTableUpdater updater(this); | ||
| 5600 | |||
| 5601 | // Unmap the memory. | ||
| 5602 | const size_t num_pages = size / PageSize; | ||
| 5603 | const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, | ||
| 5604 | DisableMergeAttribute::None}; | ||
| 5605 | R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, 0, false, unmap_properties, | ||
| 5606 | OperationType::Unmap, false)); | ||
| 5607 | |||
| 5608 | // Apply the memory block update. | ||
| 5609 | m_memory_block_manager.Update(std::addressof(allocator), dst_address, num_pages, | ||
| 5610 | KMemoryState::Free, KMemoryPermission::None, | ||
| 5611 | KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, | ||
| 5612 | KMemoryBlockDisableMergeAttribute::Normal); | ||
| 5613 | |||
| 5614 | R_SUCCEED(); | ||
| 5615 | } | ||
| 5616 | |||
| 5617 | Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_addr, | ||
| 5618 | size_t num_pages, KPhysicalAddress phys_addr, bool is_pa_valid, | ||
| 5619 | const KPageProperties properties, OperationType operation, | ||
| 5620 | bool reuse_ll) { | ||
| 5621 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 5622 | ASSERT(num_pages > 0); | ||
| 5623 | ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); | ||
| 5624 | ASSERT(this->ContainsPages(virt_addr, num_pages)); | ||
| 5625 | |||
| 5626 | // As we don't allocate page entries in guest memory, we don't need to allocate them from | ||
| 5627 | // or free them to the page list, and so it goes unused (along with page properties). | ||
| 5628 | |||
| 5629 | switch (operation) { | ||
| 5630 | case OperationType::Unmap: { | ||
| 5631 | // Ensure that any pages we track are closed on exit. | ||
| 5632 | KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager()); | ||
| 5633 | SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); | ||
| 5634 | |||
| 5635 | // Make a page group representing the region to unmap. | ||
| 5636 | this->MakePageGroup(pages_to_close, virt_addr, num_pages); | ||
| 5637 | |||
| 5638 | // Unmap. | ||
| 5639 | m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize); | ||
| 5640 | |||
| 5641 | R_SUCCEED(); | ||
| 5642 | } | ||
| 5643 | case OperationType::Map: { | ||
| 5644 | ASSERT(virt_addr != 0); | ||
| 5645 | ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); | ||
| 5646 | m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr); | ||
| 5647 | |||
| 5648 | // Open references to pages, if we should. | ||
| 5649 | if (this->IsHeapPhysicalAddress(phys_addr)) { | ||
| 5650 | m_kernel.MemoryManager().Open(phys_addr, num_pages); | ||
| 5651 | } | ||
| 5652 | |||
| 5653 | R_SUCCEED(); | ||
| 5654 | } | ||
| 5655 | case OperationType::Separate: { | ||
| 5656 | // TODO: Unimplemented. | ||
| 5657 | R_SUCCEED(); | ||
| 5658 | } | ||
| 5659 | case OperationType::ChangePermissions: | ||
| 5660 | case OperationType::ChangePermissionsAndRefresh: | ||
| 5661 | case OperationType::ChangePermissionsAndRefreshAndFlush: | ||
| 5662 | R_SUCCEED(); | ||
| 5663 | default: | ||
| 5664 | UNREACHABLE(); | ||
| 5665 | } | ||
| 5666 | } | ||
| 5667 | |||
| 5668 | Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_addr, | ||
| 5669 | size_t num_pages, const KPageGroup& page_group, | ||
| 5670 | const KPageProperties properties, OperationType operation, | ||
| 5671 | bool reuse_ll) { | ||
| 5672 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 5673 | ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); | ||
| 5674 | ASSERT(num_pages > 0); | ||
| 5675 | ASSERT(num_pages == page_group.GetNumPages()); | ||
| 5676 | |||
| 5677 | // As we don't allocate page entries in guest memory, we don't need to allocate them from | ||
| 5678 | // the page list, and so it goes unused (along with page properties). | ||
| 5679 | |||
| 5680 | switch (operation) { | ||
| 5681 | case OperationType::MapGroup: | ||
| 5682 | case OperationType::MapFirstGroup: { | ||
| 5683 | // We want to maintain a new reference to every page in the group. | ||
| 5684 | KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup); | ||
| 5685 | |||
| 5686 | for (const auto& node : page_group) { | ||
| 5687 | const size_t size{node.GetNumPages() * PageSize}; | ||
| 5688 | |||
| 5689 | // Map the pages. | ||
| 5690 | m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress()); | ||
| 5691 | |||
| 5692 | virt_addr += size; | ||
| 5693 | } | ||
| 5694 | |||
| 5695 | // We succeeded! We want to persist the reference to the pages. | ||
| 5696 | spg.CancelClose(); | ||
| 5697 | |||
| 5698 | R_SUCCEED(); | ||
| 5699 | } | ||
| 5700 | default: | ||
| 5701 | UNREACHABLE(); | ||
| 5702 | } | ||
| 5703 | } | ||
| 5704 | |||
| 5705 | void KPageTableBase::FinalizeUpdate(PageLinkedList* page_list) { | ||
| 5706 | while (page_list->Peek()) { | ||
| 5707 | [[maybe_unused]] auto page = page_list->Pop(); | ||
| 5708 | |||
| 5709 | // TODO: Free page entries once they are allocated in guest memory. | ||
| 5710 | // ASSERT(this->GetPageTableManager().IsInPageTableHeap(page)); | ||
| 5711 | // ASSERT(this->GetPageTableManager().GetRefCount(page) == 0); | ||
| 5712 | // this->GetPageTableManager().Free(page); | ||
| 5713 | } | ||
| 5714 | } | ||
| 5715 | |||
| 5716 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h new file mode 100644 index 000000000..ee2c41e67 --- /dev/null +++ b/src/core/hle/kernel/k_page_table_base.h | |||
| @@ -0,0 +1,759 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | |||
| 8 | #include "common/common_funcs.h" | ||
| 9 | #include "common/page_table.h" | ||
| 10 | #include "core/core.h" | ||
| 11 | #include "core/hle/kernel/k_dynamic_resource_manager.h" | ||
| 12 | #include "core/hle/kernel/k_light_lock.h" | ||
| 13 | #include "core/hle/kernel/k_memory_block.h" | ||
| 14 | #include "core/hle/kernel/k_memory_block_manager.h" | ||
| 15 | #include "core/hle/kernel/k_memory_layout.h" | ||
| 16 | #include "core/hle/kernel/k_memory_manager.h" | ||
| 17 | #include "core/hle/kernel/k_typed_address.h" | ||
| 18 | #include "core/hle/kernel/kernel.h" | ||
| 19 | #include "core/hle/result.h" | ||
| 20 | #include "core/memory.h" | ||
| 21 | |||
| 22 | namespace Kernel { | ||
| 23 | |||
| 24 | enum class DisableMergeAttribute : u8 { | ||
| 25 | None = (0U << 0), | ||
| 26 | |||
| 27 | DisableHead = (1U << 0), | ||
| 28 | DisableHeadAndBody = (1U << 1), | ||
| 29 | EnableHeadAndBody = (1U << 2), | ||
| 30 | DisableTail = (1U << 3), | ||
| 31 | EnableTail = (1U << 4), | ||
| 32 | EnableAndMergeHeadBodyTail = (1U << 5), | ||
| 33 | |||
| 34 | EnableHeadBodyTail = EnableHeadAndBody | EnableTail, | ||
| 35 | DisableHeadBodyTail = DisableHeadAndBody | DisableTail, | ||
| 36 | }; | ||
| 37 | DECLARE_ENUM_FLAG_OPERATORS(DisableMergeAttribute); | ||
| 38 | |||
| 39 | struct KPageProperties { | ||
| 40 | KMemoryPermission perm; | ||
| 41 | bool io; | ||
| 42 | bool uncached; | ||
| 43 | DisableMergeAttribute disable_merge_attributes; | ||
| 44 | }; | ||
| 45 | static_assert(std::is_trivial_v<KPageProperties>); | ||
| 46 | static_assert(sizeof(KPageProperties) == sizeof(u32)); | ||
| 47 | |||
| 48 | class KResourceLimit; | ||
| 49 | class KSystemResource; | ||
| 50 | |||
| 51 | class KPageTableBase { | ||
| 52 | YUZU_NON_COPYABLE(KPageTableBase); | ||
| 53 | YUZU_NON_MOVEABLE(KPageTableBase); | ||
| 54 | |||
| 55 | public: | ||
| 56 | using TraversalEntry = Common::PageTable::TraversalEntry; | ||
| 57 | using TraversalContext = Common::PageTable::TraversalContext; | ||
| 58 | |||
| 59 | class MemoryRange { | ||
| 60 | private: | ||
| 61 | KernelCore& m_kernel; | ||
| 62 | KPhysicalAddress m_address; | ||
| 63 | size_t m_size; | ||
| 64 | bool m_heap; | ||
| 65 | |||
| 66 | public: | ||
| 67 | explicit MemoryRange(KernelCore& kernel) | ||
| 68 | : m_kernel(kernel), m_address(0), m_size(0), m_heap(false) {} | ||
| 69 | |||
| 70 | void Set(KPhysicalAddress address, size_t size, bool heap) { | ||
| 71 | m_address = address; | ||
| 72 | m_size = size; | ||
| 73 | m_heap = heap; | ||
| 74 | } | ||
| 75 | |||
| 76 | KPhysicalAddress GetAddress() const { | ||
| 77 | return m_address; | ||
| 78 | } | ||
| 79 | size_t GetSize() const { | ||
| 80 | return m_size; | ||
| 81 | } | ||
| 82 | bool IsHeap() const { | ||
| 83 | return m_heap; | ||
| 84 | } | ||
| 85 | |||
| 86 | void Open(); | ||
| 87 | void Close(); | ||
| 88 | }; | ||
| 89 | |||
| 90 | protected: | ||
| 91 | enum MemoryFillValue : u8 { | ||
| 92 | MemoryFillValue_Zero = 0, | ||
| 93 | MemoryFillValue_Stack = 'X', | ||
| 94 | MemoryFillValue_Ipc = 'Y', | ||
| 95 | MemoryFillValue_Heap = 'Z', | ||
| 96 | }; | ||
| 97 | |||
| 98 | enum class OperationType { | ||
| 99 | Map = 0, | ||
| 100 | MapGroup = 1, | ||
| 101 | MapFirstGroup = 2, | ||
| 102 | Unmap = 3, | ||
| 103 | ChangePermissions = 4, | ||
| 104 | ChangePermissionsAndRefresh = 5, | ||
| 105 | ChangePermissionsAndRefreshAndFlush = 6, | ||
| 106 | Separate = 7, | ||
| 107 | }; | ||
| 108 | |||
| 109 | static constexpr size_t MaxPhysicalMapAlignment = 1_GiB; | ||
| 110 | static constexpr size_t RegionAlignment = 2_MiB; | ||
| 111 | static_assert(RegionAlignment == KernelAslrAlignment); | ||
| 112 | |||
| 113 | struct PageLinkedList { | ||
| 114 | private: | ||
| 115 | struct Node { | ||
| 116 | Node* m_next; | ||
| 117 | std::array<u8, PageSize - sizeof(Node*)> m_buffer; | ||
| 118 | }; | ||
| 119 | static_assert(std::is_trivial_v<Node>); | ||
| 120 | |||
| 121 | private: | ||
| 122 | Node* m_root{}; | ||
| 123 | |||
| 124 | public: | ||
| 125 | constexpr PageLinkedList() : m_root(nullptr) {} | ||
| 126 | |||
| 127 | void Push(Node* n) { | ||
| 128 | ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize)); | ||
| 129 | n->m_next = m_root; | ||
| 130 | m_root = n; | ||
| 131 | } | ||
| 132 | |||
| 133 | Node* Peek() const { | ||
| 134 | return m_root; | ||
| 135 | } | ||
| 136 | |||
| 137 | Node* Pop() { | ||
| 138 | Node* const r = m_root; | ||
| 139 | |||
| 140 | m_root = r->m_next; | ||
| 141 | r->m_next = nullptr; | ||
| 142 | |||
| 143 | return r; | ||
| 144 | } | ||
| 145 | }; | ||
| 146 | static_assert(std::is_trivially_destructible_v<PageLinkedList>); | ||
| 147 | |||
| 148 | static constexpr auto DefaultMemoryIgnoreAttr = | ||
| 149 | KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; | ||
| 150 | |||
| 151 | static constexpr size_t GetAddressSpaceWidth(Svc::CreateProcessFlag as_type) { | ||
| 152 | switch (static_cast<Svc::CreateProcessFlag>(as_type & | ||
| 153 | Svc::CreateProcessFlag::AddressSpaceMask)) { | ||
| 154 | case Svc::CreateProcessFlag::AddressSpace64Bit: | ||
| 155 | return 39; | ||
| 156 | case Svc::CreateProcessFlag::AddressSpace64BitDeprecated: | ||
| 157 | return 36; | ||
| 158 | case Svc::CreateProcessFlag::AddressSpace32Bit: | ||
| 159 | case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias: | ||
| 160 | return 32; | ||
| 161 | default: | ||
| 162 | UNREACHABLE(); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | private: | ||
| 167 | class KScopedPageTableUpdater { | ||
| 168 | private: | ||
| 169 | KPageTableBase* m_pt; | ||
| 170 | PageLinkedList m_ll; | ||
| 171 | |||
| 172 | public: | ||
| 173 | explicit KScopedPageTableUpdater(KPageTableBase* pt) : m_pt(pt), m_ll() {} | ||
| 174 | explicit KScopedPageTableUpdater(KPageTableBase& pt) | ||
| 175 | : KScopedPageTableUpdater(std::addressof(pt)) {} | ||
| 176 | ~KScopedPageTableUpdater() { | ||
| 177 | m_pt->FinalizeUpdate(this->GetPageList()); | ||
| 178 | } | ||
| 179 | |||
| 180 | PageLinkedList* GetPageList() { | ||
| 181 | return std::addressof(m_ll); | ||
| 182 | } | ||
| 183 | }; | ||
| 184 | |||
| 185 | private: | ||
| 186 | KernelCore& m_kernel; | ||
| 187 | Core::System& m_system; | ||
| 188 | KProcessAddress m_address_space_start{}; | ||
| 189 | KProcessAddress m_address_space_end{}; | ||
| 190 | KProcessAddress m_heap_region_start{}; | ||
| 191 | KProcessAddress m_heap_region_end{}; | ||
| 192 | KProcessAddress m_current_heap_end{}; | ||
| 193 | KProcessAddress m_alias_region_start{}; | ||
| 194 | KProcessAddress m_alias_region_end{}; | ||
| 195 | KProcessAddress m_stack_region_start{}; | ||
| 196 | KProcessAddress m_stack_region_end{}; | ||
| 197 | KProcessAddress m_kernel_map_region_start{}; | ||
| 198 | KProcessAddress m_kernel_map_region_end{}; | ||
| 199 | KProcessAddress m_alias_code_region_start{}; | ||
| 200 | KProcessAddress m_alias_code_region_end{}; | ||
| 201 | KProcessAddress m_code_region_start{}; | ||
| 202 | KProcessAddress m_code_region_end{}; | ||
| 203 | size_t m_max_heap_size{}; | ||
| 204 | size_t m_mapped_physical_memory_size{}; | ||
| 205 | size_t m_mapped_unsafe_physical_memory{}; | ||
| 206 | size_t m_mapped_insecure_memory{}; | ||
| 207 | size_t m_mapped_ipc_server_memory{}; | ||
| 208 | mutable KLightLock m_general_lock; | ||
| 209 | mutable KLightLock m_map_physical_memory_lock; | ||
| 210 | KLightLock m_device_map_lock; | ||
| 211 | std::unique_ptr<Common::PageTable> m_impl{}; | ||
| 212 | Core::Memory::Memory* m_memory{}; | ||
| 213 | KMemoryBlockManager m_memory_block_manager{}; | ||
| 214 | u32 m_allocate_option{}; | ||
| 215 | u32 m_address_space_width{}; | ||
| 216 | bool m_is_kernel{}; | ||
| 217 | bool m_enable_aslr{}; | ||
| 218 | bool m_enable_device_address_space_merge{}; | ||
| 219 | KMemoryBlockSlabManager* m_memory_block_slab_manager{}; | ||
| 220 | KBlockInfoManager* m_block_info_manager{}; | ||
| 221 | KResourceLimit* m_resource_limit{}; | ||
| 222 | const KMemoryRegion* m_cached_physical_linear_region{}; | ||
| 223 | const KMemoryRegion* m_cached_physical_heap_region{}; | ||
| 224 | MemoryFillValue m_heap_fill_value{}; | ||
| 225 | MemoryFillValue m_ipc_fill_value{}; | ||
| 226 | MemoryFillValue m_stack_fill_value{}; | ||
| 227 | |||
| 228 | public: | ||
| 229 | explicit KPageTableBase(KernelCore& kernel); | ||
| 230 | ~KPageTableBase(); | ||
| 231 | |||
| 232 | Result InitializeForKernel(bool is_64_bit, KVirtualAddress start, KVirtualAddress end, | ||
| 233 | Core::Memory::Memory& memory); | ||
| 234 | Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, | ||
| 235 | bool enable_device_address_space_merge, bool from_back, | ||
| 236 | KMemoryManager::Pool pool, KProcessAddress code_address, | ||
| 237 | size_t code_size, KSystemResource* system_resource, | ||
| 238 | KResourceLimit* resource_limit, Core::Memory::Memory& memory); | ||
| 239 | |||
| 240 | void Finalize(); | ||
| 241 | |||
| 242 | bool IsKernel() const { | ||
| 243 | return m_is_kernel; | ||
| 244 | } | ||
| 245 | bool IsAslrEnabled() const { | ||
| 246 | return m_enable_aslr; | ||
| 247 | } | ||
| 248 | |||
| 249 | bool Contains(KProcessAddress addr) const { | ||
| 250 | return m_address_space_start <= addr && addr <= m_address_space_end - 1; | ||
| 251 | } | ||
| 252 | |||
| 253 | bool Contains(KProcessAddress addr, size_t size) const { | ||
| 254 | return m_address_space_start <= addr && addr < addr + size && | ||
| 255 | addr + size - 1 <= m_address_space_end - 1; | ||
| 256 | } | ||
| 257 | |||
| 258 | bool IsInAliasRegion(KProcessAddress addr, size_t size) const { | ||
| 259 | return this->Contains(addr, size) && m_alias_region_start <= addr && | ||
| 260 | addr + size - 1 <= m_alias_region_end - 1; | ||
| 261 | } | ||
| 262 | |||
| 263 | bool IsInHeapRegion(KProcessAddress addr, size_t size) const { | ||
| 264 | return this->Contains(addr, size) && m_heap_region_start <= addr && | ||
| 265 | addr + size - 1 <= m_heap_region_end - 1; | ||
| 266 | } | ||
| 267 | |||
| 268 | bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const { | ||
| 269 | // Even though Unsafe physical memory is KMemoryState_Normal, it must be mapped inside the | ||
| 270 | // alias code region. | ||
| 271 | return this->CanContain(addr, size, Svc::MemoryState::AliasCode); | ||
| 272 | } | ||
| 273 | |||
| 274 | KScopedLightLock AcquireDeviceMapLock() { | ||
| 275 | return KScopedLightLock(m_device_map_lock); | ||
| 276 | } | ||
| 277 | |||
| 278 | KProcessAddress GetRegionAddress(Svc::MemoryState state) const; | ||
| 279 | size_t GetRegionSize(Svc::MemoryState state) const; | ||
| 280 | bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const; | ||
| 281 | |||
| 282 | KProcessAddress GetRegionAddress(KMemoryState state) const { | ||
| 283 | return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask)); | ||
| 284 | } | ||
| 285 | size_t GetRegionSize(KMemoryState state) const { | ||
| 286 | return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask)); | ||
| 287 | } | ||
| 288 | bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { | ||
| 289 | return this->CanContain(addr, size, | ||
| 290 | static_cast<Svc::MemoryState>(state & KMemoryState::Mask)); | ||
| 291 | } | ||
| 292 | |||
| 293 | public: | ||
| 294 | Core::Memory::Memory& GetMemory() { | ||
| 295 | return *m_memory; | ||
| 296 | } | ||
| 297 | |||
| 298 | Core::Memory::Memory& GetMemory() const { | ||
| 299 | return *m_memory; | ||
| 300 | } | ||
| 301 | |||
| 302 | Common::PageTable& GetImpl() { | ||
| 303 | return *m_impl; | ||
| 304 | } | ||
| 305 | |||
| 306 | Common::PageTable& GetImpl() const { | ||
| 307 | return *m_impl; | ||
| 308 | } | ||
| 309 | |||
| 310 | size_t GetNumGuardPages() const { | ||
| 311 | return this->IsKernel() ? 1 : 4; | ||
| 312 | } | ||
| 313 | |||
| 314 | protected: | ||
| 315 | // NOTE: These three functions (Operate, Operate, FinalizeUpdate) are virtual functions | ||
| 316 | // in Nintendo's kernel. We devirtualize them, since KPageTable is the only derived | ||
| 317 | // class, and this avoids unnecessary virtual function calls. | ||
| 318 | Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages, | ||
| 319 | KPhysicalAddress phys_addr, bool is_pa_valid, const KPageProperties properties, | ||
| 320 | OperationType operation, bool reuse_ll); | ||
| 321 | Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages, | ||
| 322 | const KPageGroup& page_group, const KPageProperties properties, | ||
| 323 | OperationType operation, bool reuse_ll); | ||
| 324 | void FinalizeUpdate(PageLinkedList* page_list); | ||
| 325 | |||
| 326 | bool IsLockedByCurrentThread() const { | ||
| 327 | return m_general_lock.IsLockedByCurrentThread(); | ||
| 328 | } | ||
| 329 | |||
| 330 | bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr) { | ||
| 331 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 332 | |||
| 333 | return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress( | ||
| 334 | m_cached_physical_linear_region, phys_addr); | ||
| 335 | } | ||
| 336 | |||
| 337 | bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr, size_t size) { | ||
| 338 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 339 | |||
| 340 | return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress( | ||
| 341 | m_cached_physical_linear_region, phys_addr, size); | ||
| 342 | } | ||
| 343 | |||
| 344 | bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) { | ||
| 345 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 346 | |||
| 347 | return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, | ||
| 348 | phys_addr); | ||
| 349 | } | ||
| 350 | |||
| 351 | bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr, size_t size) { | ||
| 352 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 353 | |||
| 354 | return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, | ||
| 355 | phys_addr, size); | ||
| 356 | } | ||
| 357 | |||
| 358 | bool IsHeapPhysicalAddressForFinalize(KPhysicalAddress phys_addr) { | ||
| 359 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 360 | |||
| 361 | return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, | ||
| 362 | phys_addr); | ||
| 363 | } | ||
| 364 | |||
| 365 | bool ContainsPages(KProcessAddress addr, size_t num_pages) const { | ||
| 366 | return (m_address_space_start <= addr) && | ||
| 367 | (num_pages <= (m_address_space_end - m_address_space_start) / PageSize) && | ||
| 368 | (addr + num_pages * PageSize - 1 <= m_address_space_end - 1); | ||
| 369 | } | ||
| 370 | |||
| 371 | private: | ||
| 372 | KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages, | ||
| 373 | size_t num_pages, size_t alignment, size_t offset, | ||
| 374 | size_t guard_pages) const; | ||
| 375 | |||
| 376 | Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size, | ||
| 377 | KMemoryState state_mask, KMemoryState state, | ||
| 378 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 379 | KMemoryAttribute attr_mask, KMemoryAttribute attr) const; | ||
| 380 | Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 381 | KMemoryState state, KMemoryPermission perm_mask, | ||
| 382 | KMemoryPermission perm, KMemoryAttribute attr_mask, | ||
| 383 | KMemoryAttribute attr) const { | ||
| 384 | R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, | ||
| 385 | perm, attr_mask, attr)); | ||
| 386 | } | ||
| 387 | |||
| 388 | Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state, | ||
| 389 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 390 | KMemoryAttribute attr_mask, KMemoryAttribute attr) const; | ||
| 391 | Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, | ||
| 392 | KMemoryAttribute* out_attr, size_t* out_blocks_needed, | ||
| 393 | KMemoryBlockManager::const_iterator it, KProcessAddress last_addr, | ||
| 394 | KMemoryState state_mask, KMemoryState state, | ||
| 395 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 396 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 397 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; | ||
| 398 | Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, | ||
| 399 | KMemoryAttribute* out_attr, size_t* out_blocks_needed, | ||
| 400 | KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 401 | KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 402 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 403 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; | ||
| 404 | Result CheckMemoryState(size_t* out_blocks_needed, KProcessAddress addr, size_t size, | ||
| 405 | KMemoryState state_mask, KMemoryState state, | ||
| 406 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 407 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 408 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { | ||
| 409 | R_RETURN(this->CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, | ||
| 410 | state_mask, state, perm_mask, perm, attr_mask, attr, | ||
| 411 | ignore_attr)); | ||
| 412 | } | ||
| 413 | Result CheckMemoryState(KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 414 | KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 415 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 416 | KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { | ||
| 417 | R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, | ||
| 418 | attr_mask, attr, ignore_attr)); | ||
| 419 | } | ||
| 420 | |||
| 421 | Result LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_paddr, KProcessAddress addr, | ||
| 422 | size_t size, KMemoryState state_mask, KMemoryState state, | ||
| 423 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 424 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 425 | KMemoryPermission new_perm, KMemoryAttribute lock_attr); | ||
| 426 | Result UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask, | ||
| 427 | KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 428 | KMemoryAttribute attr_mask, KMemoryAttribute attr, | ||
| 429 | KMemoryPermission new_perm, KMemoryAttribute lock_attr, | ||
| 430 | const KPageGroup* pg); | ||
| 431 | |||
| 432 | Result QueryInfoImpl(KMemoryInfo* out_info, Svc::PageInfo* out_page, | ||
| 433 | KProcessAddress address) const; | ||
| 434 | |||
| 435 | Result QueryMappingImpl(KProcessAddress* out, KPhysicalAddress address, size_t size, | ||
| 436 | Svc::MemoryState state) const; | ||
| 437 | |||
| 438 | Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address, | ||
| 439 | size_t num_pages, KMemoryPermission perm); | ||
| 440 | Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address, | ||
| 441 | const KPageGroup& pg, const KPageProperties properties, bool reuse_ll); | ||
| 442 | |||
| 443 | void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size, | ||
| 444 | const KPageGroup& pg); | ||
| 445 | |||
| 446 | Result MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages); | ||
| 447 | bool IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages); | ||
| 448 | |||
| 449 | Result GetContiguousMemoryRangeWithState(MemoryRange* out, KProcessAddress address, size_t size, | ||
| 450 | KMemoryState state_mask, KMemoryState state, | ||
| 451 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 452 | KMemoryAttribute attr_mask, KMemoryAttribute attr); | ||
| 453 | |||
| 454 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, | ||
| 455 | KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, | ||
| 456 | size_t region_num_pages, KMemoryState state, KMemoryPermission perm); | ||
| 457 | |||
| 458 | Result MapIoImpl(KProcessAddress* out, PageLinkedList* page_list, KPhysicalAddress phys_addr, | ||
| 459 | size_t size, KMemoryState state, KMemoryPermission perm); | ||
| 460 | Result ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddress phys_addr, size_t size, | ||
| 461 | KMemoryState state); | ||
| 462 | Result WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAddress src_addr, size_t size, | ||
| 463 | KMemoryState state); | ||
| 464 | |||
| 465 | Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed, | ||
| 466 | KProcessAddress address, size_t size, KMemoryPermission test_perm, | ||
| 467 | KMemoryState dst_state); | ||
| 468 | Result SetupForIpcServer(KProcessAddress* out_addr, size_t size, KProcessAddress src_addr, | ||
| 469 | KMemoryPermission test_perm, KMemoryState dst_state, | ||
| 470 | KPageTableBase& src_page_table, bool send); | ||
| 471 | void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, KProcessAddress address, | ||
| 472 | size_t size, KMemoryPermission prot_perm); | ||
| 473 | |||
| 474 | size_t GetSize(KMemoryState state) const; | ||
| 475 | |||
| 476 | bool GetPhysicalAddressLocked(KPhysicalAddress* out, KProcessAddress virt_addr) const { | ||
| 477 | // Validate pre-conditions. | ||
| 478 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 479 | |||
| 480 | return this->GetImpl().GetPhysicalAddress(out, virt_addr); | ||
| 481 | } | ||
| 482 | |||
| 483 | public: | ||
| 484 | bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress virt_addr) const { | ||
| 485 | // Validate pre-conditions. | ||
| 486 | ASSERT(!this->IsLockedByCurrentThread()); | ||
| 487 | |||
| 488 | // Acquire exclusive access to the table while doing address translation. | ||
| 489 | KScopedLightLock lk(m_general_lock); | ||
| 490 | |||
| 491 | return this->GetPhysicalAddressLocked(out, virt_addr); | ||
| 492 | } | ||
| 493 | |||
| 494 | KBlockInfoManager* GetBlockInfoManager() const { | ||
| 495 | return m_block_info_manager; | ||
| 496 | } | ||
| 497 | |||
| 498 | Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm); | ||
| 499 | Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, | ||
| 500 | Svc::MemoryPermission perm); | ||
| 501 | Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask, | ||
| 502 | KMemoryAttribute attr); | ||
| 503 | Result SetHeapSize(KProcessAddress* out, size_t size); | ||
| 504 | Result SetMaxHeapSize(size_t size); | ||
| 505 | Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info, | ||
| 506 | KProcessAddress addr) const; | ||
| 507 | Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) const; | ||
| 508 | Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const { | ||
| 509 | R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Static)); | ||
| 510 | } | ||
| 511 | Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const { | ||
| 512 | R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Io)); | ||
| 513 | } | ||
| 514 | Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); | ||
| 515 | Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); | ||
| 516 | Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); | ||
| 517 | Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); | ||
| 518 | Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm); | ||
| 519 | Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, | ||
| 520 | Svc::MemoryMapping mapping, Svc::MemoryPermission perm); | ||
| 521 | Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, | ||
| 522 | Svc::MemoryMapping mapping); | ||
| 523 | Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm); | ||
| 524 | Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm); | ||
| 525 | Result MapInsecureMemory(KProcessAddress address, size_t size); | ||
| 526 | Result UnmapInsecureMemory(KProcessAddress address, size_t size); | ||
| 527 | |||
| 528 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, | ||
| 529 | KPhysicalAddress phys_addr, KProcessAddress region_start, | ||
| 530 | size_t region_num_pages, KMemoryState state, KMemoryPermission perm) { | ||
| 531 | R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start, | ||
| 532 | region_num_pages, state, perm)); | ||
| 533 | } | ||
| 534 | |||
| 535 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, | ||
| 536 | KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) { | ||
| 537 | R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, | ||
| 538 | this->GetRegionAddress(state), | ||
| 539 | this->GetRegionSize(state) / PageSize, state, perm)); | ||
| 540 | } | ||
| 541 | |||
| 542 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state, | ||
| 543 | KMemoryPermission perm) { | ||
| 544 | R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false, | ||
| 545 | this->GetRegionAddress(state), | ||
| 546 | this->GetRegionSize(state) / PageSize, state, perm)); | ||
| 547 | } | ||
| 548 | |||
| 549 | Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, | ||
| 550 | KMemoryPermission perm); | ||
| 551 | Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state); | ||
| 552 | |||
| 553 | Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg, | ||
| 554 | KProcessAddress region_start, size_t region_num_pages, KMemoryState state, | ||
| 555 | KMemoryPermission perm); | ||
| 556 | Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state, | ||
| 557 | KMemoryPermission perm); | ||
| 558 | Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state); | ||
| 559 | |||
| 560 | Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, | ||
| 561 | KMemoryState state_mask, KMemoryState state, | ||
| 562 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 563 | KMemoryAttribute attr_mask, KMemoryAttribute attr); | ||
| 564 | |||
| 565 | Result InvalidateProcessDataCache(KProcessAddress address, size_t size); | ||
| 566 | Result InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size); | ||
| 567 | |||
| 568 | Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); | ||
| 569 | Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size, | ||
| 570 | KMemoryState state); | ||
| 571 | |||
| 572 | Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); | ||
| 573 | Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size, | ||
| 574 | KMemoryState state); | ||
| 575 | |||
| 576 | Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size, | ||
| 577 | KMemoryPermission perm, bool is_aligned, bool check_heap); | ||
| 578 | Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap); | ||
| 579 | |||
| 580 | Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size); | ||
| 581 | Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size); | ||
| 582 | |||
| 583 | Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out, | ||
| 584 | KProcessAddress address, size_t size, | ||
| 585 | KMemoryPermission perm, bool is_aligned); | ||
| 586 | Result OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange* out, KProcessAddress address, | ||
| 587 | size_t size); | ||
| 588 | |||
| 589 | Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size); | ||
| 590 | Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size); | ||
| 591 | |||
| 592 | Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, | ||
| 593 | KMemoryPermission perm); | ||
| 594 | Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg); | ||
| 595 | Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size); | ||
| 596 | Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg); | ||
| 597 | |||
| 598 | Result OpenMemoryRangeForProcessCacheOperation(MemoryRange* out, KProcessAddress address, | ||
| 599 | size_t size); | ||
| 600 | |||
| 601 | Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, | ||
| 602 | KProcessAddress src_addr, KMemoryState src_state_mask, | ||
| 603 | KMemoryState src_state, KMemoryPermission src_test_perm, | ||
| 604 | KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr); | ||
| 605 | Result CopyMemoryFromLinearToKernel(void* buffer, size_t size, KProcessAddress src_addr, | ||
| 606 | KMemoryState src_state_mask, KMemoryState src_state, | ||
| 607 | KMemoryPermission src_test_perm, | ||
| 608 | KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr); | ||
| 609 | Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size, | ||
| 610 | KMemoryState dst_state_mask, KMemoryState dst_state, | ||
| 611 | KMemoryPermission dst_test_perm, | ||
| 612 | KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, | ||
| 613 | KProcessAddress src_addr); | ||
| 614 | Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size, | ||
| 615 | KMemoryState dst_state_mask, KMemoryState dst_state, | ||
| 616 | KMemoryPermission dst_test_perm, | ||
| 617 | KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, | ||
| 618 | void* buffer); | ||
| 619 | Result CopyMemoryFromHeapToHeap(KPageTableBase& dst_page_table, KProcessAddress dst_addr, | ||
| 620 | size_t size, KMemoryState dst_state_mask, | ||
| 621 | KMemoryState dst_state, KMemoryPermission dst_test_perm, | ||
| 622 | KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, | ||
| 623 | KProcessAddress src_addr, KMemoryState src_state_mask, | ||
| 624 | KMemoryState src_state, KMemoryPermission src_test_perm, | ||
| 625 | KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr); | ||
| 626 | Result CopyMemoryFromHeapToHeapWithoutCheckDestination( | ||
| 627 | KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size, | ||
| 628 | KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm, | ||
| 629 | KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr, | ||
| 630 | KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm, | ||
| 631 | KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr); | ||
| 632 | |||
| 633 | Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr, | ||
| 634 | KPageTableBase& src_page_table, KMemoryPermission test_perm, | ||
| 635 | KMemoryState dst_state, bool send); | ||
| 636 | Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state); | ||
| 637 | Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); | ||
| 638 | |||
| 639 | Result MapPhysicalMemory(KProcessAddress address, size_t size); | ||
| 640 | Result UnmapPhysicalMemory(KProcessAddress address, size_t size); | ||
| 641 | |||
| 642 | Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size); | ||
| 643 | Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size); | ||
| 644 | |||
| 645 | Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KPageTableBase& src_pt, | ||
| 646 | KProcessAddress src_address); | ||
| 647 | |||
| 648 | public: | ||
| 649 | KProcessAddress GetAddressSpaceStart() const { | ||
| 650 | return m_address_space_start; | ||
| 651 | } | ||
| 652 | KProcessAddress GetHeapRegionStart() const { | ||
| 653 | return m_heap_region_start; | ||
| 654 | } | ||
| 655 | KProcessAddress GetAliasRegionStart() const { | ||
| 656 | return m_alias_region_start; | ||
| 657 | } | ||
| 658 | KProcessAddress GetStackRegionStart() const { | ||
| 659 | return m_stack_region_start; | ||
| 660 | } | ||
| 661 | KProcessAddress GetKernelMapRegionStart() const { | ||
| 662 | return m_kernel_map_region_start; | ||
| 663 | } | ||
| 664 | KProcessAddress GetCodeRegionStart() const { | ||
| 665 | return m_code_region_start; | ||
| 666 | } | ||
| 667 | KProcessAddress GetAliasCodeRegionStart() const { | ||
| 668 | return m_alias_code_region_start; | ||
| 669 | } | ||
| 670 | |||
| 671 | size_t GetAddressSpaceSize() const { | ||
| 672 | return m_address_space_end - m_address_space_start; | ||
| 673 | } | ||
| 674 | size_t GetHeapRegionSize() const { | ||
| 675 | return m_heap_region_end - m_heap_region_start; | ||
| 676 | } | ||
| 677 | size_t GetAliasRegionSize() const { | ||
| 678 | return m_alias_region_end - m_alias_region_start; | ||
| 679 | } | ||
| 680 | size_t GetStackRegionSize() const { | ||
| 681 | return m_stack_region_end - m_stack_region_start; | ||
| 682 | } | ||
| 683 | size_t GetKernelMapRegionSize() const { | ||
| 684 | return m_kernel_map_region_end - m_kernel_map_region_start; | ||
| 685 | } | ||
| 686 | size_t GetCodeRegionSize() const { | ||
| 687 | return m_code_region_end - m_code_region_start; | ||
| 688 | } | ||
| 689 | size_t GetAliasCodeRegionSize() const { | ||
| 690 | return m_alias_code_region_end - m_alias_code_region_start; | ||
| 691 | } | ||
| 692 | |||
| 693 | size_t GetNormalMemorySize() const { | ||
| 694 | // Lock the table. | ||
| 695 | KScopedLightLock lk(m_general_lock); | ||
| 696 | |||
| 697 | return (m_current_heap_end - m_heap_region_start) + m_mapped_physical_memory_size; | ||
| 698 | } | ||
| 699 | |||
| 700 | size_t GetCodeSize() const; | ||
| 701 | size_t GetCodeDataSize() const; | ||
| 702 | size_t GetAliasCodeSize() const; | ||
| 703 | size_t GetAliasCodeDataSize() const; | ||
| 704 | |||
| 705 | u32 GetAllocateOption() const { | ||
| 706 | return m_allocate_option; | ||
| 707 | } | ||
| 708 | |||
| 709 | u32 GetAddressSpaceWidth() const { | ||
| 710 | return m_address_space_width; | ||
| 711 | } | ||
| 712 | |||
| 713 | public: | ||
| 714 | // Linear mapped | ||
| 715 | static u8* GetLinearMappedVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) { | ||
| 716 | return kernel.System().DeviceMemory().GetPointer<u8>(addr); | ||
| 717 | } | ||
| 718 | |||
| 719 | static KPhysicalAddress GetLinearMappedPhysicalAddress(KernelCore& kernel, | ||
| 720 | KVirtualAddress addr) { | ||
| 721 | return kernel.MemoryLayout().GetLinearPhysicalAddress(addr); | ||
| 722 | } | ||
| 723 | |||
| 724 | static KVirtualAddress GetLinearMappedVirtualAddress(KernelCore& kernel, | ||
| 725 | KPhysicalAddress addr) { | ||
| 726 | return kernel.MemoryLayout().GetLinearVirtualAddress(addr); | ||
| 727 | } | ||
| 728 | |||
| 729 | // Heap | ||
| 730 | static u8* GetHeapVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) { | ||
| 731 | return kernel.System().DeviceMemory().GetPointer<u8>(addr); | ||
| 732 | } | ||
| 733 | |||
| 734 | static KPhysicalAddress GetHeapPhysicalAddress(KernelCore& kernel, KVirtualAddress addr) { | ||
| 735 | return GetLinearMappedPhysicalAddress(kernel, addr); | ||
| 736 | } | ||
| 737 | |||
| 738 | static KVirtualAddress GetHeapVirtualAddress(KernelCore& kernel, KPhysicalAddress addr) { | ||
| 739 | return GetLinearMappedVirtualAddress(kernel, addr); | ||
| 740 | } | ||
| 741 | |||
| 742 | // Member heap | ||
| 743 | u8* GetHeapVirtualPointer(KPhysicalAddress addr) { | ||
| 744 | return GetHeapVirtualPointer(m_kernel, addr); | ||
| 745 | } | ||
| 746 | |||
| 747 | KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress addr) { | ||
| 748 | return GetHeapPhysicalAddress(m_kernel, addr); | ||
| 749 | } | ||
| 750 | |||
| 751 | KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) { | ||
| 752 | return GetHeapVirtualAddress(m_kernel, addr); | ||
| 753 | } | ||
| 754 | |||
| 755 | // TODO: GetPageTableVirtualAddress | ||
| 756 | // TODO: GetPageTablePhysicalAddress | ||
| 757 | }; | ||
| 758 | |||
| 759 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 1f4b0755d..3cfb414e5 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -298,9 +298,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa | |||
| 298 | const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); | 298 | const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); |
| 299 | const bool enable_das_merge = | 299 | const bool enable_das_merge = |
| 300 | False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); | 300 | False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); |
| 301 | R_TRY(m_page_table.InitializeForProcess( | 301 | R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, |
| 302 | as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, | 302 | params.code_address, params.code_num_pages * PageSize, |
| 303 | params.code_num_pages * PageSize, m_system_resource, res_limit, this->GetMemory())); | 303 | m_system_resource, res_limit, this->GetMemory())); |
| 304 | } | 304 | } |
| 305 | ON_RESULT_FAILURE_2 { | 305 | ON_RESULT_FAILURE_2 { |
| 306 | m_page_table.Finalize(); | 306 | m_page_table.Finalize(); |
| @@ -391,9 +391,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, | |||
| 391 | const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); | 391 | const bool enable_aslr = True(params.flags & Svc::CreateProcessFlag::EnableAslr); |
| 392 | const bool enable_das_merge = | 392 | const bool enable_das_merge = |
| 393 | False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); | 393 | False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); |
| 394 | R_TRY(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge, | 394 | R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, |
| 395 | !enable_aslr, pool, params.code_address, code_size, | 395 | params.code_address, code_size, m_system_resource, res_limit, |
| 396 | m_system_resource, res_limit, this->GetMemory())); | 396 | this->GetMemory())); |
| 397 | } | 397 | } |
| 398 | ON_RESULT_FAILURE_2 { | 398 | ON_RESULT_FAILURE_2 { |
| 399 | m_page_table.Finalize(); | 399 | m_page_table.Finalize(); |
| @@ -1122,9 +1122,9 @@ Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ | |||
| 1122 | void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {} | 1122 | void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {} |
| 1123 | 1123 | ||
| 1124 | KProcess::KProcess(KernelCore& kernel) | 1124 | KProcess::KProcess(KernelCore& kernel) |
| 1125 | : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel.System()}, | 1125 | : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel}, |
| 1126 | m_state_lock{kernel}, m_list_lock{kernel}, m_cond_var{kernel.System()}, | 1126 | m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()}, |
| 1127 | m_address_arbiter{kernel.System()}, m_handle_table{kernel} {} | 1127 | m_handle_table{kernel} {} |
| 1128 | KProcess::~KProcess() = default; | 1128 | KProcess::~KProcess() = default; |
| 1129 | 1129 | ||
| 1130 | Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, | 1130 | Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, |
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index f9f755afa..8339465fd 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h | |||
| @@ -5,13 +5,14 @@ | |||
| 5 | 5 | ||
| 6 | #include <map> | 6 | #include <map> |
| 7 | 7 | ||
| 8 | #include "core/file_sys/program_metadata.h" | ||
| 8 | #include "core/hle/kernel/code_set.h" | 9 | #include "core/hle/kernel/code_set.h" |
| 9 | #include "core/hle/kernel/k_address_arbiter.h" | 10 | #include "core/hle/kernel/k_address_arbiter.h" |
| 10 | #include "core/hle/kernel/k_capabilities.h" | 11 | #include "core/hle/kernel/k_capabilities.h" |
| 11 | #include "core/hle/kernel/k_condition_variable.h" | 12 | #include "core/hle/kernel/k_condition_variable.h" |
| 12 | #include "core/hle/kernel/k_handle_table.h" | 13 | #include "core/hle/kernel/k_handle_table.h" |
| 13 | #include "core/hle/kernel/k_page_table.h" | ||
| 14 | #include "core/hle/kernel/k_page_table_manager.h" | 14 | #include "core/hle/kernel/k_page_table_manager.h" |
| 15 | #include "core/hle/kernel/k_process_page_table.h" | ||
| 15 | #include "core/hle/kernel/k_system_resource.h" | 16 | #include "core/hle/kernel/k_system_resource.h" |
| 16 | #include "core/hle/kernel/k_thread.h" | 17 | #include "core/hle/kernel/k_thread.h" |
| 17 | #include "core/hle/kernel/k_thread_local_page.h" | 18 | #include "core/hle/kernel/k_thread_local_page.h" |
| @@ -65,7 +66,7 @@ private: | |||
| 65 | using TLPIterator = TLPTree::iterator; | 66 | using TLPIterator = TLPTree::iterator; |
| 66 | 67 | ||
| 67 | private: | 68 | private: |
| 68 | KPageTable m_page_table; | 69 | KProcessPageTable m_page_table; |
| 69 | std::atomic<size_t> m_used_kernel_memory_size{}; | 70 | std::atomic<size_t> m_used_kernel_memory_size{}; |
| 70 | TLPTree m_fully_used_tlp_tree{}; | 71 | TLPTree m_fully_used_tlp_tree{}; |
| 71 | TLPTree m_partially_used_tlp_tree{}; | 72 | TLPTree m_partially_used_tlp_tree{}; |
| @@ -254,9 +255,8 @@ public: | |||
| 254 | return m_is_hbl; | 255 | return m_is_hbl; |
| 255 | } | 256 | } |
| 256 | 257 | ||
| 257 | Kernel::KMemoryManager::Direction GetAllocateOption() const { | 258 | u32 GetAllocateOption() const { |
| 258 | // TODO: property of the KPageTableBase | 259 | return m_page_table.GetAllocateOption(); |
| 259 | return KMemoryManager::Direction::FromFront; | ||
| 260 | } | 260 | } |
| 261 | 261 | ||
| 262 | ThreadList& GetThreadList() { | 262 | ThreadList& GetThreadList() { |
| @@ -295,10 +295,10 @@ public: | |||
| 295 | return m_list_lock; | 295 | return m_list_lock; |
| 296 | } | 296 | } |
| 297 | 297 | ||
| 298 | KPageTable& GetPageTable() { | 298 | KProcessPageTable& GetPageTable() { |
| 299 | return m_page_table; | 299 | return m_page_table; |
| 300 | } | 300 | } |
| 301 | const KPageTable& GetPageTable() const { | 301 | const KProcessPageTable& GetPageTable() const { |
| 302 | return m_page_table; | 302 | return m_page_table; |
| 303 | } | 303 | } |
| 304 | 304 | ||
diff --git a/src/core/hle/kernel/k_process_page_table.h b/src/core/hle/kernel/k_process_page_table.h new file mode 100644 index 000000000..b7ae5abd0 --- /dev/null +++ b/src/core/hle/kernel/k_process_page_table.h | |||
| @@ -0,0 +1,480 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/kernel/k_page_table.h" | ||
| 7 | #include "core/hle/kernel/k_scoped_lock.h" | ||
| 8 | #include "core/hle/kernel/svc_types.h" | ||
| 9 | |||
| 10 | namespace Core { | ||
| 11 | class ARM_Interface; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | |||
| 16 | class KProcessPageTable { | ||
| 17 | private: | ||
| 18 | KPageTable m_page_table; | ||
| 19 | |||
| 20 | public: | ||
| 21 | KProcessPageTable(KernelCore& kernel) : m_page_table(kernel) {} | ||
| 22 | |||
| 23 | Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, | ||
| 24 | bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, | ||
| 25 | size_t code_size, KSystemResource* system_resource, | ||
| 26 | KResourceLimit* resource_limit, Core::Memory::Memory& memory) { | ||
| 27 | R_RETURN(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge, | ||
| 28 | from_back, pool, code_address, code_size, | ||
| 29 | system_resource, resource_limit, memory)); | ||
| 30 | } | ||
| 31 | |||
| 32 | void Finalize() { | ||
| 33 | m_page_table.Finalize(); | ||
| 34 | } | ||
| 35 | |||
| 36 | Core::Memory::Memory& GetMemory() { | ||
| 37 | return m_page_table.GetMemory(); | ||
| 38 | } | ||
| 39 | |||
| 40 | Core::Memory::Memory& GetMemory() const { | ||
| 41 | return m_page_table.GetMemory(); | ||
| 42 | } | ||
| 43 | |||
| 44 | Common::PageTable& GetImpl() { | ||
| 45 | return m_page_table.GetImpl(); | ||
| 46 | } | ||
| 47 | |||
| 48 | Common::PageTable& GetImpl() const { | ||
| 49 | return m_page_table.GetImpl(); | ||
| 50 | } | ||
| 51 | |||
| 52 | size_t GetNumGuardPages() const { | ||
| 53 | return m_page_table.GetNumGuardPages(); | ||
| 54 | } | ||
| 55 | |||
| 56 | KScopedLightLock AcquireDeviceMapLock() { | ||
| 57 | return m_page_table.AcquireDeviceMapLock(); | ||
| 58 | } | ||
| 59 | |||
| 60 | Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm) { | ||
| 61 | R_RETURN(m_page_table.SetMemoryPermission(addr, size, perm)); | ||
| 62 | } | ||
| 63 | |||
| 64 | Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, | ||
| 65 | Svc::MemoryPermission perm) { | ||
| 66 | R_RETURN(m_page_table.SetProcessMemoryPermission(addr, size, perm)); | ||
| 67 | } | ||
| 68 | |||
| 69 | Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask, | ||
| 70 | KMemoryAttribute attr) { | ||
| 71 | R_RETURN(m_page_table.SetMemoryAttribute(addr, size, mask, attr)); | ||
| 72 | } | ||
| 73 | |||
| 74 | Result SetHeapSize(KProcessAddress* out, size_t size) { | ||
| 75 | R_RETURN(m_page_table.SetHeapSize(out, size)); | ||
| 76 | } | ||
| 77 | |||
| 78 | Result SetMaxHeapSize(size_t size) { | ||
| 79 | R_RETURN(m_page_table.SetMaxHeapSize(size)); | ||
| 80 | } | ||
| 81 | |||
| 82 | Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info, | ||
| 83 | KProcessAddress addr) const { | ||
| 84 | R_RETURN(m_page_table.QueryInfo(out_info, out_page_info, addr)); | ||
| 85 | } | ||
| 86 | |||
| 87 | Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) { | ||
| 88 | R_RETURN(m_page_table.QueryPhysicalAddress(out, address)); | ||
| 89 | } | ||
| 90 | |||
| 91 | Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) { | ||
| 92 | R_RETURN(m_page_table.QueryStaticMapping(out, address, size)); | ||
| 93 | } | ||
| 94 | |||
| 95 | Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) { | ||
| 96 | R_RETURN(m_page_table.QueryIoMapping(out, address, size)); | ||
| 97 | } | ||
| 98 | |||
| 99 | Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { | ||
| 100 | R_RETURN(m_page_table.MapMemory(dst_address, src_address, size)); | ||
| 101 | } | ||
| 102 | |||
| 103 | Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { | ||
| 104 | R_RETURN(m_page_table.UnmapMemory(dst_address, src_address, size)); | ||
| 105 | } | ||
| 106 | |||
| 107 | Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { | ||
| 108 | R_RETURN(m_page_table.MapCodeMemory(dst_address, src_address, size)); | ||
| 109 | } | ||
| 110 | |||
| 111 | Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { | ||
| 112 | R_RETURN(m_page_table.UnmapCodeMemory(dst_address, src_address, size)); | ||
| 113 | } | ||
| 114 | |||
| 115 | Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { | ||
| 116 | R_RETURN(m_page_table.MapIo(phys_addr, size, perm)); | ||
| 117 | } | ||
| 118 | |||
| 119 | Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, | ||
| 120 | Svc::MemoryMapping mapping, Svc::MemoryPermission perm) { | ||
| 121 | R_RETURN(m_page_table.MapIoRegion(dst_address, phys_addr, size, mapping, perm)); | ||
| 122 | } | ||
| 123 | |||
| 124 | Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, | ||
| 125 | Svc::MemoryMapping mapping) { | ||
| 126 | R_RETURN(m_page_table.UnmapIoRegion(dst_address, phys_addr, size, mapping)); | ||
| 127 | } | ||
| 128 | |||
| 129 | Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { | ||
| 130 | R_RETURN(m_page_table.MapStatic(phys_addr, size, perm)); | ||
| 131 | } | ||
| 132 | |||
| 133 | Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) { | ||
| 134 | R_RETURN(m_page_table.MapRegion(region_type, perm)); | ||
| 135 | } | ||
| 136 | |||
| 137 | Result MapInsecureMemory(KProcessAddress address, size_t size) { | ||
| 138 | R_RETURN(m_page_table.MapInsecureMemory(address, size)); | ||
| 139 | } | ||
| 140 | |||
| 141 | Result UnmapInsecureMemory(KProcessAddress address, size_t size) { | ||
| 142 | R_RETURN(m_page_table.UnmapInsecureMemory(address, size)); | ||
| 143 | } | ||
| 144 | |||
| 145 | Result MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state, | ||
| 146 | KMemoryPermission perm) { | ||
| 147 | R_RETURN(m_page_table.MapPageGroup(addr, pg, state, perm)); | ||
| 148 | } | ||
| 149 | |||
| 150 | Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state) { | ||
| 151 | R_RETURN(m_page_table.UnmapPageGroup(address, pg, state)); | ||
| 152 | } | ||
| 153 | |||
| 154 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, | ||
| 155 | KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) { | ||
| 156 | R_RETURN(m_page_table.MapPages(out_addr, num_pages, alignment, phys_addr, state, perm)); | ||
| 157 | } | ||
| 158 | |||
| 159 | Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state, | ||
| 160 | KMemoryPermission perm) { | ||
| 161 | R_RETURN(m_page_table.MapPages(out_addr, num_pages, state, perm)); | ||
| 162 | } | ||
| 163 | |||
| 164 | Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, | ||
| 165 | KMemoryPermission perm) { | ||
| 166 | R_RETURN(m_page_table.MapPages(address, num_pages, state, perm)); | ||
| 167 | } | ||
| 168 | |||
| 169 | Result UnmapPages(KProcessAddress addr, size_t num_pages, KMemoryState state) { | ||
| 170 | R_RETURN(m_page_table.UnmapPages(addr, num_pages, state)); | ||
| 171 | } | ||
| 172 | |||
| 173 | Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, | ||
| 174 | KMemoryState state_mask, KMemoryState state, | ||
| 175 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 176 | KMemoryAttribute attr_mask, KMemoryAttribute attr) { | ||
| 177 | R_RETURN(m_page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state, | ||
| 178 | perm_mask, perm, attr_mask, attr)); | ||
| 179 | } | ||
| 180 | |||
| 181 | Result InvalidateProcessDataCache(KProcessAddress address, size_t size) { | ||
| 182 | R_RETURN(m_page_table.InvalidateProcessDataCache(address, size)); | ||
| 183 | } | ||
| 184 | |||
| 185 | Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { | ||
| 186 | R_RETURN(m_page_table.ReadDebugMemory(dst_address, src_address, size)); | ||
| 187 | } | ||
| 188 | |||
| 189 | Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size, | ||
| 190 | KMemoryState state) { | ||
| 191 | R_RETURN(m_page_table.ReadDebugIoMemory(dst_address, src_address, size, state)); | ||
| 192 | } | ||
| 193 | |||
| 194 | Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { | ||
| 195 | R_RETURN(m_page_table.WriteDebugMemory(dst_address, src_address, size)); | ||
| 196 | } | ||
| 197 | |||
| 198 | Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size, | ||
| 199 | KMemoryState state) { | ||
| 200 | R_RETURN(m_page_table.WriteDebugIoMemory(dst_address, src_address, size, state)); | ||
| 201 | } | ||
| 202 | |||
| 203 | Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size, | ||
| 204 | KMemoryPermission perm, bool is_aligned, bool check_heap) { | ||
| 205 | R_RETURN(m_page_table.LockForMapDeviceAddressSpace(out_is_io, address, size, perm, | ||
| 206 | is_aligned, check_heap)); | ||
| 207 | } | ||
| 208 | |||
| 209 | Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap) { | ||
| 210 | R_RETURN(m_page_table.LockForUnmapDeviceAddressSpace(address, size, check_heap)); | ||
| 211 | } | ||
| 212 | |||
| 213 | Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) { | ||
| 214 | R_RETURN(m_page_table.UnlockForDeviceAddressSpace(address, size)); | ||
| 215 | } | ||
| 216 | |||
| 217 | Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size) { | ||
| 218 | R_RETURN(m_page_table.UnlockForDeviceAddressSpacePartialMap(address, size)); | ||
| 219 | } | ||
| 220 | |||
| 221 | Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out, | ||
| 222 | KProcessAddress address, size_t size, | ||
| 223 | KMemoryPermission perm, bool is_aligned) { | ||
| 224 | R_RETURN(m_page_table.OpenMemoryRangeForMapDeviceAddressSpace(out, address, size, perm, | ||
| 225 | is_aligned)); | ||
| 226 | } | ||
| 227 | |||
| 228 | Result OpenMemoryRangeForUnmapDeviceAddressSpace(KPageTableBase::MemoryRange* out, | ||
| 229 | KProcessAddress address, size_t size) { | ||
| 230 | R_RETURN(m_page_table.OpenMemoryRangeForUnmapDeviceAddressSpace(out, address, size)); | ||
| 231 | } | ||
| 232 | |||
| 233 | Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size) { | ||
| 234 | R_RETURN(m_page_table.LockForIpcUserBuffer(out, address, size)); | ||
| 235 | } | ||
| 236 | |||
| 237 | Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size) { | ||
| 238 | R_RETURN(m_page_table.UnlockForIpcUserBuffer(address, size)); | ||
| 239 | } | ||
| 240 | |||
| 241 | Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, | ||
| 242 | KMemoryPermission perm) { | ||
| 243 | R_RETURN(m_page_table.LockForTransferMemory(out, address, size, perm)); | ||
| 244 | } | ||
| 245 | |||
| 246 | Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg) { | ||
| 247 | R_RETURN(m_page_table.UnlockForTransferMemory(address, size, pg)); | ||
| 248 | } | ||
| 249 | |||
| 250 | Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size) { | ||
| 251 | R_RETURN(m_page_table.LockForCodeMemory(out, address, size)); | ||
| 252 | } | ||
| 253 | |||
| 254 | Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg) { | ||
| 255 | R_RETURN(m_page_table.UnlockForCodeMemory(address, size, pg)); | ||
| 256 | } | ||
| 257 | |||
| 258 | Result OpenMemoryRangeForProcessCacheOperation(KPageTableBase::MemoryRange* out, | ||
| 259 | KProcessAddress address, size_t size) { | ||
| 260 | R_RETURN(m_page_table.OpenMemoryRangeForProcessCacheOperation(out, address, size)); | ||
| 261 | } | ||
| 262 | |||
| 263 | Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, | ||
| 264 | KProcessAddress src_addr, KMemoryState src_state_mask, | ||
| 265 | KMemoryState src_state, KMemoryPermission src_test_perm, | ||
| 266 | KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) { | ||
| 267 | R_RETURN(m_page_table.CopyMemoryFromLinearToUser(dst_addr, size, src_addr, src_state_mask, | ||
| 268 | src_state, src_test_perm, src_attr_mask, | ||
| 269 | src_attr)); | ||
| 270 | } | ||
| 271 | |||
| 272 | Result CopyMemoryFromLinearToKernel(void* dst_addr, size_t size, KProcessAddress src_addr, | ||
| 273 | KMemoryState src_state_mask, KMemoryState src_state, | ||
| 274 | KMemoryPermission src_test_perm, | ||
| 275 | KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) { | ||
| 276 | R_RETURN(m_page_table.CopyMemoryFromLinearToKernel(dst_addr, size, src_addr, src_state_mask, | ||
| 277 | src_state, src_test_perm, src_attr_mask, | ||
| 278 | src_attr)); | ||
| 279 | } | ||
| 280 | |||
| 281 | Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size, | ||
| 282 | KMemoryState dst_state_mask, KMemoryState dst_state, | ||
| 283 | KMemoryPermission dst_test_perm, | ||
| 284 | KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, | ||
| 285 | KProcessAddress src_addr) { | ||
| 286 | R_RETURN(m_page_table.CopyMemoryFromUserToLinear(dst_addr, size, dst_state_mask, dst_state, | ||
| 287 | dst_test_perm, dst_attr_mask, dst_attr, | ||
| 288 | src_addr)); | ||
| 289 | } | ||
| 290 | |||
| 291 | Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size, | ||
| 292 | KMemoryState dst_state_mask, KMemoryState dst_state, | ||
| 293 | KMemoryPermission dst_test_perm, | ||
| 294 | KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, | ||
| 295 | void* src_addr) { | ||
| 296 | R_RETURN(m_page_table.CopyMemoryFromKernelToLinear(dst_addr, size, dst_state_mask, | ||
| 297 | dst_state, dst_test_perm, dst_attr_mask, | ||
| 298 | dst_attr, src_addr)); | ||
| 299 | } | ||
| 300 | |||
| 301 | Result CopyMemoryFromHeapToHeap(KProcessPageTable& dst_page_table, KProcessAddress dst_addr, | ||
| 302 | size_t size, KMemoryState dst_state_mask, | ||
| 303 | KMemoryState dst_state, KMemoryPermission dst_test_perm, | ||
| 304 | KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, | ||
| 305 | KProcessAddress src_addr, KMemoryState src_state_mask, | ||
| 306 | KMemoryState src_state, KMemoryPermission src_test_perm, | ||
| 307 | KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) { | ||
| 308 | R_RETURN(m_page_table.CopyMemoryFromHeapToHeap( | ||
| 309 | dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm, | ||
| 310 | dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm, | ||
| 311 | src_attr_mask, src_attr)); | ||
| 312 | } | ||
| 313 | |||
| 314 | Result CopyMemoryFromHeapToHeapWithoutCheckDestination( | ||
| 315 | KProcessPageTable& dst_page_table, KProcessAddress dst_addr, size_t size, | ||
| 316 | KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm, | ||
| 317 | KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr, | ||
| 318 | KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm, | ||
| 319 | KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) { | ||
| 320 | R_RETURN(m_page_table.CopyMemoryFromHeapToHeapWithoutCheckDestination( | ||
| 321 | dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm, | ||
| 322 | dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm, | ||
| 323 | src_attr_mask, src_attr)); | ||
| 324 | } | ||
| 325 | |||
| 326 | Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr, | ||
| 327 | KProcessPageTable& src_page_table, KMemoryPermission test_perm, | ||
| 328 | KMemoryState dst_state, bool send) { | ||
| 329 | R_RETURN(m_page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.m_page_table, | ||
| 330 | test_perm, dst_state, send)); | ||
| 331 | } | ||
| 332 | |||
| 333 | Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) { | ||
| 334 | R_RETURN(m_page_table.CleanupForIpcServer(address, size, dst_state)); | ||
| 335 | } | ||
| 336 | |||
| 337 | Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) { | ||
| 338 | R_RETURN(m_page_table.CleanupForIpcClient(address, size, dst_state)); | ||
| 339 | } | ||
| 340 | |||
| 341 | Result MapPhysicalMemory(KProcessAddress address, size_t size) { | ||
| 342 | R_RETURN(m_page_table.MapPhysicalMemory(address, size)); | ||
| 343 | } | ||
| 344 | |||
| 345 | Result UnmapPhysicalMemory(KProcessAddress address, size_t size) { | ||
| 346 | R_RETURN(m_page_table.UnmapPhysicalMemory(address, size)); | ||
| 347 | } | ||
| 348 | |||
| 349 | Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) { | ||
| 350 | R_RETURN(m_page_table.MapPhysicalMemoryUnsafe(address, size)); | ||
| 351 | } | ||
| 352 | |||
| 353 | Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) { | ||
| 354 | R_RETURN(m_page_table.UnmapPhysicalMemoryUnsafe(address, size)); | ||
| 355 | } | ||
| 356 | |||
| 357 | Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, | ||
| 358 | KProcessPageTable& src_page_table, KProcessAddress src_address) { | ||
| 359 | R_RETURN(m_page_table.UnmapProcessMemory(dst_address, size, src_page_table.m_page_table, | ||
| 360 | src_address)); | ||
| 361 | } | ||
| 362 | |||
| 363 | bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress address) { | ||
| 364 | return m_page_table.GetPhysicalAddress(out, address); | ||
| 365 | } | ||
| 366 | |||
| 367 | bool Contains(KProcessAddress addr, size_t size) const { | ||
| 368 | return m_page_table.Contains(addr, size); | ||
| 369 | } | ||
| 370 | |||
| 371 | bool IsInAliasRegion(KProcessAddress addr, size_t size) const { | ||
| 372 | return m_page_table.IsInAliasRegion(addr, size); | ||
| 373 | } | ||
| 374 | bool IsInHeapRegion(KProcessAddress addr, size_t size) const { | ||
| 375 | return m_page_table.IsInHeapRegion(addr, size); | ||
| 376 | } | ||
| 377 | bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const { | ||
| 378 | return m_page_table.IsInUnsafeAliasRegion(addr, size); | ||
| 379 | } | ||
| 380 | |||
| 381 | bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { | ||
| 382 | return m_page_table.CanContain(addr, size, state); | ||
| 383 | } | ||
| 384 | |||
| 385 | KProcessAddress GetAddressSpaceStart() const { | ||
| 386 | return m_page_table.GetAddressSpaceStart(); | ||
| 387 | } | ||
| 388 | KProcessAddress GetHeapRegionStart() const { | ||
| 389 | return m_page_table.GetHeapRegionStart(); | ||
| 390 | } | ||
| 391 | KProcessAddress GetAliasRegionStart() const { | ||
| 392 | return m_page_table.GetAliasRegionStart(); | ||
| 393 | } | ||
| 394 | KProcessAddress GetStackRegionStart() const { | ||
| 395 | return m_page_table.GetStackRegionStart(); | ||
| 396 | } | ||
| 397 | KProcessAddress GetKernelMapRegionStart() const { | ||
| 398 | return m_page_table.GetKernelMapRegionStart(); | ||
| 399 | } | ||
| 400 | KProcessAddress GetCodeRegionStart() const { | ||
| 401 | return m_page_table.GetCodeRegionStart(); | ||
| 402 | } | ||
| 403 | KProcessAddress GetAliasCodeRegionStart() const { | ||
| 404 | return m_page_table.GetAliasCodeRegionStart(); | ||
| 405 | } | ||
| 406 | |||
| 407 | size_t GetAddressSpaceSize() const { | ||
| 408 | return m_page_table.GetAddressSpaceSize(); | ||
| 409 | } | ||
| 410 | size_t GetHeapRegionSize() const { | ||
| 411 | return m_page_table.GetHeapRegionSize(); | ||
| 412 | } | ||
| 413 | size_t GetAliasRegionSize() const { | ||
| 414 | return m_page_table.GetAliasRegionSize(); | ||
| 415 | } | ||
| 416 | size_t GetStackRegionSize() const { | ||
| 417 | return m_page_table.GetStackRegionSize(); | ||
| 418 | } | ||
| 419 | size_t GetKernelMapRegionSize() const { | ||
| 420 | return m_page_table.GetKernelMapRegionSize(); | ||
| 421 | } | ||
| 422 | size_t GetCodeRegionSize() const { | ||
| 423 | return m_page_table.GetCodeRegionSize(); | ||
| 424 | } | ||
| 425 | size_t GetAliasCodeRegionSize() const { | ||
| 426 | return m_page_table.GetAliasCodeRegionSize(); | ||
| 427 | } | ||
| 428 | |||
| 429 | size_t GetNormalMemorySize() const { | ||
| 430 | return m_page_table.GetNormalMemorySize(); | ||
| 431 | } | ||
| 432 | |||
| 433 | size_t GetCodeSize() const { | ||
| 434 | return m_page_table.GetCodeSize(); | ||
| 435 | } | ||
| 436 | size_t GetCodeDataSize() const { | ||
| 437 | return m_page_table.GetCodeDataSize(); | ||
| 438 | } | ||
| 439 | |||
| 440 | size_t GetAliasCodeSize() const { | ||
| 441 | return m_page_table.GetAliasCodeSize(); | ||
| 442 | } | ||
| 443 | size_t GetAliasCodeDataSize() const { | ||
| 444 | return m_page_table.GetAliasCodeDataSize(); | ||
| 445 | } | ||
| 446 | |||
| 447 | u32 GetAllocateOption() const { | ||
| 448 | return m_page_table.GetAllocateOption(); | ||
| 449 | } | ||
| 450 | |||
| 451 | u32 GetAddressSpaceWidth() const { | ||
| 452 | return m_page_table.GetAddressSpaceWidth(); | ||
| 453 | } | ||
| 454 | |||
| 455 | KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) { | ||
| 456 | return m_page_table.GetHeapPhysicalAddress(address); | ||
| 457 | } | ||
| 458 | |||
| 459 | u8* GetHeapVirtualPointer(KPhysicalAddress address) { | ||
| 460 | return m_page_table.GetHeapVirtualPointer(address); | ||
| 461 | } | ||
| 462 | |||
| 463 | KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress address) { | ||
| 464 | return m_page_table.GetHeapVirtualAddress(address); | ||
| 465 | } | ||
| 466 | |||
| 467 | KBlockInfoManager* GetBlockInfoManager() { | ||
| 468 | return m_page_table.GetBlockInfoManager(); | ||
| 469 | } | ||
| 470 | |||
| 471 | KPageTable& GetBasePageTable() { | ||
| 472 | return m_page_table; | ||
| 473 | } | ||
| 474 | |||
| 475 | const KPageTable& GetBasePageTable() const { | ||
| 476 | return m_page_table; | ||
| 477 | } | ||
| 478 | }; | ||
| 479 | |||
| 480 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index c64ceb530..3ea653163 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp | |||
| @@ -383,7 +383,7 @@ Result KServerSession::SendReply(bool is_hle) { | |||
| 383 | if (event != nullptr) { | 383 | if (event != nullptr) { |
| 384 | // // Get the client process/page table. | 384 | // // Get the client process/page table. |
| 385 | // KProcess *client_process = client_thread->GetOwnerProcess(); | 385 | // KProcess *client_process = client_thread->GetOwnerProcess(); |
| 386 | // KPageTable *client_page_table = std::addressof(client_process->PageTable()); | 386 | // KProcessPageTable *client_page_table = std::addressof(client_process->PageTable()); |
| 387 | 387 | ||
| 388 | // // If we need to, reply with an async error. | 388 | // // If we need to, reply with an async error. |
| 389 | // if (R_FAILED(client_result)) { | 389 | // if (R_FAILED(client_result)) { |
diff --git a/src/core/hle/kernel/k_system_resource.cpp b/src/core/hle/kernel/k_system_resource.cpp index 07e92aa80..b51941faf 100644 --- a/src/core/hle/kernel/k_system_resource.cpp +++ b/src/core/hle/kernel/k_system_resource.cpp | |||
| @@ -40,7 +40,7 @@ Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_l | |||
| 40 | 40 | ||
| 41 | // Get resource pointer. | 41 | // Get resource pointer. |
| 42 | KPhysicalAddress resource_paddr = | 42 | KPhysicalAddress resource_paddr = |
| 43 | KPageTable::GetHeapPhysicalAddress(m_kernel.MemoryLayout(), m_resource_address); | 43 | KPageTable::GetHeapPhysicalAddress(m_kernel, m_resource_address); |
| 44 | auto* resource = | 44 | auto* resource = |
| 45 | m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr); | 45 | m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr); |
| 46 | 46 | ||
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp index 2c45b4232..a632d1634 100644 --- a/src/core/hle/kernel/k_thread_local_page.cpp +++ b/src/core/hle/kernel/k_thread_local_page.cpp | |||
| @@ -37,8 +37,8 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) { | |||
| 37 | 37 | ||
| 38 | Result KThreadLocalPage::Finalize() { | 38 | Result KThreadLocalPage::Finalize() { |
| 39 | // Get the physical address of the page. | 39 | // Get the physical address of the page. |
| 40 | const KPhysicalAddress phys_addr = m_owner->GetPageTable().GetPhysicalAddr(m_virt_addr); | 40 | KPhysicalAddress phys_addr{}; |
| 41 | ASSERT(phys_addr); | 41 | ASSERT(m_owner->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), m_virt_addr)); |
| 42 | 42 | ||
| 43 | // Unmap the page. | 43 | // Unmap the page. |
| 44 | R_TRY(m_owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); | 44 | R_TRY(m_owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); |
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp deleted file mode 100644 index 773319ad8..000000000 --- a/src/core/hle/kernel/process_capability.cpp +++ /dev/null | |||
| @@ -1,389 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <bit> | ||
| 5 | |||
| 6 | #include "common/bit_util.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/hle/kernel/k_handle_table.h" | ||
| 9 | #include "core/hle/kernel/k_page_table.h" | ||
| 10 | #include "core/hle/kernel/process_capability.h" | ||
| 11 | #include "core/hle/kernel/svc_results.h" | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | namespace { | ||
| 15 | |||
| 16 | // clang-format off | ||
| 17 | |||
| 18 | // Shift offsets for kernel capability types. | ||
| 19 | enum : u32 { | ||
| 20 | CapabilityOffset_PriorityAndCoreNum = 3, | ||
| 21 | CapabilityOffset_Syscall = 4, | ||
| 22 | CapabilityOffset_MapPhysical = 6, | ||
| 23 | CapabilityOffset_MapIO = 7, | ||
| 24 | CapabilityOffset_MapRegion = 10, | ||
| 25 | CapabilityOffset_Interrupt = 11, | ||
| 26 | CapabilityOffset_ProgramType = 13, | ||
| 27 | CapabilityOffset_KernelVersion = 14, | ||
| 28 | CapabilityOffset_HandleTableSize = 15, | ||
| 29 | CapabilityOffset_Debug = 16, | ||
| 30 | }; | ||
| 31 | |||
| 32 | // Combined mask of all parameters that may be initialized only once. | ||
| 33 | constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) | | ||
| 34 | (1U << CapabilityOffset_ProgramType) | | ||
| 35 | (1U << CapabilityOffset_KernelVersion) | | ||
| 36 | (1U << CapabilityOffset_HandleTableSize) | | ||
| 37 | (1U << CapabilityOffset_Debug); | ||
| 38 | |||
| 39 | // Packed kernel version indicating 10.4.0 | ||
| 40 | constexpr u32 PackedKernelVersion = 0x520000; | ||
| 41 | |||
| 42 | // Indicates possible types of capabilities that can be specified. | ||
| 43 | enum class CapabilityType : u32 { | ||
| 44 | Unset = 0U, | ||
| 45 | PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1, | ||
| 46 | Syscall = (1U << CapabilityOffset_Syscall) - 1, | ||
| 47 | MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1, | ||
| 48 | MapIO = (1U << CapabilityOffset_MapIO) - 1, | ||
| 49 | MapRegion = (1U << CapabilityOffset_MapRegion) - 1, | ||
| 50 | Interrupt = (1U << CapabilityOffset_Interrupt) - 1, | ||
| 51 | ProgramType = (1U << CapabilityOffset_ProgramType) - 1, | ||
| 52 | KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1, | ||
| 53 | HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1, | ||
| 54 | Debug = (1U << CapabilityOffset_Debug) - 1, | ||
| 55 | Ignorable = 0xFFFFFFFFU, | ||
| 56 | }; | ||
| 57 | |||
| 58 | // clang-format on | ||
| 59 | |||
| 60 | constexpr CapabilityType GetCapabilityType(u32 value) { | ||
| 61 | return static_cast<CapabilityType>((~value & (value + 1)) - 1); | ||
| 62 | } | ||
| 63 | |||
| 64 | u32 GetFlagBitOffset(CapabilityType type) { | ||
| 65 | const auto value = static_cast<u32>(type); | ||
| 66 | return static_cast<u32>(Common::BitSize<u32>() - static_cast<u32>(std::countl_zero(value))); | ||
| 67 | } | ||
| 68 | |||
| 69 | } // Anonymous namespace | ||
| 70 | |||
| 71 | Result ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities, | ||
| 72 | std::size_t num_capabilities, | ||
| 73 | KPageTable& page_table) { | ||
| 74 | Clear(); | ||
| 75 | |||
| 76 | // Allow all cores and priorities. | ||
| 77 | core_mask = 0xF; | ||
| 78 | priority_mask = 0xFFFFFFFFFFFFFFFF; | ||
| 79 | kernel_version = PackedKernelVersion; | ||
| 80 | |||
| 81 | return ParseCapabilities(capabilities, num_capabilities, page_table); | ||
| 82 | } | ||
| 83 | |||
| 84 | Result ProcessCapabilities::InitializeForUserProcess(const u32* capabilities, | ||
| 85 | std::size_t num_capabilities, | ||
| 86 | KPageTable& page_table) { | ||
| 87 | Clear(); | ||
| 88 | |||
| 89 | return ParseCapabilities(capabilities, num_capabilities, page_table); | ||
| 90 | } | ||
| 91 | |||
| 92 | void ProcessCapabilities::InitializeForMetadatalessProcess() { | ||
| 93 | // Allow all cores and priorities | ||
| 94 | core_mask = 0xF; | ||
| 95 | priority_mask = 0xFFFFFFFFFFFFFFFF; | ||
| 96 | kernel_version = PackedKernelVersion; | ||
| 97 | |||
| 98 | // Allow all system calls and interrupts. | ||
| 99 | svc_capabilities.set(); | ||
| 100 | interrupt_capabilities.set(); | ||
| 101 | |||
| 102 | // Allow using the maximum possible amount of handles | ||
| 103 | handle_table_size = static_cast<s32>(KHandleTable::MaxTableSize); | ||
| 104 | |||
| 105 | // Allow all debugging capabilities. | ||
| 106 | is_debuggable = true; | ||
| 107 | can_force_debug = true; | ||
| 108 | } | ||
| 109 | |||
| 110 | Result ProcessCapabilities::ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, | ||
| 111 | KPageTable& page_table) { | ||
| 112 | u32 set_flags = 0; | ||
| 113 | u32 set_svc_bits = 0; | ||
| 114 | |||
| 115 | for (std::size_t i = 0; i < num_capabilities; ++i) { | ||
| 116 | const u32 descriptor = capabilities[i]; | ||
| 117 | const auto type = GetCapabilityType(descriptor); | ||
| 118 | |||
| 119 | if (type == CapabilityType::MapPhysical) { | ||
| 120 | i++; | ||
| 121 | |||
| 122 | // The MapPhysical type uses two descriptor flags for its parameters. | ||
| 123 | // If there's only one, then there's a problem. | ||
| 124 | if (i >= num_capabilities) { | ||
| 125 | LOG_ERROR(Kernel, "Invalid combination! i={}", i); | ||
| 126 | return ResultInvalidCombination; | ||
| 127 | } | ||
| 128 | |||
| 129 | const auto size_flags = capabilities[i]; | ||
| 130 | if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) { | ||
| 131 | LOG_ERROR(Kernel, "Invalid capability type! size_flags={}", size_flags); | ||
| 132 | return ResultInvalidCombination; | ||
| 133 | } | ||
| 134 | |||
| 135 | const auto result = HandleMapPhysicalFlags(descriptor, size_flags, page_table); | ||
| 136 | if (result.IsError()) { | ||
| 137 | LOG_ERROR(Kernel, "Failed to map physical flags! descriptor={}, size_flags={}", | ||
| 138 | descriptor, size_flags); | ||
| 139 | return result; | ||
| 140 | } | ||
| 141 | } else { | ||
| 142 | const auto result = | ||
| 143 | ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, page_table); | ||
| 144 | if (result.IsError()) { | ||
| 145 | LOG_ERROR( | ||
| 146 | Kernel, | ||
| 147 | "Failed to parse capability flag! set_flags={}, set_svc_bits={}, descriptor={}", | ||
| 148 | set_flags, set_svc_bits, descriptor); | ||
| 149 | return result; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | return ResultSuccess; | ||
| 155 | } | ||
| 156 | |||
| 157 | Result ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, | ||
| 158 | KPageTable& page_table) { | ||
| 159 | const auto type = GetCapabilityType(flag); | ||
| 160 | |||
| 161 | if (type == CapabilityType::Unset) { | ||
| 162 | return ResultInvalidArgument; | ||
| 163 | } | ||
| 164 | |||
| 165 | // Bail early on ignorable entries, as one would expect, | ||
| 166 | // ignorable descriptors can be ignored. | ||
| 167 | if (type == CapabilityType::Ignorable) { | ||
| 168 | return ResultSuccess; | ||
| 169 | } | ||
| 170 | |||
| 171 | // Ensure that the give flag hasn't already been initialized before. | ||
| 172 | // If it has been, then bail. | ||
| 173 | const u32 flag_length = GetFlagBitOffset(type); | ||
| 174 | const u32 set_flag = 1U << flag_length; | ||
| 175 | if ((set_flag & set_flags & InitializeOnceMask) != 0) { | ||
| 176 | LOG_ERROR(Kernel, | ||
| 177 | "Attempted to initialize flags that may only be initialized once. set_flags={}", | ||
| 178 | set_flags); | ||
| 179 | return ResultInvalidCombination; | ||
| 180 | } | ||
| 181 | set_flags |= set_flag; | ||
| 182 | |||
| 183 | switch (type) { | ||
| 184 | case CapabilityType::PriorityAndCoreNum: | ||
| 185 | return HandlePriorityCoreNumFlags(flag); | ||
| 186 | case CapabilityType::Syscall: | ||
| 187 | return HandleSyscallFlags(set_svc_bits, flag); | ||
| 188 | case CapabilityType::MapIO: | ||
| 189 | return HandleMapIOFlags(flag, page_table); | ||
| 190 | case CapabilityType::MapRegion: | ||
| 191 | return HandleMapRegionFlags(flag, page_table); | ||
| 192 | case CapabilityType::Interrupt: | ||
| 193 | return HandleInterruptFlags(flag); | ||
| 194 | case CapabilityType::ProgramType: | ||
| 195 | return HandleProgramTypeFlags(flag); | ||
| 196 | case CapabilityType::KernelVersion: | ||
| 197 | return HandleKernelVersionFlags(flag); | ||
| 198 | case CapabilityType::HandleTableSize: | ||
| 199 | return HandleHandleTableFlags(flag); | ||
| 200 | case CapabilityType::Debug: | ||
| 201 | return HandleDebugFlags(flag); | ||
| 202 | default: | ||
| 203 | break; | ||
| 204 | } | ||
| 205 | |||
| 206 | LOG_ERROR(Kernel, "Invalid capability type! type={}", type); | ||
| 207 | return ResultInvalidArgument; | ||
| 208 | } | ||
| 209 | |||
| 210 | void ProcessCapabilities::Clear() { | ||
| 211 | svc_capabilities.reset(); | ||
| 212 | interrupt_capabilities.reset(); | ||
| 213 | |||
| 214 | core_mask = 0; | ||
| 215 | priority_mask = 0; | ||
| 216 | |||
| 217 | handle_table_size = 0; | ||
| 218 | kernel_version = 0; | ||
| 219 | |||
| 220 | program_type = ProgramType::SysModule; | ||
| 221 | |||
| 222 | is_debuggable = false; | ||
| 223 | can_force_debug = false; | ||
| 224 | } | ||
| 225 | |||
| 226 | Result ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { | ||
| 227 | if (priority_mask != 0 || core_mask != 0) { | ||
| 228 | LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}", | ||
| 229 | priority_mask, core_mask); | ||
| 230 | return ResultInvalidArgument; | ||
| 231 | } | ||
| 232 | |||
| 233 | const u32 core_num_min = (flags >> 16) & 0xFF; | ||
| 234 | const u32 core_num_max = (flags >> 24) & 0xFF; | ||
| 235 | if (core_num_min > core_num_max) { | ||
| 236 | LOG_ERROR(Kernel, "Core min is greater than core max! core_num_min={}, core_num_max={}", | ||
| 237 | core_num_min, core_num_max); | ||
| 238 | return ResultInvalidCombination; | ||
| 239 | } | ||
| 240 | |||
| 241 | const u32 priority_min = (flags >> 10) & 0x3F; | ||
| 242 | const u32 priority_max = (flags >> 4) & 0x3F; | ||
| 243 | if (priority_min > priority_max) { | ||
| 244 | LOG_ERROR(Kernel, | ||
| 245 | "Priority min is greater than priority max! priority_min={}, priority_max={}", | ||
| 246 | core_num_min, priority_max); | ||
| 247 | return ResultInvalidCombination; | ||
| 248 | } | ||
| 249 | |||
| 250 | // The switch only has 4 usable cores. | ||
| 251 | if (core_num_max >= 4) { | ||
| 252 | LOG_ERROR(Kernel, "Invalid max cores specified! core_num_max={}", core_num_max); | ||
| 253 | return ResultInvalidCoreId; | ||
| 254 | } | ||
| 255 | |||
| 256 | const auto make_mask = [](u64 min, u64 max) { | ||
| 257 | const u64 range = max - min + 1; | ||
| 258 | const u64 mask = (1ULL << range) - 1; | ||
| 259 | |||
| 260 | return mask << min; | ||
| 261 | }; | ||
| 262 | |||
| 263 | core_mask = make_mask(core_num_min, core_num_max); | ||
| 264 | priority_mask = make_mask(priority_min, priority_max); | ||
| 265 | return ResultSuccess; | ||
| 266 | } | ||
| 267 | |||
| 268 | Result ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) { | ||
| 269 | const u32 index = flags >> 29; | ||
| 270 | const u32 svc_bit = 1U << index; | ||
| 271 | |||
| 272 | // If we've already set this svc before, bail. | ||
| 273 | if ((set_svc_bits & svc_bit) != 0) { | ||
| 274 | return ResultInvalidCombination; | ||
| 275 | } | ||
| 276 | set_svc_bits |= svc_bit; | ||
| 277 | |||
| 278 | const u32 svc_mask = (flags >> 5) & 0xFFFFFF; | ||
| 279 | for (u32 i = 0; i < 24; ++i) { | ||
| 280 | const u32 svc_number = index * 24 + i; | ||
| 281 | |||
| 282 | if ((svc_mask & (1U << i)) == 0) { | ||
| 283 | continue; | ||
| 284 | } | ||
| 285 | |||
| 286 | svc_capabilities[svc_number] = true; | ||
| 287 | } | ||
| 288 | |||
| 289 | return ResultSuccess; | ||
| 290 | } | ||
| 291 | |||
| 292 | Result ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags, | ||
| 293 | KPageTable& page_table) { | ||
| 294 | // TODO(Lioncache): Implement once the memory manager can handle this. | ||
| 295 | return ResultSuccess; | ||
| 296 | } | ||
| 297 | |||
| 298 | Result ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) { | ||
| 299 | // TODO(Lioncache): Implement once the memory manager can handle this. | ||
| 300 | return ResultSuccess; | ||
| 301 | } | ||
| 302 | |||
| 303 | Result ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) { | ||
| 304 | // TODO(Lioncache): Implement once the memory manager can handle this. | ||
| 305 | return ResultSuccess; | ||
| 306 | } | ||
| 307 | |||
| 308 | Result ProcessCapabilities::HandleInterruptFlags(u32 flags) { | ||
| 309 | constexpr u32 interrupt_ignore_value = 0x3FF; | ||
| 310 | const u32 interrupt0 = (flags >> 12) & 0x3FF; | ||
| 311 | const u32 interrupt1 = (flags >> 22) & 0x3FF; | ||
| 312 | |||
| 313 | for (u32 interrupt : {interrupt0, interrupt1}) { | ||
| 314 | if (interrupt == interrupt_ignore_value) { | ||
| 315 | continue; | ||
| 316 | } | ||
| 317 | |||
| 318 | // NOTE: | ||
| 319 | // This should be checking a generic interrupt controller value | ||
| 320 | // as part of the calculation, however, given we don't currently | ||
| 321 | // emulate that, it's sufficient to mark every interrupt as defined. | ||
| 322 | |||
| 323 | if (interrupt >= interrupt_capabilities.size()) { | ||
| 324 | LOG_ERROR(Kernel, "Process interrupt capability is out of range! svc_number={}", | ||
| 325 | interrupt); | ||
| 326 | return ResultOutOfRange; | ||
| 327 | } | ||
| 328 | |||
| 329 | interrupt_capabilities[interrupt] = true; | ||
| 330 | } | ||
| 331 | |||
| 332 | return ResultSuccess; | ||
| 333 | } | ||
| 334 | |||
| 335 | Result ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { | ||
| 336 | const u32 reserved = flags >> 17; | ||
| 337 | if (reserved != 0) { | ||
| 338 | LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); | ||
| 339 | return ResultReservedUsed; | ||
| 340 | } | ||
| 341 | |||
| 342 | program_type = static_cast<ProgramType>((flags >> 14) & 0b111); | ||
| 343 | return ResultSuccess; | ||
| 344 | } | ||
| 345 | |||
| 346 | Result ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { | ||
| 347 | // Yes, the internal member variable is checked in the actual kernel here. | ||
| 348 | // This might look odd for options that are only allowed to be initialized | ||
| 349 | // just once, however the kernel has a separate initialization function for | ||
| 350 | // kernel processes and userland processes. The kernel variant sets this | ||
| 351 | // member variable ahead of time. | ||
| 352 | |||
| 353 | const u32 major_version = kernel_version >> 19; | ||
| 354 | |||
| 355 | if (major_version != 0 || flags < 0x80000) { | ||
| 356 | LOG_ERROR(Kernel, | ||
| 357 | "Kernel version is non zero or flags are too small! major_version={}, flags={}", | ||
| 358 | major_version, flags); | ||
| 359 | return ResultInvalidArgument; | ||
| 360 | } | ||
| 361 | |||
| 362 | kernel_version = flags; | ||
| 363 | return ResultSuccess; | ||
| 364 | } | ||
| 365 | |||
| 366 | Result ProcessCapabilities::HandleHandleTableFlags(u32 flags) { | ||
| 367 | const u32 reserved = flags >> 26; | ||
| 368 | if (reserved != 0) { | ||
| 369 | LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); | ||
| 370 | return ResultReservedUsed; | ||
| 371 | } | ||
| 372 | |||
| 373 | handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF); | ||
| 374 | return ResultSuccess; | ||
| 375 | } | ||
| 376 | |||
| 377 | Result ProcessCapabilities::HandleDebugFlags(u32 flags) { | ||
| 378 | const u32 reserved = flags >> 19; | ||
| 379 | if (reserved != 0) { | ||
| 380 | LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); | ||
| 381 | return ResultReservedUsed; | ||
| 382 | } | ||
| 383 | |||
| 384 | is_debuggable = (flags & 0x20000) != 0; | ||
| 385 | can_force_debug = (flags & 0x40000) != 0; | ||
| 386 | return ResultSuccess; | ||
| 387 | } | ||
| 388 | |||
| 389 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h deleted file mode 100644 index ff05dc5ff..000000000 --- a/src/core/hle/kernel/process_capability.h +++ /dev/null | |||
| @@ -1,266 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <bitset> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | union Result; | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | class KPageTable; | ||
| 15 | |||
| 16 | /// The possible types of programs that may be indicated | ||
| 17 | /// by the program type capability descriptor. | ||
| 18 | enum class ProgramType { | ||
| 19 | SysModule, | ||
| 20 | Application, | ||
| 21 | Applet, | ||
| 22 | }; | ||
| 23 | |||
| 24 | /// Handles kernel capability descriptors that are provided by | ||
| 25 | /// application metadata. These descriptors provide information | ||
| 26 | /// that alters certain parameters for kernel process instance | ||
| 27 | /// that will run said application (or applet). | ||
| 28 | /// | ||
| 29 | /// Capabilities are a sequence of flag descriptors, that indicate various | ||
| 30 | /// configurations and constraints for a particular process. | ||
| 31 | /// | ||
| 32 | /// Flag types are indicated by a sequence of set low bits. E.g. the | ||
| 33 | /// types are indicated with the low bits as follows (where x indicates "don't care"): | ||
| 34 | /// | ||
| 35 | /// - Priority and core mask : 0bxxxxxxxxxxxx0111 | ||
| 36 | /// - Allowed service call mask: 0bxxxxxxxxxxx01111 | ||
| 37 | /// - Map physical memory : 0bxxxxxxxxx0111111 | ||
| 38 | /// - Map IO memory : 0bxxxxxxxx01111111 | ||
| 39 | /// - Interrupts : 0bxxxx011111111111 | ||
| 40 | /// - Application type : 0bxx01111111111111 | ||
| 41 | /// - Kernel version : 0bx011111111111111 | ||
| 42 | /// - Handle table size : 0b0111111111111111 | ||
| 43 | /// - Debugger flags : 0b1111111111111111 | ||
| 44 | /// | ||
| 45 | /// These are essentially a bit offset subtracted by 1 to create a mask. | ||
| 46 | /// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000) | ||
| 47 | /// subtracted by one (7 -> 0b0111) | ||
| 48 | /// | ||
| 49 | /// An example of a bit layout (using the map physical layout): | ||
| 50 | /// <example> | ||
| 51 | /// The MapPhysical type indicates a sequence entry pair of: | ||
| 52 | /// | ||
| 53 | /// [initial, memory_flags], where: | ||
| 54 | /// | ||
| 55 | /// initial: | ||
| 56 | /// bits: | ||
| 57 | /// 7-24: Starting page to map memory at. | ||
| 58 | /// 25 : Indicates if the memory should be mapped as read only. | ||
| 59 | /// | ||
| 60 | /// memory_flags: | ||
| 61 | /// bits: | ||
| 62 | /// 7-20 : Number of pages to map | ||
| 63 | /// 21-25: Seems to be reserved (still checked against though) | ||
| 64 | /// 26 : Whether or not the memory being mapped is IO memory, or physical memory | ||
| 65 | /// </example> | ||
| 66 | /// | ||
| 67 | class ProcessCapabilities { | ||
| 68 | public: | ||
| 69 | using InterruptCapabilities = std::bitset<1024>; | ||
| 70 | using SyscallCapabilities = std::bitset<192>; | ||
| 71 | |||
| 72 | ProcessCapabilities() = default; | ||
| 73 | ProcessCapabilities(const ProcessCapabilities&) = delete; | ||
| 74 | ProcessCapabilities(ProcessCapabilities&&) = default; | ||
| 75 | |||
| 76 | ProcessCapabilities& operator=(const ProcessCapabilities&) = delete; | ||
| 77 | ProcessCapabilities& operator=(ProcessCapabilities&&) = default; | ||
| 78 | |||
| 79 | /// Initializes this process capabilities instance for a kernel process. | ||
| 80 | /// | ||
| 81 | /// @param capabilities The capabilities to parse | ||
| 82 | /// @param num_capabilities The number of capabilities to parse. | ||
| 83 | /// @param page_table The memory manager to use for handling any mapping-related | ||
| 84 | /// operations (such as mapping IO memory, etc). | ||
| 85 | /// | ||
| 86 | /// @returns ResultSuccess if this capabilities instance was able to be initialized, | ||
| 87 | /// otherwise, an error code upon failure. | ||
| 88 | /// | ||
| 89 | Result InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities, | ||
| 90 | KPageTable& page_table); | ||
| 91 | |||
| 92 | /// Initializes this process capabilities instance for a userland process. | ||
| 93 | /// | ||
| 94 | /// @param capabilities The capabilities to parse. | ||
| 95 | /// @param num_capabilities The total number of capabilities to parse. | ||
| 96 | /// @param page_table The memory manager to use for handling any mapping-related | ||
| 97 | /// operations (such as mapping IO memory, etc). | ||
| 98 | /// | ||
| 99 | /// @returns ResultSuccess if this capabilities instance was able to be initialized, | ||
| 100 | /// otherwise, an error code upon failure. | ||
| 101 | /// | ||
| 102 | Result InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities, | ||
| 103 | KPageTable& page_table); | ||
| 104 | |||
| 105 | /// Initializes this process capabilities instance for a process that does not | ||
| 106 | /// have any metadata to parse. | ||
| 107 | /// | ||
| 108 | /// This is necessary, as we allow running raw executables, and the internal | ||
| 109 | /// kernel process capabilities also determine what CPU cores the process is | ||
| 110 | /// allowed to run on, and what priorities are allowed for threads. It also | ||
| 111 | /// determines the max handle table size, what the program type is, whether or | ||
| 112 | /// not the process can be debugged, or whether it's possible for a process to | ||
| 113 | /// forcibly debug another process. | ||
| 114 | /// | ||
| 115 | /// Given the above, this essentially enables all capabilities across the board | ||
| 116 | /// for the process. It allows the process to: | ||
| 117 | /// | ||
| 118 | /// - Run on any core | ||
| 119 | /// - Use any thread priority | ||
| 120 | /// - Use the maximum amount of handles a process is allowed to. | ||
| 121 | /// - Be debuggable | ||
| 122 | /// - Forcibly debug other processes. | ||
| 123 | /// | ||
| 124 | /// Note that this is not a behavior that the kernel allows a process to do via | ||
| 125 | /// a single function like this. This is yuzu-specific behavior to handle | ||
| 126 | /// executables with no capability descriptors whatsoever to derive behavior from. | ||
| 127 | /// It being yuzu-specific is why this is also not the default behavior and not | ||
| 128 | /// done by default in the constructor. | ||
| 129 | /// | ||
| 130 | void InitializeForMetadatalessProcess(); | ||
| 131 | |||
| 132 | /// Gets the allowable core mask | ||
| 133 | u64 GetCoreMask() const { | ||
| 134 | return core_mask; | ||
| 135 | } | ||
| 136 | |||
| 137 | /// Gets the allowable priority mask | ||
| 138 | u64 GetPriorityMask() const { | ||
| 139 | return priority_mask; | ||
| 140 | } | ||
| 141 | |||
| 142 | /// Gets the SVC access permission bits | ||
| 143 | const SyscallCapabilities& GetServiceCapabilities() const { | ||
| 144 | return svc_capabilities; | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Gets the valid interrupt bits. | ||
| 148 | const InterruptCapabilities& GetInterruptCapabilities() const { | ||
| 149 | return interrupt_capabilities; | ||
| 150 | } | ||
| 151 | |||
| 152 | /// Gets the program type for this process. | ||
| 153 | ProgramType GetProgramType() const { | ||
| 154 | return program_type; | ||
| 155 | } | ||
| 156 | |||
| 157 | /// Gets the number of total allowable handles for the process' handle table. | ||
| 158 | s32 GetHandleTableSize() const { | ||
| 159 | return handle_table_size; | ||
| 160 | } | ||
| 161 | |||
| 162 | /// Gets the kernel version value. | ||
| 163 | u32 GetKernelVersion() const { | ||
| 164 | return kernel_version; | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Whether or not this process can be debugged. | ||
| 168 | bool IsDebuggable() const { | ||
| 169 | return is_debuggable; | ||
| 170 | } | ||
| 171 | |||
| 172 | /// Whether or not this process can forcibly debug another | ||
| 173 | /// process, even if that process is not considered debuggable. | ||
| 174 | bool CanForceDebug() const { | ||
| 175 | return can_force_debug; | ||
| 176 | } | ||
| 177 | |||
| 178 | private: | ||
| 179 | /// Attempts to parse a given sequence of capability descriptors. | ||
| 180 | /// | ||
| 181 | /// @param capabilities The sequence of capability descriptors to parse. | ||
| 182 | /// @param num_capabilities The number of descriptors within the given sequence. | ||
| 183 | /// @param page_table The memory manager that will perform any memory | ||
| 184 | /// mapping if necessary. | ||
| 185 | /// | ||
| 186 | /// @return ResultSuccess if no errors occur, otherwise an error code. | ||
| 187 | /// | ||
| 188 | Result ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, | ||
| 189 | KPageTable& page_table); | ||
| 190 | |||
| 191 | /// Attempts to parse a capability descriptor that is only represented by a | ||
| 192 | /// single flag set. | ||
| 193 | /// | ||
| 194 | /// @param set_flags Running set of flags that are used to catch | ||
| 195 | /// flags being initialized more than once when they shouldn't be. | ||
| 196 | /// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask. | ||
| 197 | /// @param flag The flag to attempt to parse. | ||
| 198 | /// @param page_table The memory manager that will perform any memory | ||
| 199 | /// mapping if necessary. | ||
| 200 | /// | ||
| 201 | /// @return ResultSuccess if no errors occurred, otherwise an error code. | ||
| 202 | /// | ||
| 203 | Result ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, | ||
| 204 | KPageTable& page_table); | ||
| 205 | |||
| 206 | /// Clears the internal state of this process capability instance. Necessary, | ||
| 207 | /// to have a sane starting point due to us allowing running executables without | ||
| 208 | /// configuration metadata. We assume a process is not going to have metadata, | ||
| 209 | /// and if it turns out that the process does, in fact, have metadata, then | ||
| 210 | /// we attempt to parse it. Thus, we need this to reset data members back to | ||
| 211 | /// a good state. | ||
| 212 | /// | ||
| 213 | /// DO NOT ever make this a public member function. This isn't an invariant | ||
| 214 | /// anything external should depend upon (and if anything comes to rely on it, | ||
| 215 | /// you should immediately be questioning the design of that thing, not this | ||
| 216 | /// class. If the kernel itself can run without depending on behavior like that, | ||
| 217 | /// then so can yuzu). | ||
| 218 | /// | ||
| 219 | void Clear(); | ||
| 220 | |||
| 221 | /// Handles flags related to the priority and core number capability flags. | ||
| 222 | Result HandlePriorityCoreNumFlags(u32 flags); | ||
| 223 | |||
| 224 | /// Handles flags related to determining the allowable SVC mask. | ||
| 225 | Result HandleSyscallFlags(u32& set_svc_bits, u32 flags); | ||
| 226 | |||
| 227 | /// Handles flags related to mapping physical memory pages. | ||
| 228 | Result HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table); | ||
| 229 | |||
| 230 | /// Handles flags related to mapping IO pages. | ||
| 231 | Result HandleMapIOFlags(u32 flags, KPageTable& page_table); | ||
| 232 | |||
| 233 | /// Handles flags related to mapping physical memory regions. | ||
| 234 | Result HandleMapRegionFlags(u32 flags, KPageTable& page_table); | ||
| 235 | |||
| 236 | /// Handles flags related to the interrupt capability flags. | ||
| 237 | Result HandleInterruptFlags(u32 flags); | ||
| 238 | |||
| 239 | /// Handles flags related to the program type. | ||
| 240 | Result HandleProgramTypeFlags(u32 flags); | ||
| 241 | |||
| 242 | /// Handles flags related to the handle table size. | ||
| 243 | Result HandleHandleTableFlags(u32 flags); | ||
| 244 | |||
| 245 | /// Handles flags related to the kernel version capability flags. | ||
| 246 | Result HandleKernelVersionFlags(u32 flags); | ||
| 247 | |||
| 248 | /// Handles flags related to debug-specific capabilities. | ||
| 249 | Result HandleDebugFlags(u32 flags); | ||
| 250 | |||
| 251 | SyscallCapabilities svc_capabilities; | ||
| 252 | InterruptCapabilities interrupt_capabilities; | ||
| 253 | |||
| 254 | u64 core_mask = 0; | ||
| 255 | u64 priority_mask = 0; | ||
| 256 | |||
| 257 | s32 handle_table_size = 0; | ||
| 258 | u32 kernel_version = 0; | ||
| 259 | |||
| 260 | ProgramType program_type = ProgramType::SysModule; | ||
| 261 | |||
| 262 | bool is_debuggable = false; | ||
| 263 | bool can_force_debug = false; | ||
| 264 | }; | ||
| 265 | |||
| 266 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp index 97f1210de..4ca62860d 100644 --- a/src/core/hle/kernel/svc/svc_memory.cpp +++ b/src/core/hle/kernel/svc/svc_memory.cpp | |||
| @@ -29,7 +29,8 @@ constexpr bool IsValidAddressRange(u64 address, u64 size) { | |||
| 29 | // Helper function that performs the common sanity checks for svcMapMemory | 29 | // Helper function that performs the common sanity checks for svcMapMemory |
| 30 | // and svcUnmapMemory. This is doable, as both functions perform their sanitizing | 30 | // and svcUnmapMemory. This is doable, as both functions perform their sanitizing |
| 31 | // in the same order. | 31 | // in the same order. |
| 32 | Result MapUnmapMemorySanityChecks(const KPageTable& manager, u64 dst_addr, u64 src_addr, u64 size) { | 32 | Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr, u64 src_addr, |
| 33 | u64 size) { | ||
| 33 | if (!Common::Is4KBAligned(dst_addr)) { | 34 | if (!Common::Is4KBAligned(dst_addr)) { |
| 34 | LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); | 35 | LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); |
| 35 | R_THROW(ResultInvalidAddress); | 36 | R_THROW(ResultInvalidAddress); |
| @@ -123,7 +124,8 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask, | |||
| 123 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); | 124 | R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); |
| 124 | 125 | ||
| 125 | // Set the memory attribute. | 126 | // Set the memory attribute. |
| 126 | R_RETURN(page_table.SetMemoryAttribute(address, size, mask, attr)); | 127 | R_RETURN(page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask), |
| 128 | static_cast<KMemoryAttribute>(attr))); | ||
| 127 | } | 129 | } |
| 128 | 130 | ||
| 129 | /// Maps a memory range into a different range. | 131 | /// Maps a memory range into a different range. |
diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp index 99330d02a..793e9f8d0 100644 --- a/src/core/hle/kernel/svc/svc_physical_memory.cpp +++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp | |||
| @@ -16,7 +16,14 @@ Result SetHeapSize(Core::System& system, u64* out_address, u64 size) { | |||
| 16 | R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); | 16 | R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); |
| 17 | 17 | ||
| 18 | // Set the heap size. | 18 | // Set the heap size. |
| 19 | R_RETURN(GetCurrentProcess(system.Kernel()).GetPageTable().SetHeapSize(out_address, size)); | 19 | KProcessAddress address{}; |
| 20 | R_TRY(GetCurrentProcess(system.Kernel()) | ||
| 21 | .GetPageTable() | ||
| 22 | .SetHeapSize(std::addressof(address), size)); | ||
| 23 | |||
| 24 | // We succeeded. | ||
| 25 | *out_address = GetInteger(address); | ||
| 26 | R_SUCCEED(); | ||
| 20 | } | 27 | } |
| 21 | 28 | ||
| 22 | /// Maps memory at a desired address | 29 | /// Maps memory at a desired address |
diff --git a/src/core/hle/kernel/svc/svc_process_memory.cpp b/src/core/hle/kernel/svc/svc_process_memory.cpp index 07cd48175..e1427947b 100644 --- a/src/core/hle/kernel/svc/svc_process_memory.cpp +++ b/src/core/hle/kernel/svc/svc_process_memory.cpp | |||
| @@ -247,8 +247,7 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d | |||
| 247 | R_THROW(ResultInvalidCurrentMemory); | 247 | R_THROW(ResultInvalidCurrentMemory); |
| 248 | } | 248 | } |
| 249 | 249 | ||
| 250 | R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size, | 250 | R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size)); |
| 251 | KPageTable::ICacheInvalidationStrategy::InvalidateAll)); | ||
| 252 | } | 251 | } |
| 253 | 252 | ||
| 254 | Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address, | 253 | Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address, |
diff --git a/src/core/hle/kernel/svc/svc_query_memory.cpp b/src/core/hle/kernel/svc/svc_query_memory.cpp index 51af06e97..816dcb8d0 100644 --- a/src/core/hle/kernel/svc/svc_query_memory.cpp +++ b/src/core/hle/kernel/svc/svc_query_memory.cpp | |||
| @@ -31,12 +31,12 @@ Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageIn | |||
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | auto& current_memory{GetCurrentMemory(system.Kernel())}; | 33 | auto& current_memory{GetCurrentMemory(system.Kernel())}; |
| 34 | const auto memory_info{process->GetPageTable().QueryInfo(address).GetSvcMemoryInfo()}; | ||
| 35 | 34 | ||
| 36 | current_memory.WriteBlock(out_memory_info, std::addressof(memory_info), sizeof(memory_info)); | 35 | KMemoryInfo mem_info; |
| 36 | R_TRY(process->GetPageTable().QueryInfo(std::addressof(mem_info), out_page_info, address)); | ||
| 37 | 37 | ||
| 38 | //! This is supposed to be part of the QueryInfo call. | 38 | const auto svc_mem_info = mem_info.GetSvcMemoryInfo(); |
| 39 | *out_page_info = {}; | 39 | current_memory.WriteBlock(out_memory_info, std::addressof(svc_mem_info), sizeof(svc_mem_info)); |
| 40 | 40 | ||
| 41 | R_SUCCEED(); | 41 | R_SUCCEED(); |
| 42 | } | 42 | } |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index dd0b27f47..749f51f69 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -407,3 +407,34 @@ constexpr inline Result __TmpCurrentResultReference = ResultSuccess; | |||
| 407 | 407 | ||
| 408 | /// Evaluates a boolean expression, and succeeds if that expression is true. | 408 | /// Evaluates a boolean expression, and succeeds if that expression is true. |
| 409 | #define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) | 409 | #define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess) |
| 410 | |||
| 411 | #define R_TRY_CATCH(res_expr) \ | ||
| 412 | { \ | ||
| 413 | const auto R_CURRENT_RESULT = (res_expr); \ | ||
| 414 | if (R_FAILED(R_CURRENT_RESULT)) { \ | ||
| 415 | if (false) | ||
| 416 | |||
| 417 | #define R_END_TRY_CATCH \ | ||
| 418 | else if (R_FAILED(R_CURRENT_RESULT)) { \ | ||
| 419 | R_THROW(R_CURRENT_RESULT); \ | ||
| 420 | } \ | ||
| 421 | } \ | ||
| 422 | } | ||
| 423 | |||
| 424 | #define R_CATCH_ALL() \ | ||
| 425 | } \ | ||
| 426 | else if (R_FAILED(R_CURRENT_RESULT)) { \ | ||
| 427 | if (true) | ||
| 428 | |||
| 429 | #define R_CATCH(res_expr) \ | ||
| 430 | } \ | ||
| 431 | else if ((res_expr) == (R_CURRENT_RESULT)) { \ | ||
| 432 | if (true) | ||
| 433 | |||
| 434 | #define R_CONVERT(catch_type, convert_type) \ | ||
| 435 | R_CATCH(catch_type) { R_THROW(static_cast<Result>(convert_type)); } | ||
| 436 | |||
| 437 | #define R_CONVERT_ALL(convert_type) \ | ||
| 438 | R_CATCH_ALL() { R_THROW(static_cast<Result>(convert_type)); } | ||
| 439 | |||
| 440 | #define R_ASSERT(res_expr) ASSERT(R_SUCCEEDED(res_expr)) | ||
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 1b1c8190e..f21553644 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -3,11 +3,13 @@ | |||
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include <algorithm> |
| 5 | #include <array> | 5 | #include <array> |
| 6 | |||
| 6 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 7 | #include "common/fs/file.h" | 8 | #include "common/fs/file.h" |
| 8 | #include "common/fs/path_util.h" | 9 | #include "common/fs/path_util.h" |
| 9 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 10 | #include "common/polyfill_ranges.h" | 11 | #include "common/polyfill_ranges.h" |
| 12 | #include "common/stb.h" | ||
| 11 | #include "common/string_util.h" | 13 | #include "common/string_util.h" |
| 12 | #include "common/swap.h" | 14 | #include "common/swap.h" |
| 13 | #include "core/constants.h" | 15 | #include "core/constants.h" |
| @@ -38,9 +40,36 @@ static std::filesystem::path GetImagePath(const Common::UUID& uuid) { | |||
| 38 | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); | 40 | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); |
| 39 | } | 41 | } |
| 40 | 42 | ||
| 41 | static constexpr u32 SanitizeJPEGSize(std::size_t size) { | 43 | static void JPGToMemory(void* context, void* data, int len) { |
| 44 | std::vector<u8>* jpg_image = static_cast<std::vector<u8>*>(context); | ||
| 45 | unsigned char* jpg = static_cast<unsigned char*>(data); | ||
| 46 | jpg_image->insert(jpg_image->end(), jpg, jpg + len); | ||
| 47 | } | ||
| 48 | |||
| 49 | static void SanitizeJPEGImageSize(std::vector<u8>& image) { | ||
| 42 | constexpr std::size_t max_jpeg_image_size = 0x20000; | 50 | constexpr std::size_t max_jpeg_image_size = 0x20000; |
| 43 | return static_cast<u32>(std::min(size, max_jpeg_image_size)); | 51 | constexpr int profile_dimensions = 256; |
| 52 | int original_width, original_height, color_channels; | ||
| 53 | |||
| 54 | const auto plain_image = | ||
| 55 | stbi_load_from_memory(image.data(), static_cast<int>(image.size()), &original_width, | ||
| 56 | &original_height, &color_channels, STBI_rgb); | ||
| 57 | |||
| 58 | // Resize image to match 256*256 | ||
| 59 | if (original_width != profile_dimensions || original_height != profile_dimensions) { | ||
| 60 | // Use vector instead of array to avoid overflowing the stack | ||
| 61 | std::vector<u8> out_image(profile_dimensions * profile_dimensions * STBI_rgb); | ||
| 62 | stbir_resize_uint8_srgb(plain_image, original_width, original_height, 0, out_image.data(), | ||
| 63 | profile_dimensions, profile_dimensions, 0, STBI_rgb, 0, | ||
| 64 | STBIR_FILTER_BOX); | ||
| 65 | image.clear(); | ||
| 66 | if (!stbi_write_jpg_to_func(JPGToMemory, &image, profile_dimensions, profile_dimensions, | ||
| 67 | STBI_rgb, out_image.data(), 0)) { | ||
| 68 | LOG_ERROR(Service_ACC, "Failed to resize the user provided image."); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | image.resize(std::min(image.size(), max_jpeg_image_size)); | ||
| 44 | } | 73 | } |
| 45 | 74 | ||
| 46 | class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> { | 75 | class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> { |
| @@ -339,19 +368,20 @@ protected: | |||
| 339 | LOG_WARNING(Service_ACC, | 368 | LOG_WARNING(Service_ACC, |
| 340 | "Failed to load user provided image! Falling back to built-in backup..."); | 369 | "Failed to load user provided image! Falling back to built-in backup..."); |
| 341 | ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG); | 370 | ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG); |
| 342 | rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); | 371 | rb.Push(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); |
| 343 | return; | 372 | return; |
| 344 | } | 373 | } |
| 345 | 374 | ||
| 346 | const u32 size = SanitizeJPEGSize(image.GetSize()); | 375 | std::vector<u8> buffer(image.GetSize()); |
| 347 | std::vector<u8> buffer(size); | ||
| 348 | 376 | ||
| 349 | if (image.Read(buffer) != buffer.size()) { | 377 | if (image.Read(buffer) != buffer.size()) { |
| 350 | LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); | 378 | LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); |
| 351 | } | 379 | } |
| 352 | 380 | ||
| 381 | SanitizeJPEGImageSize(buffer); | ||
| 382 | |||
| 353 | ctx.WriteBuffer(buffer); | 383 | ctx.WriteBuffer(buffer); |
| 354 | rb.Push<u32>(size); | 384 | rb.Push(static_cast<u32>(buffer.size())); |
| 355 | } | 385 | } |
| 356 | 386 | ||
| 357 | void GetImageSize(HLERequestContext& ctx) { | 387 | void GetImageSize(HLERequestContext& ctx) { |
| @@ -365,10 +395,18 @@ protected: | |||
| 365 | if (!image.IsOpen()) { | 395 | if (!image.IsOpen()) { |
| 366 | LOG_WARNING(Service_ACC, | 396 | LOG_WARNING(Service_ACC, |
| 367 | "Failed to load user provided image! Falling back to built-in backup..."); | 397 | "Failed to load user provided image! Falling back to built-in backup..."); |
| 368 | rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); | 398 | rb.Push(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); |
| 369 | } else { | 399 | return; |
| 370 | rb.Push(SanitizeJPEGSize(image.GetSize())); | ||
| 371 | } | 400 | } |
| 401 | |||
| 402 | std::vector<u8> buffer(image.GetSize()); | ||
| 403 | |||
| 404 | if (image.Read(buffer) != buffer.size()) { | ||
| 405 | LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); | ||
| 406 | } | ||
| 407 | |||
| 408 | SanitizeJPEGImageSize(buffer); | ||
| 409 | rb.Push(static_cast<u32>(buffer.size())); | ||
| 372 | } | 410 | } |
| 373 | 411 | ||
| 374 | void Store(HLERequestContext& ctx) { | 412 | void Store(HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index cc643ea09..a266d7c21 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "core/file_sys/patch_manager.h" | 13 | #include "core/file_sys/patch_manager.h" |
| 14 | #include "core/file_sys/registered_cache.h" | 14 | #include "core/file_sys/registered_cache.h" |
| 15 | #include "core/file_sys/savedata_factory.h" | 15 | #include "core/file_sys/savedata_factory.h" |
| 16 | #include "core/hid/hid_types.h" | ||
| 16 | #include "core/hle/kernel/k_event.h" | 17 | #include "core/hle/kernel/k_event.h" |
| 17 | #include "core/hle/kernel/k_transfer_memory.h" | 18 | #include "core/hle/kernel/k_transfer_memory.h" |
| 18 | #include "core/hle/result.h" | 19 | #include "core/hle/result.h" |
| @@ -21,6 +22,7 @@ | |||
| 21 | #include "core/hle/service/am/applet_ae.h" | 22 | #include "core/hle/service/am/applet_ae.h" |
| 22 | #include "core/hle/service/am/applet_oe.h" | 23 | #include "core/hle/service/am/applet_oe.h" |
| 23 | #include "core/hle/service/am/applets/applet_cabinet.h" | 24 | #include "core/hle/service/am/applets/applet_cabinet.h" |
| 25 | #include "core/hle/service/am/applets/applet_controller.h" | ||
| 24 | #include "core/hle/service/am/applets/applet_mii_edit_types.h" | 26 | #include "core/hle/service/am/applets/applet_mii_edit_types.h" |
| 25 | #include "core/hle/service/am/applets/applet_profile_select.h" | 27 | #include "core/hle/service/am/applets/applet_profile_select.h" |
| 26 | #include "core/hle/service/am/applets/applet_software_keyboard_types.h" | 28 | #include "core/hle/service/am/applets/applet_software_keyboard_types.h" |
| @@ -35,6 +37,7 @@ | |||
| 35 | #include "core/hle/service/caps/caps_su.h" | 37 | #include "core/hle/service/caps/caps_su.h" |
| 36 | #include "core/hle/service/caps/caps_types.h" | 38 | #include "core/hle/service/caps/caps_types.h" |
| 37 | #include "core/hle/service/filesystem/filesystem.h" | 39 | #include "core/hle/service/filesystem/filesystem.h" |
| 40 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 38 | #include "core/hle/service/ipc_helpers.h" | 41 | #include "core/hle/service/ipc_helpers.h" |
| 39 | #include "core/hle/service/ns/ns.h" | 42 | #include "core/hle/service/ns/ns.h" |
| 40 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" | 43 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" |
| @@ -73,7 +76,7 @@ IWindowController::IWindowController(Core::System& system_) | |||
| 73 | static const FunctionInfo functions[] = { | 76 | static const FunctionInfo functions[] = { |
| 74 | {0, nullptr, "CreateWindow"}, | 77 | {0, nullptr, "CreateWindow"}, |
| 75 | {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, | 78 | {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, |
| 76 | {2, nullptr, "GetAppletResourceUserIdOfCallerApplet"}, | 79 | {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"}, |
| 77 | {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, | 80 | {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, |
| 78 | {11, nullptr, "ReleaseForegroundRights"}, | 81 | {11, nullptr, "ReleaseForegroundRights"}, |
| 79 | {12, nullptr, "RejectToChangeIntoBackground"}, | 82 | {12, nullptr, "RejectToChangeIntoBackground"}, |
| @@ -97,6 +100,16 @@ void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) { | |||
| 97 | rb.Push<u64>(process_id); | 100 | rb.Push<u64>(process_id); |
| 98 | } | 101 | } |
| 99 | 102 | ||
| 103 | void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) { | ||
| 104 | const u64 process_id = 0; | ||
| 105 | |||
| 106 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 107 | |||
| 108 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 109 | rb.Push(ResultSuccess); | ||
| 110 | rb.Push<u64>(process_id); | ||
| 111 | } | ||
| 112 | |||
| 100 | void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) { | 113 | void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) { |
| 101 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 114 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 102 | IPC::ResponseBuilder rb{ctx, 2}; | 115 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -1565,7 +1578,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) | |||
| 1565 | {6, nullptr, "GetPopInteractiveInDataEvent"}, | 1578 | {6, nullptr, "GetPopInteractiveInDataEvent"}, |
| 1566 | {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"}, | 1579 | {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"}, |
| 1567 | {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"}, | 1580 | {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"}, |
| 1568 | {12, nullptr, "GetMainAppletIdentityInfo"}, | 1581 | {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"}, |
| 1569 | {13, nullptr, "CanUseApplicationCore"}, | 1582 | {13, nullptr, "CanUseApplicationCore"}, |
| 1570 | {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"}, | 1583 | {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"}, |
| 1571 | {15, nullptr, "GetMainAppletApplicationControlProperty"}, | 1584 | {15, nullptr, "GetMainAppletApplicationControlProperty"}, |
| @@ -1609,6 +1622,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) | |||
| 1609 | case Applets::AppletId::SoftwareKeyboard: | 1622 | case Applets::AppletId::SoftwareKeyboard: |
| 1610 | PushInShowSoftwareKeyboard(); | 1623 | PushInShowSoftwareKeyboard(); |
| 1611 | break; | 1624 | break; |
| 1625 | case Applets::AppletId::Controller: | ||
| 1626 | PushInShowController(); | ||
| 1627 | break; | ||
| 1612 | default: | 1628 | default: |
| 1613 | break; | 1629 | break; |
| 1614 | } | 1630 | } |
| @@ -1666,13 +1682,33 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) { | |||
| 1666 | rb.PushRaw(applet_info); | 1682 | rb.PushRaw(applet_info); |
| 1667 | } | 1683 | } |
| 1668 | 1684 | ||
| 1669 | void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { | 1685 | void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) { |
| 1670 | struct AppletIdentityInfo { | 1686 | struct AppletIdentityInfo { |
| 1671 | Applets::AppletId applet_id; | 1687 | Applets::AppletId applet_id; |
| 1672 | INSERT_PADDING_BYTES(0x4); | 1688 | INSERT_PADDING_BYTES(0x4); |
| 1673 | u64 application_id; | 1689 | u64 application_id; |
| 1674 | }; | 1690 | }; |
| 1691 | static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size."); | ||
| 1692 | |||
| 1693 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 1694 | |||
| 1695 | const AppletIdentityInfo applet_info{ | ||
| 1696 | .applet_id = Applets::AppletId::QLaunch, | ||
| 1697 | .application_id = 0x0100000000001000ull, | ||
| 1698 | }; | ||
| 1699 | |||
| 1700 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 1701 | rb.Push(ResultSuccess); | ||
| 1702 | rb.PushRaw(applet_info); | ||
| 1703 | } | ||
| 1675 | 1704 | ||
| 1705 | void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { | ||
| 1706 | struct AppletIdentityInfo { | ||
| 1707 | Applets::AppletId applet_id; | ||
| 1708 | INSERT_PADDING_BYTES(0x4); | ||
| 1709 | u64 application_id; | ||
| 1710 | }; | ||
| 1711 | static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size."); | ||
| 1676 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 1712 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 1677 | 1713 | ||
| 1678 | const AppletIdentityInfo applet_info{ | 1714 | const AppletIdentityInfo applet_info{ |
| @@ -1737,6 +1773,55 @@ void ILibraryAppletSelfAccessor::PushInShowAlbum() { | |||
| 1737 | queue_data.emplace_back(std::move(settings_data)); | 1773 | queue_data.emplace_back(std::move(settings_data)); |
| 1738 | } | 1774 | } |
| 1739 | 1775 | ||
| 1776 | void ILibraryAppletSelfAccessor::PushInShowController() { | ||
| 1777 | const Applets::CommonArguments common_args = { | ||
| 1778 | .arguments_version = Applets::CommonArgumentVersion::Version3, | ||
| 1779 | .size = Applets::CommonArgumentSize::Version3, | ||
| 1780 | .library_version = static_cast<u32>(Applets::ControllerAppletVersion::Version8), | ||
| 1781 | .theme_color = Applets::ThemeColor::BasicBlack, | ||
| 1782 | .play_startup_sound = true, | ||
| 1783 | .system_tick = system.CoreTiming().GetClockTicks(), | ||
| 1784 | }; | ||
| 1785 | |||
| 1786 | Applets::ControllerSupportArgNew user_args = { | ||
| 1787 | .header = {.player_count_min = 1, | ||
| 1788 | .player_count_max = 4, | ||
| 1789 | .enable_take_over_connection = true, | ||
| 1790 | .enable_left_justify = false, | ||
| 1791 | .enable_permit_joy_dual = true, | ||
| 1792 | .enable_single_mode = false, | ||
| 1793 | .enable_identification_color = false}, | ||
| 1794 | .identification_colors = {}, | ||
| 1795 | .enable_explain_text = false, | ||
| 1796 | .explain_text = {}, | ||
| 1797 | }; | ||
| 1798 | |||
| 1799 | Applets::ControllerSupportArgPrivate private_args = { | ||
| 1800 | .arg_private_size = sizeof(Applets::ControllerSupportArgPrivate), | ||
| 1801 | .arg_size = sizeof(Applets::ControllerSupportArgNew), | ||
| 1802 | .is_home_menu = true, | ||
| 1803 | .flag_1 = true, | ||
| 1804 | .mode = Applets::ControllerSupportMode::ShowControllerSupport, | ||
| 1805 | .caller = Applets::ControllerSupportCaller:: | ||
| 1806 | Application, // switchbrew: Always zero except with | ||
| 1807 | // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem, | ||
| 1808 | // which sets this to the input param | ||
| 1809 | .style_set = Core::HID::NpadStyleSet::None, | ||
| 1810 | .joy_hold_type = 0, | ||
| 1811 | }; | ||
| 1812 | std::vector<u8> common_args_data(sizeof(common_args)); | ||
| 1813 | std::vector<u8> private_args_data(sizeof(private_args)); | ||
| 1814 | std::vector<u8> user_args_data(sizeof(user_args)); | ||
| 1815 | |||
| 1816 | std::memcpy(common_args_data.data(), &common_args, sizeof(common_args)); | ||
| 1817 | std::memcpy(private_args_data.data(), &private_args, sizeof(private_args)); | ||
| 1818 | std::memcpy(user_args_data.data(), &user_args, sizeof(user_args)); | ||
| 1819 | |||
| 1820 | queue_data.emplace_back(std::move(common_args_data)); | ||
| 1821 | queue_data.emplace_back(std::move(private_args_data)); | ||
| 1822 | queue_data.emplace_back(std::move(user_args_data)); | ||
| 1823 | } | ||
| 1824 | |||
| 1740 | void ILibraryAppletSelfAccessor::PushInShowCabinetData() { | 1825 | void ILibraryAppletSelfAccessor::PushInShowCabinetData() { |
| 1741 | const Applets::CommonArguments arguments{ | 1826 | const Applets::CommonArguments arguments{ |
| 1742 | .arguments_version = Applets::CommonArgumentVersion::Version3, | 1827 | .arguments_version = Applets::CommonArgumentVersion::Version3, |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 8f8cb8a9e..905a71b9f 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -87,6 +87,7 @@ public: | |||
| 87 | 87 | ||
| 88 | private: | 88 | private: |
| 89 | void GetAppletResourceUserId(HLERequestContext& ctx); | 89 | void GetAppletResourceUserId(HLERequestContext& ctx); |
| 90 | void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx); | ||
| 90 | void AcquireForegroundRights(HLERequestContext& ctx); | 91 | void AcquireForegroundRights(HLERequestContext& ctx); |
| 91 | }; | 92 | }; |
| 92 | 93 | ||
| @@ -345,6 +346,7 @@ private: | |||
| 345 | void PopInData(HLERequestContext& ctx); | 346 | void PopInData(HLERequestContext& ctx); |
| 346 | void PushOutData(HLERequestContext& ctx); | 347 | void PushOutData(HLERequestContext& ctx); |
| 347 | void GetLibraryAppletInfo(HLERequestContext& ctx); | 348 | void GetLibraryAppletInfo(HLERequestContext& ctx); |
| 349 | void GetMainAppletIdentityInfo(HLERequestContext& ctx); | ||
| 348 | void ExitProcessAndReturn(HLERequestContext& ctx); | 350 | void ExitProcessAndReturn(HLERequestContext& ctx); |
| 349 | void GetCallerAppletIdentityInfo(HLERequestContext& ctx); | 351 | void GetCallerAppletIdentityInfo(HLERequestContext& ctx); |
| 350 | void GetDesirableKeyboardLayout(HLERequestContext& ctx); | 352 | void GetDesirableKeyboardLayout(HLERequestContext& ctx); |
| @@ -355,6 +357,7 @@ private: | |||
| 355 | void PushInShowCabinetData(); | 357 | void PushInShowCabinetData(); |
| 356 | void PushInShowMiiEditData(); | 358 | void PushInShowMiiEditData(); |
| 357 | void PushInShowSoftwareKeyboard(); | 359 | void PushInShowSoftwareKeyboard(); |
| 360 | void PushInShowController(); | ||
| 358 | 361 | ||
| 359 | std::deque<std::vector<u8>> queue_data; | 362 | std::deque<std::vector<u8>> queue_data; |
| 360 | }; | 363 | }; |
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index b379dadeb..9d1960cb7 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp | |||
| @@ -122,7 +122,8 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) | |||
| 122 | Service::NFP::RegisterInfoPrivate register_info{}; | 122 | Service::NFP::RegisterInfoPrivate register_info{}; |
| 123 | std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(), | 123 | std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(), |
| 124 | std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1)); | 124 | std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1)); |
| 125 | 125 | register_info.mii_store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All); | |
| 126 | register_info.mii_store_data.SetNickname({u'y', u'u', u'z', u'u'}); | ||
| 126 | nfp_device->SetRegisterInfoPrivate(register_info); | 127 | nfp_device->SetRegisterInfoPrivate(register_info); |
| 127 | break; | 128 | break; |
| 128 | } | 129 | } |
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h index f6c64f633..9f839f3d7 100644 --- a/src/core/hle/service/am/applets/applet_controller.h +++ b/src/core/hle/service/am/applets/applet_controller.h | |||
| @@ -56,7 +56,7 @@ enum class ControllerSupportResult : u32 { | |||
| 56 | struct ControllerSupportArgPrivate { | 56 | struct ControllerSupportArgPrivate { |
| 57 | u32 arg_private_size{}; | 57 | u32 arg_private_size{}; |
| 58 | u32 arg_size{}; | 58 | u32 arg_size{}; |
| 59 | bool flag_0{}; | 59 | bool is_home_menu{}; |
| 60 | bool flag_1{}; | 60 | bool flag_1{}; |
| 61 | ControllerSupportMode mode{}; | 61 | ControllerSupportMode mode{}; |
| 62 | ControllerSupportCaller caller{}; | 62 | ControllerSupportCaller caller{}; |
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index 1c9a1dc29..b0ea2b381 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp | |||
| @@ -330,8 +330,7 @@ void WebBrowser::ExtractOfflineRomFS() { | |||
| 330 | LOG_DEBUG(Service_AM, "Extracting RomFS to {}", | 330 | LOG_DEBUG(Service_AM, "Extracting RomFS to {}", |
| 331 | Common::FS::PathToUTF8String(offline_cache_dir)); | 331 | Common::FS::PathToUTF8String(offline_cache_dir)); |
| 332 | 332 | ||
| 333 | const auto extracted_romfs_dir = | 333 | const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs); |
| 334 | FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); | ||
| 335 | 334 | ||
| 336 | const auto temp_dir = system.GetFilesystem()->CreateDirectory( | 335 | const auto temp_dir = system.GetFilesystem()->CreateDirectory( |
| 337 | Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); | 336 | Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); |
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index f02bbc450..0bf2598b7 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h | |||
| @@ -69,6 +69,30 @@ enum class AppletId : u32 { | |||
| 69 | MyPage = 0x1A, | 69 | MyPage = 0x1A, |
| 70 | }; | 70 | }; |
| 71 | 71 | ||
| 72 | enum class AppletProgramId : u64 { | ||
| 73 | QLaunch = 0x0100000000001000ull, | ||
| 74 | Auth = 0x0100000000001001ull, | ||
| 75 | Cabinet = 0x0100000000001002ull, | ||
| 76 | Controller = 0x0100000000001003ull, | ||
| 77 | DataErase = 0x0100000000001004ull, | ||
| 78 | Error = 0x0100000000001005ull, | ||
| 79 | NetConnect = 0x0100000000001006ull, | ||
| 80 | ProfileSelect = 0x0100000000001007ull, | ||
| 81 | SoftwareKeyboard = 0x0100000000001008ull, | ||
| 82 | MiiEdit = 0x0100000000001009ull, | ||
| 83 | Web = 0x010000000000100Aull, | ||
| 84 | Shop = 0x010000000000100Bull, | ||
| 85 | OverlayDisplay = 0x010000000000100Cull, | ||
| 86 | PhotoViewer = 0x010000000000100Dull, | ||
| 87 | Settings = 0x010000000000100Eull, | ||
| 88 | OfflineWeb = 0x010000000000100Full, | ||
| 89 | LoginShare = 0x0100000000001010ull, | ||
| 90 | WebAuth = 0x0100000000001011ull, | ||
| 91 | Starter = 0x0100000000001012ull, | ||
| 92 | MyPage = 0x0100000000001013ull, | ||
| 93 | MaxProgramId = 0x0100000000001FFFull, | ||
| 94 | }; | ||
| 95 | |||
| 72 | enum class LibraryAppletMode : u32 { | 96 | enum class LibraryAppletMode : u32 { |
| 73 | AllForeground = 0, | 97 | AllForeground = 0, |
| 74 | Background = 1, | 98 | Background = 1, |
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 8069f75b7..c65e32489 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp | |||
| @@ -127,7 +127,7 @@ public: | |||
| 127 | 127 | ||
| 128 | private: | 128 | private: |
| 129 | void GetCore(HLERequestContext& ctx) { | 129 | void GetCore(HLERequestContext& ctx) { |
| 130 | LOG_DEBUG(Service_BTM, "called"); | 130 | LOG_WARNING(Service_BTM, "called"); |
| 131 | 131 | ||
| 132 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 132 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 133 | rb.Push(ResultSuccess); | 133 | rb.Push(ResultSuccess); |
| @@ -263,13 +263,13 @@ public: | |||
| 263 | explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} { | 263 | explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} { |
| 264 | // clang-format off | 264 | // clang-format off |
| 265 | static const FunctionInfo functions[] = { | 265 | static const FunctionInfo functions[] = { |
| 266 | {0, nullptr, "StartGamepadPairing"}, | 266 | {0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"}, |
| 267 | {1, nullptr, "CancelGamepadPairing"}, | 267 | {1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"}, |
| 268 | {2, nullptr, "ClearGamepadPairingDatabase"}, | 268 | {2, nullptr, "ClearGamepadPairingDatabase"}, |
| 269 | {3, nullptr, "GetPairedGamepadCount"}, | 269 | {3, nullptr, "GetPairedGamepadCount"}, |
| 270 | {4, nullptr, "EnableRadio"}, | 270 | {4, nullptr, "EnableRadio"}, |
| 271 | {5, nullptr, "DisableRadio"}, | 271 | {5, nullptr, "DisableRadio"}, |
| 272 | {6, nullptr, "GetRadioOnOff"}, | 272 | {6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"}, |
| 273 | {7, nullptr, "AcquireRadioEvent"}, | 273 | {7, nullptr, "AcquireRadioEvent"}, |
| 274 | {8, nullptr, "AcquireGamepadPairingEvent"}, | 274 | {8, nullptr, "AcquireGamepadPairingEvent"}, |
| 275 | {9, nullptr, "IsGamepadPairingStarted"}, | 275 | {9, nullptr, "IsGamepadPairingStarted"}, |
| @@ -280,18 +280,58 @@ public: | |||
| 280 | {14, nullptr, "AcquireAudioDeviceConnectionEvent"}, | 280 | {14, nullptr, "AcquireAudioDeviceConnectionEvent"}, |
| 281 | {15, nullptr, "ConnectAudioDevice"}, | 281 | {15, nullptr, "ConnectAudioDevice"}, |
| 282 | {16, nullptr, "IsConnectingAudioDevice"}, | 282 | {16, nullptr, "IsConnectingAudioDevice"}, |
| 283 | {17, nullptr, "GetConnectedAudioDevices"}, | 283 | {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"}, |
| 284 | {18, nullptr, "DisconnectAudioDevice"}, | 284 | {18, nullptr, "DisconnectAudioDevice"}, |
| 285 | {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, | 285 | {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, |
| 286 | {20, nullptr, "GetPairedAudioDevices"}, | 286 | {20, nullptr, "GetPairedAudioDevices"}, |
| 287 | {21, nullptr, "RemoveAudioDevicePairing"}, | 287 | {21, nullptr, "RemoveAudioDevicePairing"}, |
| 288 | {22, nullptr, "RequestAudioDeviceConnectionRejection"}, | 288 | {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"}, |
| 289 | {23, nullptr, "CancelAudioDeviceConnectionRejection"} | 289 | {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"} |
| 290 | }; | 290 | }; |
| 291 | // clang-format on | 291 | // clang-format on |
| 292 | 292 | ||
| 293 | RegisterHandlers(functions); | 293 | RegisterHandlers(functions); |
| 294 | } | 294 | } |
| 295 | |||
| 296 | private: | ||
| 297 | void IsRadioEnabled(HLERequestContext& ctx) { | ||
| 298 | LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running | ||
| 299 | |||
| 300 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 301 | rb.Push(ResultSuccess); | ||
| 302 | rb.Push(true); | ||
| 303 | } | ||
| 304 | |||
| 305 | void StartGamepadPairing(HLERequestContext& ctx) { | ||
| 306 | LOG_WARNING(Service_BTM, "(STUBBED) called"); | ||
| 307 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 308 | rb.Push(ResultSuccess); | ||
| 309 | } | ||
| 310 | |||
| 311 | void CancelGamepadPairing(HLERequestContext& ctx) { | ||
| 312 | LOG_WARNING(Service_BTM, "(STUBBED) called"); | ||
| 313 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 314 | rb.Push(ResultSuccess); | ||
| 315 | } | ||
| 316 | |||
| 317 | void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) { | ||
| 318 | LOG_WARNING(Service_BTM, "(STUBBED) called"); | ||
| 319 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 320 | rb.Push(ResultSuccess); | ||
| 321 | } | ||
| 322 | |||
| 323 | void GetConnectedAudioDevices(HLERequestContext& ctx) { | ||
| 324 | LOG_WARNING(Service_BTM, "(STUBBED) called"); | ||
| 325 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 326 | rb.Push(ResultSuccess); | ||
| 327 | rb.Push<u32>(0); | ||
| 328 | } | ||
| 329 | |||
| 330 | void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) { | ||
| 331 | LOG_WARNING(Service_BTM, "(STUBBED) called"); | ||
| 332 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 333 | rb.Push(ResultSuccess); | ||
| 334 | } | ||
| 295 | }; | 335 | }; |
| 296 | 336 | ||
| 297 | class BTM_SYS final : public ServiceFramework<BTM_SYS> { | 337 | class BTM_SYS final : public ServiceFramework<BTM_SYS> { |
| @@ -308,7 +348,7 @@ public: | |||
| 308 | 348 | ||
| 309 | private: | 349 | private: |
| 310 | void GetCore(HLERequestContext& ctx) { | 350 | void GetCore(HLERequestContext& ctx) { |
| 311 | LOG_DEBUG(Service_BTM, "called"); | 351 | LOG_WARNING(Service_BTM, "called"); |
| 312 | 352 | ||
| 313 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 353 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 314 | rb.Push(ResultSuccess); | 354 | rb.Push(ResultSuccess); |
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 9d05f9801..0507b14e7 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -32,7 +32,7 @@ public: | |||
| 32 | {10200, nullptr, "SendFriendRequestForApplication"}, | 32 | {10200, nullptr, "SendFriendRequestForApplication"}, |
| 33 | {10211, nullptr, "AddFacedFriendRequestForApplication"}, | 33 | {10211, nullptr, "AddFacedFriendRequestForApplication"}, |
| 34 | {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, | 34 | {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, |
| 35 | {10420, nullptr, "IsBlockedUserListCacheAvailable"}, | 35 | {10420, &IFriendService::CheckBlockedUserListAvailability, "CheckBlockedUserListAvailability"}, |
| 36 | {10421, nullptr, "EnsureBlockedUserListAvailable"}, | 36 | {10421, nullptr, "EnsureBlockedUserListAvailable"}, |
| 37 | {10500, nullptr, "GetProfileList"}, | 37 | {10500, nullptr, "GetProfileList"}, |
| 38 | {10600, nullptr, "DeclareOpenOnlinePlaySession"}, | 38 | {10600, nullptr, "DeclareOpenOnlinePlaySession"}, |
| @@ -206,6 +206,17 @@ private: | |||
| 206 | rb.Push(true); | 206 | rb.Push(true); |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | void CheckBlockedUserListAvailability(HLERequestContext& ctx) { | ||
| 210 | IPC::RequestParser rp{ctx}; | ||
| 211 | const auto uuid{rp.PopRaw<Common::UUID>()}; | ||
| 212 | |||
| 213 | LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString()); | ||
| 214 | |||
| 215 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 216 | rb.Push(ResultSuccess); | ||
| 217 | rb.Push(true); | ||
| 218 | } | ||
| 219 | |||
| 209 | KernelHelpers::ServiceContext service_context; | 220 | KernelHelpers::ServiceContext service_context; |
| 210 | 221 | ||
| 211 | Kernel::KEvent* completion_event; | 222 | Kernel::KEvent* completion_event; |
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.cpp b/src/core/hle/service/hid/controllers/console_six_axis.cpp new file mode 100644 index 000000000..b2bf1d78d --- /dev/null +++ b/src/core/hle/service/hid/controllers/console_six_axis.cpp | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hid/emulated_console.h" | ||
| 7 | #include "core/hid/hid_core.h" | ||
| 8 | #include "core/hle/service/hid/controllers/console_six_axis.h" | ||
| 9 | #include "core/memory.h" | ||
| 10 | |||
| 11 | namespace Service::HID { | ||
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; | ||
| 13 | |||
| 14 | ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) | ||
| 15 | : ControllerBase{hid_core_} { | ||
| 16 | console = hid_core.GetEmulatedConsole(); | ||
| 17 | static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size, | ||
| 18 | "ConsoleSharedMemory is bigger than the shared memory"); | ||
| 19 | shared_memory = std::construct_at( | ||
| 20 | reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); | ||
| 21 | } | ||
| 22 | |||
| 23 | ConsoleSixAxis::~ConsoleSixAxis() = default; | ||
| 24 | |||
| 25 | void ConsoleSixAxis::OnInit() {} | ||
| 26 | |||
| 27 | void ConsoleSixAxis::OnRelease() {} | ||
| 28 | |||
| 29 | void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 30 | if (!IsControllerActivated()) { | ||
| 31 | return; | ||
| 32 | } | ||
| 33 | |||
| 34 | const auto motion_status = console->GetMotion(); | ||
| 35 | |||
| 36 | shared_memory->sampling_number++; | ||
| 37 | shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; | ||
| 38 | shared_memory->verticalization_error = motion_status.verticalization_error; | ||
| 39 | shared_memory->gyro_bias = motion_status.gyro_bias; | ||
| 40 | } | ||
| 41 | |||
| 42 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.h b/src/core/hle/service/hid/controllers/console_six_axis.h new file mode 100644 index 000000000..5b7c6a29a --- /dev/null +++ b/src/core/hle/service/hid/controllers/console_six_axis.h | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/vector_math.h" | ||
| 7 | #include "core/hle/service/hid/controllers/controller_base.h" | ||
| 8 | |||
| 9 | namespace Core::HID { | ||
| 10 | class EmulatedConsole; | ||
| 11 | } // namespace Core::HID | ||
| 12 | |||
| 13 | namespace Service::HID { | ||
| 14 | class ConsoleSixAxis final : public ControllerBase { | ||
| 15 | public: | ||
| 16 | explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); | ||
| 17 | ~ConsoleSixAxis() override; | ||
| 18 | |||
| 19 | // Called when the controller is initialized | ||
| 20 | void OnInit() override; | ||
| 21 | |||
| 22 | // When the controller is released | ||
| 23 | void OnRelease() override; | ||
| 24 | |||
| 25 | // When the controller is requesting an update for the shared memory | ||
| 26 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 27 | |||
| 28 | private: | ||
| 29 | // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat | ||
| 30 | struct ConsoleSharedMemory { | ||
| 31 | u64 sampling_number{}; | ||
| 32 | bool is_seven_six_axis_sensor_at_rest{}; | ||
| 33 | INSERT_PADDING_BYTES(3); // padding | ||
| 34 | f32 verticalization_error{}; | ||
| 35 | Common::Vec3f gyro_bias{}; | ||
| 36 | INSERT_PADDING_BYTES(4); // padding | ||
| 37 | }; | ||
| 38 | static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size"); | ||
| 39 | |||
| 40 | ConsoleSharedMemory* shared_memory = nullptr; | ||
| 41 | Core::HID::EmulatedConsole* console = nullptr; | ||
| 42 | }; | ||
| 43 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp index c58d67d7d..0bcd87062 100644 --- a/src/core/hle/service/hid/controllers/controller_base.cpp +++ b/src/core/hle/service/hid/controllers/controller_base.cpp | |||
| @@ -8,12 +8,17 @@ namespace Service::HID { | |||
| 8 | ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {} | 8 | ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {} |
| 9 | ControllerBase::~ControllerBase() = default; | 9 | ControllerBase::~ControllerBase() = default; |
| 10 | 10 | ||
| 11 | void ControllerBase::ActivateController() { | 11 | Result ControllerBase::Activate() { |
| 12 | if (is_activated) { | 12 | if (is_activated) { |
| 13 | return; | 13 | return ResultSuccess; |
| 14 | } | 14 | } |
| 15 | is_activated = true; | 15 | is_activated = true; |
| 16 | OnInit(); | 16 | OnInit(); |
| 17 | return ResultSuccess; | ||
| 18 | } | ||
| 19 | |||
| 20 | Result ControllerBase::Activate(u64 aruid) { | ||
| 21 | return Activate(); | ||
| 17 | } | 22 | } |
| 18 | 23 | ||
| 19 | void ControllerBase::DeactivateController() { | 24 | void ControllerBase::DeactivateController() { |
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index d6f7a5073..9a44ee41e 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "core/hle/result.h" | ||
| 7 | 8 | ||
| 8 | namespace Core::Timing { | 9 | namespace Core::Timing { |
| 9 | class CoreTiming; | 10 | class CoreTiming; |
| @@ -31,7 +32,8 @@ public: | |||
| 31 | // When the controller is requesting a motion update for the shared memory | 32 | // When the controller is requesting a motion update for the shared memory |
| 32 | virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {} | 33 | virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {} |
| 33 | 34 | ||
| 34 | void ActivateController(); | 35 | Result Activate(); |
| 36 | Result Activate(u64 aruid); | ||
| 35 | 37 | ||
| 36 | void DeactivateController(); | 38 | void DeactivateController(); |
| 37 | 39 | ||
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 8ec9f4a95..9de19ebfc 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | namespace Service::HID { | 13 | namespace Service::HID { |
| 14 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; | 14 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; |
| 15 | 15 | ||
| 16 | Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) | 16 | DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) |
| 17 | : ControllerBase{hid_core_} { | 17 | : ControllerBase{hid_core_} { |
| 18 | static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size, | 18 | static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size, |
| 19 | "DebugPadSharedMemory is bigger than the shared memory"); | 19 | "DebugPadSharedMemory is bigger than the shared memory"); |
| @@ -22,13 +22,13 @@ Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_ | |||
| 22 | controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); | 22 | controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | Controller_DebugPad::~Controller_DebugPad() = default; | 25 | DebugPad::~DebugPad() = default; |
| 26 | 26 | ||
| 27 | void Controller_DebugPad::OnInit() {} | 27 | void DebugPad::OnInit() {} |
| 28 | 28 | ||
| 29 | void Controller_DebugPad::OnRelease() {} | 29 | void DebugPad::OnRelease() {} |
| 30 | 30 | ||
| 31 | void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 31 | void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 32 | if (!IsControllerActivated()) { | 32 | if (!IsControllerActivated()) { |
| 33 | shared_memory->debug_pad_lifo.buffer_count = 0; | 33 | shared_memory->debug_pad_lifo.buffer_count = 0; |
| 34 | shared_memory->debug_pad_lifo.buffer_tail = 0; | 34 | shared_memory->debug_pad_lifo.buffer_tail = 0; |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index 68ff0ea79..5566dba77 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h | |||
| @@ -15,10 +15,10 @@ struct AnalogStickState; | |||
| 15 | } // namespace Core::HID | 15 | } // namespace Core::HID |
| 16 | 16 | ||
| 17 | namespace Service::HID { | 17 | namespace Service::HID { |
| 18 | class Controller_DebugPad final : public ControllerBase { | 18 | class DebugPad final : public ControllerBase { |
| 19 | public: | 19 | public: |
| 20 | explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); | 20 | explicit DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); |
| 21 | ~Controller_DebugPad() override; | 21 | ~DebugPad() override; |
| 22 | 22 | ||
| 23 | // Called when the controller is initialized | 23 | // Called when the controller is initialized |
| 24 | void OnInit() override; | 24 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 63eecd42b..59b2ec73c 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp | |||
| @@ -23,7 +23,7 @@ constexpr f32 Square(s32 num) { | |||
| 23 | return static_cast<f32>(num * num); | 23 | return static_cast<f32>(num * num); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) | 26 | Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) |
| 27 | : ControllerBase(hid_core_) { | 27 | : ControllerBase(hid_core_) { |
| 28 | static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size, | 28 | static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size, |
| 29 | "GestureSharedMemory is bigger than the shared memory"); | 29 | "GestureSharedMemory is bigger than the shared memory"); |
| @@ -31,17 +31,17 @@ Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_sh | |||
| 31 | reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); | 31 | reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); |
| 32 | console = hid_core.GetEmulatedConsole(); | 32 | console = hid_core.GetEmulatedConsole(); |
| 33 | } | 33 | } |
| 34 | Controller_Gesture::~Controller_Gesture() = default; | 34 | Gesture::~Gesture() = default; |
| 35 | 35 | ||
| 36 | void Controller_Gesture::OnInit() { | 36 | void Gesture::OnInit() { |
| 37 | shared_memory->gesture_lifo.buffer_count = 0; | 37 | shared_memory->gesture_lifo.buffer_count = 0; |
| 38 | shared_memory->gesture_lifo.buffer_tail = 0; | 38 | shared_memory->gesture_lifo.buffer_tail = 0; |
| 39 | force_update = true; | 39 | force_update = true; |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | void Controller_Gesture::OnRelease() {} | 42 | void Gesture::OnRelease() {} |
| 43 | 43 | ||
| 44 | void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 44 | void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 45 | if (!IsControllerActivated()) { | 45 | if (!IsControllerActivated()) { |
| 46 | shared_memory->gesture_lifo.buffer_count = 0; | 46 | shared_memory->gesture_lifo.buffer_count = 0; |
| 47 | shared_memory->gesture_lifo.buffer_tail = 0; | 47 | shared_memory->gesture_lifo.buffer_tail = 0; |
| @@ -64,7 +64,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | |||
| 64 | UpdateGestureSharedMemory(gesture, time_difference); | 64 | UpdateGestureSharedMemory(gesture, time_difference); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | void Controller_Gesture::ReadTouchInput() { | 67 | void Gesture::ReadTouchInput() { |
| 68 | if (!Settings::values.touchscreen.enabled) { | 68 | if (!Settings::values.touchscreen.enabled) { |
| 69 | fingers = {}; | 69 | fingers = {}; |
| 70 | return; | 70 | return; |
| @@ -76,8 +76,7 @@ void Controller_Gesture::ReadTouchInput() { | |||
| 76 | } | 76 | } |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, | 79 | bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) { |
| 80 | f32 time_difference) { | ||
| 81 | const auto& last_entry = GetLastGestureEntry(); | 80 | const auto& last_entry = GetLastGestureEntry(); |
| 82 | if (force_update) { | 81 | if (force_update) { |
| 83 | force_update = false; | 82 | force_update = false; |
| @@ -100,8 +99,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, | |||
| 100 | return false; | 99 | return false; |
| 101 | } | 100 | } |
| 102 | 101 | ||
| 103 | void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, | 102 | void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { |
| 104 | f32 time_difference) { | ||
| 105 | GestureType type = GestureType::Idle; | 103 | GestureType type = GestureType::Idle; |
| 106 | GestureAttribute attributes{}; | 104 | GestureAttribute attributes{}; |
| 107 | 105 | ||
| @@ -138,8 +136,8 @@ void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, | |||
| 138 | shared_memory->gesture_lifo.WriteNextEntry(next_state); | 136 | shared_memory->gesture_lifo.WriteNextEntry(next_state); |
| 139 | } | 137 | } |
| 140 | 138 | ||
| 141 | void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type, | 139 | void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, |
| 142 | GestureAttribute& attributes) { | 140 | GestureAttribute& attributes) { |
| 143 | const auto& last_entry = GetLastGestureEntry(); | 141 | const auto& last_entry = GetLastGestureEntry(); |
| 144 | 142 | ||
| 145 | gesture.detection_count++; | 143 | gesture.detection_count++; |
| @@ -152,8 +150,8 @@ void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& typ | |||
| 152 | } | 150 | } |
| 153 | } | 151 | } |
| 154 | 152 | ||
| 155 | void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, | 153 | void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, |
| 156 | f32 time_difference) { | 154 | f32 time_difference) { |
| 157 | const auto& last_entry = GetLastGestureEntry(); | 155 | const auto& last_entry = GetLastGestureEntry(); |
| 158 | 156 | ||
| 159 | // Promote to pan type if touch moved | 157 | // Promote to pan type if touch moved |
| @@ -186,9 +184,8 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Gestu | |||
| 186 | } | 184 | } |
| 187 | } | 185 | } |
| 188 | 186 | ||
| 189 | void Controller_Gesture::EndGesture(GestureProperties& gesture, | 187 | void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, |
| 190 | GestureProperties& last_gesture_props, GestureType& type, | 188 | GestureType& type, GestureAttribute& attributes, f32 time_difference) { |
| 191 | GestureAttribute& attributes, f32 time_difference) { | ||
| 192 | const auto& last_entry = GetLastGestureEntry(); | 189 | const auto& last_entry = GetLastGestureEntry(); |
| 193 | 190 | ||
| 194 | if (last_gesture_props.active_points != 0) { | 191 | if (last_gesture_props.active_points != 0) { |
| @@ -222,9 +219,8 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture, | |||
| 222 | } | 219 | } |
| 223 | } | 220 | } |
| 224 | 221 | ||
| 225 | void Controller_Gesture::SetTapEvent(GestureProperties& gesture, | 222 | void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, |
| 226 | GestureProperties& last_gesture_props, GestureType& type, | 223 | GestureType& type, GestureAttribute& attributes) { |
| 227 | GestureAttribute& attributes) { | ||
| 228 | type = GestureType::Tap; | 224 | type = GestureType::Tap; |
| 229 | gesture = last_gesture_props; | 225 | gesture = last_gesture_props; |
| 230 | force_update = true; | 226 | force_update = true; |
| @@ -236,9 +232,8 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture, | |||
| 236 | } | 232 | } |
| 237 | } | 233 | } |
| 238 | 234 | ||
| 239 | void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, | 235 | void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, |
| 240 | GestureProperties& last_gesture_props, GestureType& type, | 236 | GestureType& type, f32 time_difference) { |
| 241 | f32 time_difference) { | ||
| 242 | const auto& last_entry = GetLastGestureEntry(); | 237 | const auto& last_entry = GetLastGestureEntry(); |
| 243 | 238 | ||
| 244 | next_state.delta = gesture.mid_point - last_entry.pos; | 239 | next_state.delta = gesture.mid_point - last_entry.pos; |
| @@ -263,9 +258,8 @@ void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, | |||
| 263 | } | 258 | } |
| 264 | } | 259 | } |
| 265 | 260 | ||
| 266 | void Controller_Gesture::EndPanEvent(GestureProperties& gesture, | 261 | void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, |
| 267 | GestureProperties& last_gesture_props, GestureType& type, | 262 | GestureType& type, f32 time_difference) { |
| 268 | f32 time_difference) { | ||
| 269 | const auto& last_entry = GetLastGestureEntry(); | 263 | const auto& last_entry = GetLastGestureEntry(); |
| 270 | next_state.vel_x = | 264 | next_state.vel_x = |
| 271 | static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); | 265 | static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); |
| @@ -287,8 +281,8 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture, | |||
| 287 | force_update = true; | 281 | force_update = true; |
| 288 | } | 282 | } |
| 289 | 283 | ||
| 290 | void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, | 284 | void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, |
| 291 | GestureProperties& last_gesture_props, GestureType& type) { | 285 | GestureType& type) { |
| 292 | const auto& last_entry = GetLastGestureEntry(); | 286 | const auto& last_entry = GetLastGestureEntry(); |
| 293 | 287 | ||
| 294 | type = GestureType::Swipe; | 288 | type = GestureType::Swipe; |
| @@ -311,11 +305,11 @@ void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, | |||
| 311 | next_state.direction = GestureDirection::Up; | 305 | next_state.direction = GestureDirection::Up; |
| 312 | } | 306 | } |
| 313 | 307 | ||
| 314 | const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { | 308 | const Gesture::GestureState& Gesture::GetLastGestureEntry() const { |
| 315 | return shared_memory->gesture_lifo.ReadCurrentEntry().state; | 309 | return shared_memory->gesture_lifo.ReadCurrentEntry().state; |
| 316 | } | 310 | } |
| 317 | 311 | ||
| 318 | Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { | 312 | Gesture::GestureProperties Gesture::GetGestureProperties() { |
| 319 | GestureProperties gesture; | 313 | GestureProperties gesture; |
| 320 | std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; | 314 | std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; |
| 321 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), | 315 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), |
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index 0d6099ea0..4c6f8ee07 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h | |||
| @@ -12,10 +12,10 @@ | |||
| 12 | #include "core/hle/service/hid/ring_lifo.h" | 12 | #include "core/hle/service/hid/ring_lifo.h" |
| 13 | 13 | ||
| 14 | namespace Service::HID { | 14 | namespace Service::HID { |
| 15 | class Controller_Gesture final : public ControllerBase { | 15 | class Gesture final : public ControllerBase { |
| 16 | public: | 16 | public: |
| 17 | explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); | 17 | explicit Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); |
| 18 | ~Controller_Gesture() override; | 18 | ~Gesture() override; |
| 19 | 19 | ||
| 20 | // Called when the controller is initialized | 20 | // Called when the controller is initialized |
| 21 | void OnInit() override; | 21 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 117d87433..ddb1b0ba4 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | namespace Service::HID { | 12 | namespace Service::HID { |
| 13 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; | 13 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; |
| 14 | 14 | ||
| 15 | Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) | 15 | Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) |
| 16 | : ControllerBase{hid_core_} { | 16 | : ControllerBase{hid_core_} { |
| 17 | static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size, | 17 | static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size, |
| 18 | "KeyboardSharedMemory is bigger than the shared memory"); | 18 | "KeyboardSharedMemory is bigger than the shared memory"); |
| @@ -21,13 +21,13 @@ Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_ | |||
| 21 | emulated_devices = hid_core.GetEmulatedDevices(); | 21 | emulated_devices = hid_core.GetEmulatedDevices(); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | Controller_Keyboard::~Controller_Keyboard() = default; | 24 | Keyboard::~Keyboard() = default; |
| 25 | 25 | ||
| 26 | void Controller_Keyboard::OnInit() {} | 26 | void Keyboard::OnInit() {} |
| 27 | 27 | ||
| 28 | void Controller_Keyboard::OnRelease() {} | 28 | void Keyboard::OnRelease() {} |
| 29 | 29 | ||
| 30 | void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 30 | void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 31 | if (!IsControllerActivated()) { | 31 | if (!IsControllerActivated()) { |
| 32 | shared_memory->keyboard_lifo.buffer_count = 0; | 32 | shared_memory->keyboard_lifo.buffer_count = 0; |
| 33 | shared_memory->keyboard_lifo.buffer_tail = 0; | 33 | shared_memory->keyboard_lifo.buffer_tail = 0; |
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index 7532f53c6..172ec1309 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h | |||
| @@ -14,10 +14,10 @@ struct KeyboardKey; | |||
| 14 | } // namespace Core::HID | 14 | } // namespace Core::HID |
| 15 | 15 | ||
| 16 | namespace Service::HID { | 16 | namespace Service::HID { |
| 17 | class Controller_Keyboard final : public ControllerBase { | 17 | class Keyboard final : public ControllerBase { |
| 18 | public: | 18 | public: |
| 19 | explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); | 19 | explicit Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); |
| 20 | ~Controller_Keyboard() override; | 20 | ~Keyboard() override; |
| 21 | 21 | ||
| 22 | // Called when the controller is initialized | 22 | // Called when the controller is initialized |
| 23 | void OnInit() override; | 23 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 0afc66681..6e5a04e34 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp | |||
| @@ -12,8 +12,7 @@ | |||
| 12 | namespace Service::HID { | 12 | namespace Service::HID { |
| 13 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; | 13 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; |
| 14 | 14 | ||
| 15 | Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) | 15 | Mouse::Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { |
| 16 | : ControllerBase{hid_core_} { | ||
| 17 | static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size, | 16 | static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size, |
| 18 | "MouseSharedMemory is bigger than the shared memory"); | 17 | "MouseSharedMemory is bigger than the shared memory"); |
| 19 | shared_memory = std::construct_at( | 18 | shared_memory = std::construct_at( |
| @@ -21,12 +20,12 @@ Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared | |||
| 21 | emulated_devices = hid_core.GetEmulatedDevices(); | 20 | emulated_devices = hid_core.GetEmulatedDevices(); |
| 22 | } | 21 | } |
| 23 | 22 | ||
| 24 | Controller_Mouse::~Controller_Mouse() = default; | 23 | Mouse::~Mouse() = default; |
| 25 | 24 | ||
| 26 | void Controller_Mouse::OnInit() {} | 25 | void Mouse::OnInit() {} |
| 27 | void Controller_Mouse::OnRelease() {} | 26 | void Mouse::OnRelease() {} |
| 28 | 27 | ||
| 29 | void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 28 | void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 30 | if (!IsControllerActivated()) { | 29 | if (!IsControllerActivated()) { |
| 31 | shared_memory->mouse_lifo.buffer_count = 0; | 30 | shared_memory->mouse_lifo.buffer_count = 0; |
| 32 | shared_memory->mouse_lifo.buffer_tail = 0; | 31 | shared_memory->mouse_lifo.buffer_tail = 0; |
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 733d00577..a80f3823f 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h | |||
| @@ -14,10 +14,10 @@ struct AnalogStickState; | |||
| 14 | } // namespace Core::HID | 14 | } // namespace Core::HID |
| 15 | 15 | ||
| 16 | namespace Service::HID { | 16 | namespace Service::HID { |
| 17 | class Controller_Mouse final : public ControllerBase { | 17 | class Mouse final : public ControllerBase { |
| 18 | public: | 18 | public: |
| 19 | explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); | 19 | explicit Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); |
| 20 | ~Controller_Mouse() override; | 20 | ~Mouse() override; |
| 21 | 21 | ||
| 22 | // Called when the controller is initialized | 22 | // Called when the controller is initialized |
| 23 | void OnInit() override; | 23 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index bc822f19e..08ee9de9c 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include "core/hle/kernel/k_readable_event.h" | 18 | #include "core/hle/kernel/k_readable_event.h" |
| 19 | #include "core/hle/service/hid/controllers/npad.h" | 19 | #include "core/hle/service/hid/controllers/npad.h" |
| 20 | #include "core/hle/service/hid/errors.h" | 20 | #include "core/hle/service/hid/errors.h" |
| 21 | #include "core/hle/service/hid/hid_util.h" | ||
| 21 | #include "core/hle/service/kernel_helpers.h" | 22 | #include "core/hle/service/kernel_helpers.h" |
| 22 | 23 | ||
| 23 | namespace Service::HID { | 24 | namespace Service::HID { |
| @@ -29,60 +30,8 @@ constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{ | |||
| 29 | Core::HID::NpadIdType::Handheld, | 30 | Core::HID::NpadIdType::Handheld, |
| 30 | }; | 31 | }; |
| 31 | 32 | ||
| 32 | bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { | 33 | NPad::NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, |
| 33 | switch (npad_id) { | 34 | KernelHelpers::ServiceContext& service_context_) |
| 34 | case Core::HID::NpadIdType::Player1: | ||
| 35 | case Core::HID::NpadIdType::Player2: | ||
| 36 | case Core::HID::NpadIdType::Player3: | ||
| 37 | case Core::HID::NpadIdType::Player4: | ||
| 38 | case Core::HID::NpadIdType::Player5: | ||
| 39 | case Core::HID::NpadIdType::Player6: | ||
| 40 | case Core::HID::NpadIdType::Player7: | ||
| 41 | case Core::HID::NpadIdType::Player8: | ||
| 42 | case Core::HID::NpadIdType::Other: | ||
| 43 | case Core::HID::NpadIdType::Handheld: | ||
| 44 | return true; | ||
| 45 | default: | ||
| 46 | LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id); | ||
| 47 | return false; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { | ||
| 52 | const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); | ||
| 53 | const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; | ||
| 54 | const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; | ||
| 55 | |||
| 56 | if (!npad_type) { | ||
| 57 | return VibrationInvalidStyleIndex; | ||
| 58 | } | ||
| 59 | if (!npad_id) { | ||
| 60 | return VibrationInvalidNpadId; | ||
| 61 | } | ||
| 62 | if (!device_index) { | ||
| 63 | return VibrationDeviceIndexOutOfRange; | ||
| 64 | } | ||
| 65 | |||
| 66 | return ResultSuccess; | ||
| 67 | } | ||
| 68 | |||
| 69 | Result Controller_NPad::VerifyValidSixAxisSensorHandle( | ||
| 70 | const Core::HID::SixAxisSensorHandle& device_handle) { | ||
| 71 | const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); | ||
| 72 | const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; | ||
| 73 | |||
| 74 | if (!npad_id) { | ||
| 75 | return InvalidNpadId; | ||
| 76 | } | ||
| 77 | if (!device_index) { | ||
| 78 | return NpadDeviceIndexOutOfRange; | ||
| 79 | } | ||
| 80 | |||
| 81 | return ResultSuccess; | ||
| 82 | } | ||
| 83 | |||
| 84 | Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, | ||
| 85 | KernelHelpers::ServiceContext& service_context_) | ||
| 86 | : ControllerBase{hid_core_}, service_context{service_context_} { | 35 | : ControllerBase{hid_core_}, service_context{service_context_} { |
| 87 | static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size); | 36 | static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size); |
| 88 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | 37 | for (std::size_t i = 0; i < controller_data.size(); ++i) { |
| @@ -103,7 +52,7 @@ Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_m | |||
| 103 | } | 52 | } |
| 104 | } | 53 | } |
| 105 | 54 | ||
| 106 | Controller_NPad::~Controller_NPad() { | 55 | NPad::~NPad() { |
| 107 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | 56 | for (std::size_t i = 0; i < controller_data.size(); ++i) { |
| 108 | auto& controller = controller_data[i]; | 57 | auto& controller = controller_data[i]; |
| 109 | controller.device->DeleteCallback(controller.callback_key); | 58 | controller.device->DeleteCallback(controller.callback_key); |
| @@ -111,8 +60,7 @@ Controller_NPad::~Controller_NPad() { | |||
| 111 | OnRelease(); | 60 | OnRelease(); |
| 112 | } | 61 | } |
| 113 | 62 | ||
| 114 | void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, | 63 | void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) { |
| 115 | std::size_t controller_idx) { | ||
| 116 | if (type == Core::HID::ControllerTriggerType::All) { | 64 | if (type == Core::HID::ControllerTriggerType::All) { |
| 117 | ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); | 65 | ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); |
| 118 | ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); | 66 | ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); |
| @@ -150,7 +98,7 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, | |||
| 150 | } | 98 | } |
| 151 | } | 99 | } |
| 152 | 100 | ||
| 153 | void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { | 101 | void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { |
| 154 | auto& controller = GetControllerFromNpadIdType(npad_id); | 102 | auto& controller = GetControllerFromNpadIdType(npad_id); |
| 155 | if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) { | 103 | if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) { |
| 156 | return; | 104 | return; |
| @@ -344,12 +292,13 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { | |||
| 344 | controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices, | 292 | controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices, |
| 345 | Common::Input::PollingMode::Active); | 293 | Common::Input::PollingMode::Active); |
| 346 | } | 294 | } |
| 295 | |||
| 347 | SignalStyleSetChangedEvent(npad_id); | 296 | SignalStyleSetChangedEvent(npad_id); |
| 348 | WriteEmptyEntry(controller.shared_memory); | 297 | WriteEmptyEntry(controller.shared_memory); |
| 349 | hid_core.SetLastActiveController(npad_id); | 298 | hid_core.SetLastActiveController(npad_id); |
| 350 | } | 299 | } |
| 351 | 300 | ||
| 352 | void Controller_NPad::OnInit() { | 301 | void NPad::OnInit() { |
| 353 | if (!IsControllerActivated()) { | 302 | if (!IsControllerActivated()) { |
| 354 | return; | 303 | return; |
| 355 | } | 304 | } |
| @@ -383,7 +332,7 @@ void Controller_NPad::OnInit() { | |||
| 383 | } | 332 | } |
| 384 | } | 333 | } |
| 385 | 334 | ||
| 386 | void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) { | 335 | void NPad::WriteEmptyEntry(NpadInternalState* npad) { |
| 387 | NPadGenericState dummy_pad_state{}; | 336 | NPadGenericState dummy_pad_state{}; |
| 388 | NpadGcTriggerState dummy_gc_state{}; | 337 | NpadGcTriggerState dummy_gc_state{}; |
| 389 | dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; | 338 | dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; |
| @@ -404,7 +353,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) { | |||
| 404 | npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); | 353 | npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); |
| 405 | } | 354 | } |
| 406 | 355 | ||
| 407 | void Controller_NPad::OnRelease() { | 356 | void NPad::OnRelease() { |
| 408 | is_controller_initialized = false; | 357 | is_controller_initialized = false; |
| 409 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | 358 | for (std::size_t i = 0; i < controller_data.size(); ++i) { |
| 410 | auto& controller = controller_data[i]; | 359 | auto& controller = controller_data[i]; |
| @@ -415,7 +364,7 @@ void Controller_NPad::OnRelease() { | |||
| 415 | } | 364 | } |
| 416 | } | 365 | } |
| 417 | 366 | ||
| 418 | void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { | 367 | void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { |
| 419 | std::scoped_lock lock{mutex}; | 368 | std::scoped_lock lock{mutex}; |
| 420 | auto& controller = GetControllerFromNpadIdType(npad_id); | 369 | auto& controller = GetControllerFromNpadIdType(npad_id); |
| 421 | const auto controller_type = controller.device->GetNpadStyleIndex(); | 370 | const auto controller_type = controller.device->GetNpadStyleIndex(); |
| @@ -457,12 +406,14 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { | |||
| 457 | pad_entry.l_stick = stick_state.left; | 406 | pad_entry.l_stick = stick_state.left; |
| 458 | } | 407 | } |
| 459 | 408 | ||
| 460 | if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) { | 409 | if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft || |
| 410 | controller_type == Core::HID::NpadStyleIndex::JoyconDual) { | ||
| 461 | pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl); | 411 | pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl); |
| 462 | pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr); | 412 | pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr); |
| 463 | } | 413 | } |
| 464 | 414 | ||
| 465 | if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) { | 415 | if (controller_type == Core::HID::NpadStyleIndex::JoyconRight || |
| 416 | controller_type == Core::HID::NpadStyleIndex::JoyconDual) { | ||
| 466 | pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl); | 417 | pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl); |
| 467 | pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr); | 418 | pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr); |
| 468 | } | 419 | } |
| @@ -482,7 +433,7 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { | |||
| 482 | } | 433 | } |
| 483 | } | 434 | } |
| 484 | 435 | ||
| 485 | void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 436 | void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 486 | if (!IsControllerActivated()) { | 437 | if (!IsControllerActivated()) { |
| 487 | return; | 438 | return; |
| 488 | } | 439 | } |
| @@ -612,134 +563,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | |||
| 612 | } | 563 | } |
| 613 | } | 564 | } |
| 614 | 565 | ||
| 615 | void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) { | 566 | void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { |
| 616 | if (!IsControllerActivated()) { | ||
| 617 | return; | ||
| 618 | } | ||
| 619 | |||
| 620 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | ||
| 621 | auto& controller = controller_data[i]; | ||
| 622 | |||
| 623 | const auto& controller_type = controller.device->GetNpadStyleIndex(); | ||
| 624 | |||
| 625 | if (controller_type == Core::HID::NpadStyleIndex::None || | ||
| 626 | !controller.device->IsConnected()) { | ||
| 627 | continue; | ||
| 628 | } | ||
| 629 | |||
| 630 | auto* npad = controller.shared_memory; | ||
| 631 | const auto& motion_state = controller.device->GetMotions(); | ||
| 632 | auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; | ||
| 633 | auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; | ||
| 634 | auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; | ||
| 635 | auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; | ||
| 636 | auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; | ||
| 637 | auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; | ||
| 638 | |||
| 639 | // Clear previous state | ||
| 640 | sixaxis_fullkey_state = {}; | ||
| 641 | sixaxis_handheld_state = {}; | ||
| 642 | sixaxis_dual_left_state = {}; | ||
| 643 | sixaxis_dual_right_state = {}; | ||
| 644 | sixaxis_left_lifo_state = {}; | ||
| 645 | sixaxis_right_lifo_state = {}; | ||
| 646 | |||
| 647 | if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { | ||
| 648 | controller.sixaxis_at_rest = true; | ||
| 649 | for (std::size_t e = 0; e < motion_state.size(); ++e) { | ||
| 650 | controller.sixaxis_at_rest = | ||
| 651 | controller.sixaxis_at_rest && motion_state[e].is_at_rest; | ||
| 652 | } | ||
| 653 | } | ||
| 654 | |||
| 655 | const auto set_motion_state = [&](SixAxisSensorState& state, | ||
| 656 | const Core::HID::ControllerMotion& hid_state) { | ||
| 657 | using namespace std::literals::chrono_literals; | ||
| 658 | static constexpr SixAxisSensorState default_motion_state = { | ||
| 659 | .delta_time = std::chrono::nanoseconds(5ms).count(), | ||
| 660 | .accel = {0, 0, -1.0f}, | ||
| 661 | .orientation = | ||
| 662 | { | ||
| 663 | Common::Vec3f{1.0f, 0, 0}, | ||
| 664 | Common::Vec3f{0, 1.0f, 0}, | ||
| 665 | Common::Vec3f{0, 0, 1.0f}, | ||
| 666 | }, | ||
| 667 | .attribute = {1}, | ||
| 668 | }; | ||
| 669 | if (!controller.sixaxis_sensor_enabled) { | ||
| 670 | state = default_motion_state; | ||
| 671 | return; | ||
| 672 | } | ||
| 673 | if (!Settings::values.motion_enabled.GetValue()) { | ||
| 674 | state = default_motion_state; | ||
| 675 | return; | ||
| 676 | } | ||
| 677 | state.attribute.is_connected.Assign(1); | ||
| 678 | state.delta_time = std::chrono::nanoseconds(5ms).count(); | ||
| 679 | state.accel = hid_state.accel; | ||
| 680 | state.gyro = hid_state.gyro; | ||
| 681 | state.rotation = hid_state.rotation; | ||
| 682 | state.orientation = hid_state.orientation; | ||
| 683 | }; | ||
| 684 | |||
| 685 | switch (controller_type) { | ||
| 686 | case Core::HID::NpadStyleIndex::None: | ||
| 687 | ASSERT(false); | ||
| 688 | break; | ||
| 689 | case Core::HID::NpadStyleIndex::ProController: | ||
| 690 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 691 | break; | ||
| 692 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 693 | set_motion_state(sixaxis_handheld_state, motion_state[0]); | ||
| 694 | break; | ||
| 695 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 696 | set_motion_state(sixaxis_dual_left_state, motion_state[0]); | ||
| 697 | set_motion_state(sixaxis_dual_right_state, motion_state[1]); | ||
| 698 | break; | ||
| 699 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 700 | set_motion_state(sixaxis_left_lifo_state, motion_state[0]); | ||
| 701 | break; | ||
| 702 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 703 | set_motion_state(sixaxis_right_lifo_state, motion_state[1]); | ||
| 704 | break; | ||
| 705 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 706 | using namespace std::literals::chrono_literals; | ||
| 707 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 708 | sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); | ||
| 709 | break; | ||
| 710 | default: | ||
| 711 | break; | ||
| 712 | } | ||
| 713 | |||
| 714 | sixaxis_fullkey_state.sampling_number = | ||
| 715 | npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 716 | sixaxis_handheld_state.sampling_number = | ||
| 717 | npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 718 | sixaxis_dual_left_state.sampling_number = | ||
| 719 | npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 720 | sixaxis_dual_right_state.sampling_number = | ||
| 721 | npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 722 | sixaxis_left_lifo_state.sampling_number = | ||
| 723 | npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 724 | sixaxis_right_lifo_state.sampling_number = | ||
| 725 | npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 726 | |||
| 727 | if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { | ||
| 728 | // This buffer only is updated on handheld on HW | ||
| 729 | npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); | ||
| 730 | } else { | ||
| 731 | // Handheld doesn't update this buffer on HW | ||
| 732 | npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); | ||
| 733 | } | ||
| 734 | |||
| 735 | npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); | ||
| 736 | npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); | ||
| 737 | npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); | ||
| 738 | npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); | ||
| 739 | } | ||
| 740 | } | ||
| 741 | |||
| 742 | void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { | ||
| 743 | hid_core.SetSupportedStyleTag(style_set); | 567 | hid_core.SetSupportedStyleTag(style_set); |
| 744 | 568 | ||
| 745 | if (is_controller_initialized) { | 569 | if (is_controller_initialized) { |
| @@ -750,14 +574,14 @@ void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { | |||
| 750 | is_controller_initialized = true; | 574 | is_controller_initialized = true; |
| 751 | } | 575 | } |
| 752 | 576 | ||
| 753 | Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { | 577 | Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const { |
| 754 | if (!is_controller_initialized) { | 578 | if (!is_controller_initialized) { |
| 755 | return {Core::HID::NpadStyleSet::None}; | 579 | return {Core::HID::NpadStyleSet::None}; |
| 756 | } | 580 | } |
| 757 | return hid_core.GetSupportedStyleTag(); | 581 | return hid_core.GetSupportedStyleTag(); |
| 758 | } | 582 | } |
| 759 | 583 | ||
| 760 | Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { | 584 | Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { |
| 761 | constexpr std::size_t max_number_npad_ids = 0xa; | 585 | constexpr std::size_t max_number_npad_ids = 0xa; |
| 762 | const auto length = data.size(); | 586 | const auto length = data.size(); |
| 763 | ASSERT(length > 0 && (length % sizeof(u32)) == 0); | 587 | ASSERT(length > 0 && (length % sizeof(u32)) == 0); |
| @@ -773,17 +597,17 @@ Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { | |||
| 773 | return ResultSuccess; | 597 | return ResultSuccess; |
| 774 | } | 598 | } |
| 775 | 599 | ||
| 776 | void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { | 600 | void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { |
| 777 | const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); | 601 | const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); |
| 778 | ASSERT(max_length <= copy_amount); | 602 | ASSERT(max_length <= copy_amount); |
| 779 | std::memcpy(data, supported_npad_id_types.data(), copy_amount); | 603 | std::memcpy(data, supported_npad_id_types.data(), copy_amount); |
| 780 | } | 604 | } |
| 781 | 605 | ||
| 782 | std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { | 606 | std::size_t NPad::GetSupportedNpadIdTypesSize() const { |
| 783 | return supported_npad_id_types.size(); | 607 | return supported_npad_id_types.size(); |
| 784 | } | 608 | } |
| 785 | 609 | ||
| 786 | void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { | 610 | void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { |
| 787 | if (joy_hold_type != NpadJoyHoldType::Horizontal && | 611 | if (joy_hold_type != NpadJoyHoldType::Horizontal && |
| 788 | joy_hold_type != NpadJoyHoldType::Vertical) { | 612 | joy_hold_type != NpadJoyHoldType::Vertical) { |
| 789 | LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}", | 613 | LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}", |
| @@ -793,11 +617,11 @@ void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { | |||
| 793 | hold_type = joy_hold_type; | 617 | hold_type = joy_hold_type; |
| 794 | } | 618 | } |
| 795 | 619 | ||
| 796 | Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { | 620 | NPad::NpadJoyHoldType NPad::GetHoldType() const { |
| 797 | return hold_type; | 621 | return hold_type; |
| 798 | } | 622 | } |
| 799 | 623 | ||
| 800 | void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { | 624 | void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { |
| 801 | if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { | 625 | if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { |
| 802 | ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); | 626 | ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); |
| 803 | return; | 627 | return; |
| @@ -806,21 +630,20 @@ void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode a | |||
| 806 | handheld_activation_mode = activation_mode; | 630 | handheld_activation_mode = activation_mode; |
| 807 | } | 631 | } |
| 808 | 632 | ||
| 809 | Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const { | 633 | NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const { |
| 810 | return handheld_activation_mode; | 634 | return handheld_activation_mode; |
| 811 | } | 635 | } |
| 812 | 636 | ||
| 813 | void Controller_NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { | 637 | void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { |
| 814 | communication_mode = communication_mode_; | 638 | communication_mode = communication_mode_; |
| 815 | } | 639 | } |
| 816 | 640 | ||
| 817 | Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode() const { | 641 | NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const { |
| 818 | return communication_mode; | 642 | return communication_mode; |
| 819 | } | 643 | } |
| 820 | 644 | ||
| 821 | bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, | 645 | bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, |
| 822 | NpadJoyDeviceType npad_device_type, | 646 | NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) { |
| 823 | NpadJoyAssignmentMode assignment_mode) { | ||
| 824 | if (!IsNpadIdValid(npad_id)) { | 647 | if (!IsNpadIdValid(npad_id)) { |
| 825 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 648 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 826 | return false; | 649 | return false; |
| @@ -889,9 +712,8 @@ bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID: | |||
| 889 | return true; | 712 | return true; |
| 890 | } | 713 | } |
| 891 | 714 | ||
| 892 | bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, | 715 | bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, |
| 893 | std::size_t device_index, | 716 | const Core::HID::VibrationValue& vibration_value) { |
| 894 | const Core::HID::VibrationValue& vibration_value) { | ||
| 895 | auto& controller = GetControllerFromNpadIdType(npad_id); | 717 | auto& controller = GetControllerFromNpadIdType(npad_id); |
| 896 | if (!controller.device->IsConnected()) { | 718 | if (!controller.device->IsConnected()) { |
| 897 | return false; | 719 | return false; |
| @@ -935,10 +757,9 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, | |||
| 935 | return controller.device->SetVibration(device_index, vibration); | 757 | return controller.device->SetVibration(device_index, vibration); |
| 936 | } | 758 | } |
| 937 | 759 | ||
| 938 | void Controller_NPad::VibrateController( | 760 | void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle, |
| 939 | const Core::HID::VibrationDeviceHandle& vibration_device_handle, | 761 | const Core::HID::VibrationValue& vibration_value) { |
| 940 | const Core::HID::VibrationValue& vibration_value) { | 762 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { |
| 941 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { | ||
| 942 | return; | 763 | return; |
| 943 | } | 764 | } |
| 944 | 765 | ||
| @@ -982,7 +803,7 @@ void Controller_NPad::VibrateController( | |||
| 982 | } | 803 | } |
| 983 | } | 804 | } |
| 984 | 805 | ||
| 985 | void Controller_NPad::VibrateControllers( | 806 | void NPad::VibrateControllers( |
| 986 | std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, | 807 | std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, |
| 987 | std::span<const Core::HID::VibrationValue> vibration_values) { | 808 | std::span<const Core::HID::VibrationValue> vibration_values) { |
| 988 | if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { | 809 | if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { |
| @@ -999,9 +820,9 @@ void Controller_NPad::VibrateControllers( | |||
| 999 | } | 820 | } |
| 1000 | } | 821 | } |
| 1001 | 822 | ||
| 1002 | Core::HID::VibrationValue Controller_NPad::GetLastVibration( | 823 | Core::HID::VibrationValue NPad::GetLastVibration( |
| 1003 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { | 824 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { |
| 1004 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { | 825 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { |
| 1005 | return {}; | 826 | return {}; |
| 1006 | } | 827 | } |
| 1007 | 828 | ||
| @@ -1010,9 +831,9 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration( | |||
| 1010 | return controller.vibration[device_index].latest_vibration_value; | 831 | return controller.vibration[device_index].latest_vibration_value; |
| 1011 | } | 832 | } |
| 1012 | 833 | ||
| 1013 | void Controller_NPad::InitializeVibrationDevice( | 834 | void NPad::InitializeVibrationDevice( |
| 1014 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) { | 835 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) { |
| 1015 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { | 836 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { |
| 1016 | return; | 837 | return; |
| 1017 | } | 838 | } |
| 1018 | 839 | ||
| @@ -1021,8 +842,8 @@ void Controller_NPad::InitializeVibrationDevice( | |||
| 1021 | InitializeVibrationDeviceAtIndex(npad_index, device_index); | 842 | InitializeVibrationDeviceAtIndex(npad_index, device_index); |
| 1022 | } | 843 | } |
| 1023 | 844 | ||
| 1024 | void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, | 845 | void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, |
| 1025 | std::size_t device_index) { | 846 | std::size_t device_index) { |
| 1026 | auto& controller = GetControllerFromNpadIdType(npad_id); | 847 | auto& controller = GetControllerFromNpadIdType(npad_id); |
| 1027 | if (!Settings::values.vibration_enabled.GetValue()) { | 848 | if (!Settings::values.vibration_enabled.GetValue()) { |
| 1028 | controller.vibration[device_index].device_mounted = false; | 849 | controller.vibration[device_index].device_mounted = false; |
| @@ -1033,13 +854,13 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa | |||
| 1033 | controller.device->IsVibrationEnabled(device_index); | 854 | controller.device->IsVibrationEnabled(device_index); |
| 1034 | } | 855 | } |
| 1035 | 856 | ||
| 1036 | void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { | 857 | void NPad::SetPermitVibrationSession(bool permit_vibration_session) { |
| 1037 | permit_vibration_session_enabled = permit_vibration_session; | 858 | permit_vibration_session_enabled = permit_vibration_session; |
| 1038 | } | 859 | } |
| 1039 | 860 | ||
| 1040 | bool Controller_NPad::IsVibrationDeviceMounted( | 861 | bool NPad::IsVibrationDeviceMounted( |
| 1041 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { | 862 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { |
| 1042 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { | 863 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { |
| 1043 | return false; | 864 | return false; |
| 1044 | } | 865 | } |
| 1045 | 866 | ||
| @@ -1048,7 +869,7 @@ bool Controller_NPad::IsVibrationDeviceMounted( | |||
| 1048 | return controller.vibration[device_index].device_mounted; | 869 | return controller.vibration[device_index].device_mounted; |
| 1049 | } | 870 | } |
| 1050 | 871 | ||
| 1051 | Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { | 872 | Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { |
| 1052 | if (!IsNpadIdValid(npad_id)) { | 873 | if (!IsNpadIdValid(npad_id)) { |
| 1053 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 874 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1054 | // Fallback to player 1 | 875 | // Fallback to player 1 |
| @@ -1060,18 +881,17 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad | |||
| 1060 | return controller.styleset_changed_event->GetReadableEvent(); | 881 | return controller.styleset_changed_event->GetReadableEvent(); |
| 1061 | } | 882 | } |
| 1062 | 883 | ||
| 1063 | void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { | 884 | void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { |
| 1064 | const auto& controller = GetControllerFromNpadIdType(npad_id); | 885 | const auto& controller = GetControllerFromNpadIdType(npad_id); |
| 1065 | controller.styleset_changed_event->Signal(); | 886 | controller.styleset_changed_event->Signal(); |
| 1066 | } | 887 | } |
| 1067 | 888 | ||
| 1068 | void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, | 889 | void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) { |
| 1069 | Core::HID::NpadIdType npad_id) { | ||
| 1070 | UpdateControllerAt(controller, npad_id, true); | 890 | UpdateControllerAt(controller, npad_id, true); |
| 1071 | } | 891 | } |
| 1072 | 892 | ||
| 1073 | void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, | 893 | void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id, |
| 1074 | Core::HID::NpadIdType npad_id, bool connected) { | 894 | bool connected) { |
| 1075 | auto& controller = GetControllerFromNpadIdType(npad_id); | 895 | auto& controller = GetControllerFromNpadIdType(npad_id); |
| 1076 | if (!connected) { | 896 | if (!connected) { |
| 1077 | DisconnectNpad(npad_id); | 897 | DisconnectNpad(npad_id); |
| @@ -1082,7 +902,7 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, | |||
| 1082 | InitNewlyAddedController(npad_id); | 902 | InitNewlyAddedController(npad_id); |
| 1083 | } | 903 | } |
| 1084 | 904 | ||
| 1085 | Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { | 905 | Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { |
| 1086 | if (!IsNpadIdValid(npad_id)) { | 906 | if (!IsNpadIdValid(npad_id)) { |
| 1087 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 907 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1088 | return InvalidNpadId; | 908 | return InvalidNpadId; |
| @@ -1108,9 +928,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { | |||
| 1108 | shared_memory->sixaxis_dual_right_properties.raw = 0; | 928 | shared_memory->sixaxis_dual_right_properties.raw = 0; |
| 1109 | shared_memory->sixaxis_left_properties.raw = 0; | 929 | shared_memory->sixaxis_left_properties.raw = 0; |
| 1110 | shared_memory->sixaxis_right_properties.raw = 0; | 930 | shared_memory->sixaxis_right_properties.raw = 0; |
| 1111 | shared_memory->battery_level_dual = 0; | 931 | shared_memory->battery_level_dual = Core::HID::NpadBatteryLevel::Empty; |
| 1112 | shared_memory->battery_level_left = 0; | 932 | shared_memory->battery_level_left = Core::HID::NpadBatteryLevel::Empty; |
| 1113 | shared_memory->battery_level_right = 0; | 933 | shared_memory->battery_level_right = Core::HID::NpadBatteryLevel::Empty; |
| 1114 | shared_memory->fullkey_color = { | 934 | shared_memory->fullkey_color = { |
| 1115 | .attribute = ColorAttribute::NoController, | 935 | .attribute = ColorAttribute::NoController, |
| 1116 | .fullkey = {}, | 936 | .fullkey = {}, |
| @@ -1131,54 +951,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { | |||
| 1131 | return ResultSuccess; | 951 | return ResultSuccess; |
| 1132 | } | 952 | } |
| 1133 | 953 | ||
| 1134 | Result Controller_NPad::SetGyroscopeZeroDriftMode( | 954 | Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor( |
| 1135 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 1136 | Core::HID::GyroscopeZeroDriftMode drift_mode) { | ||
| 1137 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1138 | if (is_valid.IsError()) { | ||
| 1139 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1140 | return is_valid; | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1144 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1145 | sixaxis.gyroscope_zero_drift_mode = drift_mode; | ||
| 1146 | controller.device->SetGyroscopeZeroDriftMode(drift_mode); | ||
| 1147 | |||
| 1148 | return ResultSuccess; | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | Result Controller_NPad::GetGyroscopeZeroDriftMode( | ||
| 1152 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 1153 | Core::HID::GyroscopeZeroDriftMode& drift_mode) const { | ||
| 1154 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1155 | if (is_valid.IsError()) { | ||
| 1156 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1157 | return is_valid; | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1161 | drift_mode = sixaxis.gyroscope_zero_drift_mode; | ||
| 1162 | |||
| 1163 | return ResultSuccess; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 1167 | bool& is_at_rest) const { | ||
| 1168 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1169 | if (is_valid.IsError()) { | ||
| 1170 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1171 | return is_valid; | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1175 | is_at_rest = controller.sixaxis_at_rest; | ||
| 1176 | return ResultSuccess; | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor( | ||
| 1180 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { | 955 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { |
| 1181 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | 956 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); |
| 1182 | if (is_valid.IsError()) { | 957 | if (is_valid.IsError()) { |
| 1183 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | 958 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); |
| 1184 | return is_valid; | 959 | return is_valid; |
| @@ -1189,65 +964,9 @@ Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor( | |||
| 1189 | return ResultSuccess; | 964 | return ResultSuccess; |
| 1190 | } | 965 | } |
| 1191 | 966 | ||
| 1192 | Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough( | 967 | Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( |
| 1193 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) { | ||
| 1194 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1195 | if (is_valid.IsError()) { | ||
| 1196 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1197 | return is_valid; | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1201 | sixaxis.unaltered_passtrough = is_enabled; | ||
| 1202 | return ResultSuccess; | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled( | ||
| 1206 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const { | ||
| 1207 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1208 | if (is_valid.IsError()) { | ||
| 1209 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1210 | return is_valid; | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1214 | is_enabled = sixaxis.unaltered_passtrough; | ||
| 1215 | return ResultSuccess; | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | Result Controller_NPad::LoadSixAxisSensorCalibrationParameter( | ||
| 1219 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 1220 | Core::HID::SixAxisSensorCalibrationParameter& calibration) const { | ||
| 1221 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1222 | if (is_valid.IsError()) { | ||
| 1223 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1224 | return is_valid; | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | // TODO: Request this data to the controller. On error return 0xd8ca | ||
| 1228 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1229 | calibration = sixaxis.calibration; | ||
| 1230 | return ResultSuccess; | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | Result Controller_NPad::GetSixAxisSensorIcInformation( | ||
| 1234 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 1235 | Core::HID::SixAxisSensorIcInformation& ic_information) const { | ||
| 1236 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1237 | if (is_valid.IsError()) { | ||
| 1238 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1239 | return is_valid; | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | // TODO: Request this data to the controller. On error return 0xd8ca | ||
| 1243 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1244 | ic_information = sixaxis.ic_information; | ||
| 1245 | return ResultSuccess; | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( | ||
| 1249 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { | 968 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { |
| 1250 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | 969 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); |
| 1251 | if (is_valid.IsError()) { | 970 | if (is_valid.IsError()) { |
| 1252 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | 971 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); |
| 1253 | return is_valid; | 972 | return is_valid; |
| @@ -1259,83 +978,32 @@ Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( | |||
| 1259 | return ResultSuccess; | 978 | return ResultSuccess; |
| 1260 | } | 979 | } |
| 1261 | 980 | ||
| 1262 | Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 981 | NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) { |
| 1263 | bool sixaxis_status) { | 982 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo; |
| 1264 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1265 | if (is_valid.IsError()) { | ||
| 1266 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1267 | return is_valid; | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1271 | controller.sixaxis_sensor_enabled = sixaxis_status; | ||
| 1272 | return ResultSuccess; | ||
| 1273 | } | 983 | } |
| 1274 | 984 | ||
| 1275 | Result Controller_NPad::IsSixAxisSensorFusionEnabled( | 985 | NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) { |
| 1276 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const { | 986 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo; |
| 1277 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1278 | if (is_valid.IsError()) { | ||
| 1279 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1280 | return is_valid; | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1284 | is_fusion_enabled = sixaxis.is_fusion_enabled; | ||
| 1285 | |||
| 1286 | return ResultSuccess; | ||
| 1287 | } | 987 | } |
| 1288 | Result Controller_NPad::SetSixAxisFusionEnabled( | ||
| 1289 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) { | ||
| 1290 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1291 | if (is_valid.IsError()) { | ||
| 1292 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1293 | return is_valid; | ||
| 1294 | } | ||
| 1295 | 988 | ||
| 1296 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | 989 | NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) { |
| 1297 | sixaxis.is_fusion_enabled = is_fusion_enabled; | 990 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo; |
| 1298 | |||
| 1299 | return ResultSuccess; | ||
| 1300 | } | 991 | } |
| 1301 | 992 | ||
| 1302 | Result Controller_NPad::SetSixAxisFusionParameters( | 993 | NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) { |
| 1303 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 994 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo; |
| 1304 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { | ||
| 1305 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1306 | if (is_valid.IsError()) { | ||
| 1307 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1308 | return is_valid; | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | const auto param1 = sixaxis_fusion_parameters.parameter1; | ||
| 1312 | if (param1 < 0.0f || param1 > 1.0f) { | ||
| 1313 | return InvalidSixAxisFusionRange; | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1317 | sixaxis.fusion = sixaxis_fusion_parameters; | ||
| 1318 | |||
| 1319 | return ResultSuccess; | ||
| 1320 | } | 995 | } |
| 1321 | 996 | ||
| 1322 | Result Controller_NPad::GetSixAxisFusionParameters( | 997 | NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) { |
| 1323 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 998 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo; |
| 1324 | Core::HID::SixAxisSensorFusionParameters& parameters) const { | 999 | } |
| 1325 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1326 | if (is_valid.IsError()) { | ||
| 1327 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1328 | return is_valid; | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1332 | parameters = sixaxis.fusion; | ||
| 1333 | 1000 | ||
| 1334 | return ResultSuccess; | 1001 | NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) { |
| 1002 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo; | ||
| 1335 | } | 1003 | } |
| 1336 | 1004 | ||
| 1337 | Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, | 1005 | Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, |
| 1338 | Core::HID::NpadIdType npad_id_2) { | 1006 | Core::HID::NpadIdType npad_id_2) { |
| 1339 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { | 1007 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { |
| 1340 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, | 1008 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, |
| 1341 | npad_id_2); | 1009 | npad_id_2); |
| @@ -1397,18 +1065,17 @@ Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, | |||
| 1397 | return ResultSuccess; | 1065 | return ResultSuccess; |
| 1398 | } | 1066 | } |
| 1399 | 1067 | ||
| 1400 | void Controller_NPad::StartLRAssignmentMode() { | 1068 | void NPad::StartLRAssignmentMode() { |
| 1401 | // Nothing internally is used for lr assignment mode. Since we have the ability to set the | 1069 | // Nothing internally is used for lr assignment mode. Since we have the ability to set the |
| 1402 | // controller types from boot, it doesn't really matter about showing a selection screen | 1070 | // controller types from boot, it doesn't really matter about showing a selection screen |
| 1403 | is_in_lr_assignment_mode = true; | 1071 | is_in_lr_assignment_mode = true; |
| 1404 | } | 1072 | } |
| 1405 | 1073 | ||
| 1406 | void Controller_NPad::StopLRAssignmentMode() { | 1074 | void NPad::StopLRAssignmentMode() { |
| 1407 | is_in_lr_assignment_mode = false; | 1075 | is_in_lr_assignment_mode = false; |
| 1408 | } | 1076 | } |
| 1409 | 1077 | ||
| 1410 | Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, | 1078 | Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) { |
| 1411 | Core::HID::NpadIdType npad_id_2) { | ||
| 1412 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { | 1079 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { |
| 1413 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, | 1080 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, |
| 1414 | npad_id_2); | 1081 | npad_id_2); |
| @@ -1439,8 +1106,7 @@ Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, | |||
| 1439 | return ResultSuccess; | 1106 | return ResultSuccess; |
| 1440 | } | 1107 | } |
| 1441 | 1108 | ||
| 1442 | Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, | 1109 | Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const { |
| 1443 | Core::HID::LedPattern& pattern) const { | ||
| 1444 | if (!IsNpadIdValid(npad_id)) { | 1110 | if (!IsNpadIdValid(npad_id)) { |
| 1445 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 1111 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1446 | return InvalidNpadId; | 1112 | return InvalidNpadId; |
| @@ -1450,8 +1116,8 @@ Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, | |||
| 1450 | return ResultSuccess; | 1116 | return ResultSuccess; |
| 1451 | } | 1117 | } |
| 1452 | 1118 | ||
| 1453 | Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, | 1119 | Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, |
| 1454 | bool& is_valid) const { | 1120 | bool& is_valid) const { |
| 1455 | if (!IsNpadIdValid(npad_id)) { | 1121 | if (!IsNpadIdValid(npad_id)) { |
| 1456 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 1122 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1457 | return InvalidNpadId; | 1123 | return InvalidNpadId; |
| @@ -1461,8 +1127,8 @@ Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID:: | |||
| 1461 | return ResultSuccess; | 1127 | return ResultSuccess; |
| 1462 | } | 1128 | } |
| 1463 | 1129 | ||
| 1464 | Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( | 1130 | Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, |
| 1465 | bool is_protection_enabled, Core::HID::NpadIdType npad_id) { | 1131 | Core::HID::NpadIdType npad_id) { |
| 1466 | if (!IsNpadIdValid(npad_id)) { | 1132 | if (!IsNpadIdValid(npad_id)) { |
| 1467 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 1133 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1468 | return InvalidNpadId; | 1134 | return InvalidNpadId; |
| @@ -1472,11 +1138,11 @@ Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( | |||
| 1472 | return ResultSuccess; | 1138 | return ResultSuccess; |
| 1473 | } | 1139 | } |
| 1474 | 1140 | ||
| 1475 | void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { | 1141 | void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { |
| 1476 | analog_stick_use_center_clamp = use_center_clamp; | 1142 | analog_stick_use_center_clamp = use_center_clamp; |
| 1477 | } | 1143 | } |
| 1478 | 1144 | ||
| 1479 | void Controller_NPad::ClearAllConnectedControllers() { | 1145 | void NPad::ClearAllConnectedControllers() { |
| 1480 | for (auto& controller : controller_data) { | 1146 | for (auto& controller : controller_data) { |
| 1481 | if (controller.device->IsConnected() && | 1147 | if (controller.device->IsConnected() && |
| 1482 | controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { | 1148 | controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { |
| @@ -1486,13 +1152,13 @@ void Controller_NPad::ClearAllConnectedControllers() { | |||
| 1486 | } | 1152 | } |
| 1487 | } | 1153 | } |
| 1488 | 1154 | ||
| 1489 | void Controller_NPad::DisconnectAllConnectedControllers() { | 1155 | void NPad::DisconnectAllConnectedControllers() { |
| 1490 | for (auto& controller : controller_data) { | 1156 | for (auto& controller : controller_data) { |
| 1491 | controller.device->Disconnect(); | 1157 | controller.device->Disconnect(); |
| 1492 | } | 1158 | } |
| 1493 | } | 1159 | } |
| 1494 | 1160 | ||
| 1495 | void Controller_NPad::ConnectAllDisconnectedControllers() { | 1161 | void NPad::ConnectAllDisconnectedControllers() { |
| 1496 | for (auto& controller : controller_data) { | 1162 | for (auto& controller : controller_data) { |
| 1497 | if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && | 1163 | if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && |
| 1498 | !controller.device->IsConnected()) { | 1164 | !controller.device->IsConnected()) { |
| @@ -1501,18 +1167,18 @@ void Controller_NPad::ConnectAllDisconnectedControllers() { | |||
| 1501 | } | 1167 | } |
| 1502 | } | 1168 | } |
| 1503 | 1169 | ||
| 1504 | void Controller_NPad::ClearAllControllers() { | 1170 | void NPad::ClearAllControllers() { |
| 1505 | for (auto& controller : controller_data) { | 1171 | for (auto& controller : controller_data) { |
| 1506 | controller.device->Disconnect(); | 1172 | controller.device->Disconnect(); |
| 1507 | controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); | 1173 | controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); |
| 1508 | } | 1174 | } |
| 1509 | } | 1175 | } |
| 1510 | 1176 | ||
| 1511 | Core::HID::NpadButton Controller_NPad::GetAndResetPressState() { | 1177 | Core::HID::NpadButton NPad::GetAndResetPressState() { |
| 1512 | return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); | 1178 | return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); |
| 1513 | } | 1179 | } |
| 1514 | 1180 | ||
| 1515 | void Controller_NPad::ApplyNpadSystemCommonPolicy() { | 1181 | void NPad::ApplyNpadSystemCommonPolicy() { |
| 1516 | Core::HID::NpadStyleTag styletag{}; | 1182 | Core::HID::NpadStyleTag styletag{}; |
| 1517 | styletag.fullkey.Assign(1); | 1183 | styletag.fullkey.Assign(1); |
| 1518 | styletag.handheld.Assign(1); | 1184 | styletag.handheld.Assign(1); |
| @@ -1537,7 +1203,7 @@ void Controller_NPad::ApplyNpadSystemCommonPolicy() { | |||
| 1537 | supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; | 1203 | supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; |
| 1538 | } | 1204 | } |
| 1539 | 1205 | ||
| 1540 | bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { | 1206 | bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { |
| 1541 | if (controller == Core::HID::NpadStyleIndex::Handheld) { | 1207 | if (controller == Core::HID::NpadStyleIndex::Handheld) { |
| 1542 | const bool support_handheld = | 1208 | const bool support_handheld = |
| 1543 | std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), | 1209 | std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), |
| @@ -1588,51 +1254,50 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller | |||
| 1588 | return false; | 1254 | return false; |
| 1589 | } | 1255 | } |
| 1590 | 1256 | ||
| 1591 | Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | 1257 | NPad::NpadControllerData& NPad::GetControllerFromHandle( |
| 1592 | const Core::HID::SixAxisSensorHandle& device_handle) { | 1258 | const Core::HID::VibrationDeviceHandle& device_handle) { |
| 1593 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | 1259 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); |
| 1594 | return GetControllerFromNpadIdType(npad_id); | 1260 | return GetControllerFromNpadIdType(npad_id); |
| 1595 | } | 1261 | } |
| 1596 | 1262 | ||
| 1597 | const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | 1263 | const NPad::NpadControllerData& NPad::GetControllerFromHandle( |
| 1598 | const Core::HID::SixAxisSensorHandle& device_handle) const { | 1264 | const Core::HID::VibrationDeviceHandle& device_handle) const { |
| 1599 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | 1265 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); |
| 1600 | return GetControllerFromNpadIdType(npad_id); | 1266 | return GetControllerFromNpadIdType(npad_id); |
| 1601 | } | 1267 | } |
| 1602 | 1268 | ||
| 1603 | Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | 1269 | NPad::NpadControllerData& NPad::GetControllerFromHandle( |
| 1604 | const Core::HID::VibrationDeviceHandle& device_handle) { | 1270 | const Core::HID::SixAxisSensorHandle& device_handle) { |
| 1605 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | 1271 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); |
| 1606 | return GetControllerFromNpadIdType(npad_id); | 1272 | return GetControllerFromNpadIdType(npad_id); |
| 1607 | } | 1273 | } |
| 1608 | 1274 | ||
| 1609 | const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | 1275 | const NPad::NpadControllerData& NPad::GetControllerFromHandle( |
| 1610 | const Core::HID::VibrationDeviceHandle& device_handle) const { | 1276 | const Core::HID::SixAxisSensorHandle& device_handle) const { |
| 1611 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | 1277 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); |
| 1612 | return GetControllerFromNpadIdType(npad_id); | 1278 | return GetControllerFromNpadIdType(npad_id); |
| 1613 | } | 1279 | } |
| 1614 | 1280 | ||
| 1615 | Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( | 1281 | NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) { |
| 1616 | Core::HID::NpadIdType npad_id) { | ||
| 1617 | if (!IsNpadIdValid(npad_id)) { | 1282 | if (!IsNpadIdValid(npad_id)) { |
| 1618 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 1283 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1619 | npad_id = Core::HID::NpadIdType::Player1; | 1284 | npad_id = Core::HID::NpadIdType::Player1; |
| 1620 | } | 1285 | } |
| 1621 | const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); | 1286 | const auto npad_index = NpadIdTypeToIndex(npad_id); |
| 1622 | return controller_data[npad_index]; | 1287 | return controller_data[npad_index]; |
| 1623 | } | 1288 | } |
| 1624 | 1289 | ||
| 1625 | const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( | 1290 | const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType( |
| 1626 | Core::HID::NpadIdType npad_id) const { | 1291 | Core::HID::NpadIdType npad_id) const { |
| 1627 | if (!IsNpadIdValid(npad_id)) { | 1292 | if (!IsNpadIdValid(npad_id)) { |
| 1628 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 1293 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1629 | npad_id = Core::HID::NpadIdType::Player1; | 1294 | npad_id = Core::HID::NpadIdType::Player1; |
| 1630 | } | 1295 | } |
| 1631 | const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); | 1296 | const auto npad_index = NpadIdTypeToIndex(npad_id); |
| 1632 | return controller_data[npad_index]; | 1297 | return controller_data[npad_index]; |
| 1633 | } | 1298 | } |
| 1634 | 1299 | ||
| 1635 | Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( | 1300 | Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( |
| 1636 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { | 1301 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { |
| 1637 | auto& controller = GetControllerFromHandle(sixaxis_handle); | 1302 | auto& controller = GetControllerFromHandle(sixaxis_handle); |
| 1638 | switch (sixaxis_handle.npad_type) { | 1303 | switch (sixaxis_handle.npad_type) { |
| @@ -1655,7 +1320,7 @@ Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( | |||
| 1655 | } | 1320 | } |
| 1656 | } | 1321 | } |
| 1657 | 1322 | ||
| 1658 | const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( | 1323 | const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( |
| 1659 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { | 1324 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { |
| 1660 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | 1325 | const auto& controller = GetControllerFromHandle(sixaxis_handle); |
| 1661 | switch (sixaxis_handle.npad_type) { | 1326 | switch (sixaxis_handle.npad_type) { |
| @@ -1678,50 +1343,13 @@ const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( | |||
| 1678 | } | 1343 | } |
| 1679 | } | 1344 | } |
| 1680 | 1345 | ||
| 1681 | Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( | 1346 | NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) { |
| 1682 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { | 1347 | const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory; |
| 1683 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1684 | switch (sixaxis_handle.npad_type) { | ||
| 1685 | case Core::HID::NpadStyleIndex::ProController: | ||
| 1686 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 1687 | return controller.sixaxis_fullkey; | ||
| 1688 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 1689 | return controller.sixaxis_handheld; | ||
| 1690 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 1691 | if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { | ||
| 1692 | return controller.sixaxis_dual_left; | ||
| 1693 | } | ||
| 1694 | return controller.sixaxis_dual_right; | ||
| 1695 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 1696 | return controller.sixaxis_left; | ||
| 1697 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 1698 | return controller.sixaxis_right; | ||
| 1699 | default: | ||
| 1700 | return controller.sixaxis_unknown; | ||
| 1701 | } | ||
| 1702 | } | ||
| 1703 | 1348 | ||
| 1704 | const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( | 1349 | return { |
| 1705 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { | 1350 | .ui_variant = 0, |
| 1706 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | 1351 | .footer = shared_memory->applet_footer_type, |
| 1707 | switch (sixaxis_handle.npad_type) { | 1352 | }; |
| 1708 | case Core::HID::NpadStyleIndex::ProController: | ||
| 1709 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 1710 | return controller.sixaxis_fullkey; | ||
| 1711 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 1712 | return controller.sixaxis_handheld; | ||
| 1713 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 1714 | if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { | ||
| 1715 | return controller.sixaxis_dual_left; | ||
| 1716 | } | ||
| 1717 | return controller.sixaxis_dual_right; | ||
| 1718 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 1719 | return controller.sixaxis_left; | ||
| 1720 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 1721 | return controller.sixaxis_right; | ||
| 1722 | default: | ||
| 1723 | return controller.sixaxis_unknown; | ||
| 1724 | } | ||
| 1725 | } | 1353 | } |
| 1726 | 1354 | ||
| 1727 | } // namespace Service::HID | 1355 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 949e58a4c..9167c93f0 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | 10 | ||
| 11 | #include "common/bit_field.h" | 11 | #include "common/bit_field.h" |
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/vector_math.h" | ||
| 14 | 13 | ||
| 15 | #include "core/hid/hid_types.h" | 14 | #include "core/hid/hid_types.h" |
| 16 | #include "core/hle/service/hid/controllers/controller_base.h" | 15 | #include "core/hle/service/hid/controllers/controller_base.h" |
| @@ -34,11 +33,11 @@ union Result; | |||
| 34 | 33 | ||
| 35 | namespace Service::HID { | 34 | namespace Service::HID { |
| 36 | 35 | ||
| 37 | class Controller_NPad final : public ControllerBase { | 36 | class NPad final : public ControllerBase { |
| 38 | public: | 37 | public: |
| 39 | explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, | 38 | explicit NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, |
| 40 | KernelHelpers::ServiceContext& service_context_); | 39 | KernelHelpers::ServiceContext& service_context_); |
| 41 | ~Controller_NPad() override; | 40 | ~NPad() override; |
| 42 | 41 | ||
| 43 | // Called when the controller is initialized | 42 | // Called when the controller is initialized |
| 44 | void OnInit() override; | 43 | void OnInit() override; |
| @@ -49,9 +48,6 @@ public: | |||
| 49 | // When the controller is requesting an update for the shared memory | 48 | // When the controller is requesting an update for the shared memory |
| 50 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | 49 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; |
| 51 | 50 | ||
| 52 | // When the controller is requesting a motion update for the shared memory | ||
| 53 | void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 54 | |||
| 55 | // This is nn::hid::NpadJoyHoldType | 51 | // This is nn::hid::NpadJoyHoldType |
| 56 | enum class NpadJoyHoldType : u64 { | 52 | enum class NpadJoyHoldType : u64 { |
| 57 | Vertical = 0, | 53 | Vertical = 0, |
| @@ -78,6 +74,46 @@ public: | |||
| 78 | MaxActivationMode = 3, | 74 | MaxActivationMode = 3, |
| 79 | }; | 75 | }; |
| 80 | 76 | ||
| 77 | // This is nn::hid::system::AppletFooterUiAttributesSet | ||
| 78 | struct AppletFooterUiAttributes { | ||
| 79 | INSERT_PADDING_BYTES(0x4); | ||
| 80 | }; | ||
| 81 | |||
| 82 | // This is nn::hid::system::AppletFooterUiType | ||
| 83 | enum class AppletFooterUiType : u8 { | ||
| 84 | None = 0, | ||
| 85 | HandheldNone = 1, | ||
| 86 | HandheldJoyConLeftOnly = 2, | ||
| 87 | HandheldJoyConRightOnly = 3, | ||
| 88 | HandheldJoyConLeftJoyConRight = 4, | ||
| 89 | JoyDual = 5, | ||
| 90 | JoyDualLeftOnly = 6, | ||
| 91 | JoyDualRightOnly = 7, | ||
| 92 | JoyLeftHorizontal = 8, | ||
| 93 | JoyLeftVertical = 9, | ||
| 94 | JoyRightHorizontal = 10, | ||
| 95 | JoyRightVertical = 11, | ||
| 96 | SwitchProController = 12, | ||
| 97 | CompatibleProController = 13, | ||
| 98 | CompatibleJoyCon = 14, | ||
| 99 | LarkHvc1 = 15, | ||
| 100 | LarkHvc2 = 16, | ||
| 101 | LarkNesLeft = 17, | ||
| 102 | LarkNesRight = 18, | ||
| 103 | Lucia = 19, | ||
| 104 | Verification = 20, | ||
| 105 | Lagon = 21, | ||
| 106 | }; | ||
| 107 | |||
| 108 | using AppletFooterUiVariant = u8; | ||
| 109 | |||
| 110 | // This is "nn::hid::system::AppletDetailedUiType". | ||
| 111 | struct AppletDetailedUiType { | ||
| 112 | AppletFooterUiVariant ui_variant; | ||
| 113 | INSERT_PADDING_BYTES(0x2); | ||
| 114 | AppletFooterUiType footer; | ||
| 115 | }; | ||
| 116 | static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size"); | ||
| 81 | // This is nn::hid::NpadCommunicationMode | 117 | // This is nn::hid::NpadCommunicationMode |
| 82 | enum class NpadCommunicationMode : u64 { | 118 | enum class NpadCommunicationMode : u64 { |
| 83 | Mode_5ms = 0, | 119 | Mode_5ms = 0, |
| @@ -86,6 +122,15 @@ public: | |||
| 86 | Default = 3, | 122 | Default = 3, |
| 87 | }; | 123 | }; |
| 88 | 124 | ||
| 125 | enum class NpadRevision : u32 { | ||
| 126 | Revision0 = 0, | ||
| 127 | Revision1 = 1, | ||
| 128 | Revision2 = 2, | ||
| 129 | Revision3 = 3, | ||
| 130 | }; | ||
| 131 | |||
| 132 | using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>; | ||
| 133 | |||
| 89 | void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); | 134 | void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); |
| 90 | Core::HID::NpadStyleTag GetSupportedStyleSet() const; | 135 | Core::HID::NpadStyleTag GetSupportedStyleSet() const; |
| 91 | 136 | ||
| @@ -138,37 +183,18 @@ public: | |||
| 138 | 183 | ||
| 139 | Result DisconnectNpad(Core::HID::NpadIdType npad_id); | 184 | Result DisconnectNpad(Core::HID::NpadIdType npad_id); |
| 140 | 185 | ||
| 141 | Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 142 | Core::HID::GyroscopeZeroDriftMode drift_mode); | ||
| 143 | Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 144 | Core::HID::GyroscopeZeroDriftMode& drift_mode) const; | ||
| 145 | Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 146 | bool& is_at_rest) const; | ||
| 147 | Result IsFirmwareUpdateAvailableForSixAxisSensor( | 186 | Result IsFirmwareUpdateAvailableForSixAxisSensor( |
| 148 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; | 187 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; |
| 149 | Result EnableSixAxisSensorUnalteredPassthrough( | ||
| 150 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled); | ||
| 151 | Result IsSixAxisSensorUnalteredPassthroughEnabled( | ||
| 152 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const; | ||
| 153 | Result LoadSixAxisSensorCalibrationParameter( | ||
| 154 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 155 | Core::HID::SixAxisSensorCalibrationParameter& calibration) const; | ||
| 156 | Result GetSixAxisSensorIcInformation( | ||
| 157 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 158 | Core::HID::SixAxisSensorIcInformation& ic_information) const; | ||
| 159 | Result ResetIsSixAxisSensorDeviceNewlyAssigned( | 188 | Result ResetIsSixAxisSensorDeviceNewlyAssigned( |
| 160 | const Core::HID::SixAxisSensorHandle& sixaxis_handle); | 189 | const Core::HID::SixAxisSensorHandle& sixaxis_handle); |
| 161 | Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 190 | |
| 162 | bool sixaxis_status); | 191 | SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id); |
| 163 | Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 192 | SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id); |
| 164 | bool& is_fusion_enabled) const; | 193 | SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id); |
| 165 | Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 194 | SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id); |
| 166 | bool is_fusion_enabled); | 195 | SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id); |
| 167 | Result SetSixAxisFusionParameters( | 196 | SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id); |
| 168 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 197 | |
| 169 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); | ||
| 170 | Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 171 | Core::HID::SixAxisSensorFusionParameters& parameters) const; | ||
| 172 | Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; | 198 | Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; |
| 173 | Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, | 199 | Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, |
| 174 | bool& is_enabled) const; | 200 | bool& is_enabled) const; |
| @@ -192,10 +218,7 @@ public: | |||
| 192 | 218 | ||
| 193 | void ApplyNpadSystemCommonPolicy(); | 219 | void ApplyNpadSystemCommonPolicy(); |
| 194 | 220 | ||
| 195 | static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); | 221 | AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); |
| 196 | static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); | ||
| 197 | static Result VerifyValidSixAxisSensorHandle( | ||
| 198 | const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 199 | 222 | ||
| 200 | private: | 223 | private: |
| 201 | static constexpr std::size_t NPAD_COUNT = 10; | 224 | static constexpr std::size_t NPAD_COUNT = 10; |
| @@ -254,29 +277,6 @@ private: | |||
| 254 | }; | 277 | }; |
| 255 | static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); | 278 | static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); |
| 256 | 279 | ||
| 257 | // This is nn::hid::SixAxisSensorAttribute | ||
| 258 | struct SixAxisSensorAttribute { | ||
| 259 | union { | ||
| 260 | u32 raw{}; | ||
| 261 | BitField<0, 1, u32> is_connected; | ||
| 262 | BitField<1, 1, u32> is_interpolated; | ||
| 263 | }; | ||
| 264 | }; | ||
| 265 | static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size"); | ||
| 266 | |||
| 267 | // This is nn::hid::SixAxisSensorState | ||
| 268 | struct SixAxisSensorState { | ||
| 269 | s64 delta_time{}; | ||
| 270 | s64 sampling_number{}; | ||
| 271 | Common::Vec3f accel{}; | ||
| 272 | Common::Vec3f gyro{}; | ||
| 273 | Common::Vec3f rotation{}; | ||
| 274 | std::array<Common::Vec3f, 3> orientation{}; | ||
| 275 | SixAxisSensorAttribute attribute{}; | ||
| 276 | INSERT_PADDING_BYTES(4); // Reserved | ||
| 277 | }; | ||
| 278 | static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); | ||
| 279 | |||
| 280 | // This is nn::hid::server::NpadGcTriggerState | 280 | // This is nn::hid::server::NpadGcTriggerState |
| 281 | struct NpadGcTriggerState { | 281 | struct NpadGcTriggerState { |
| 282 | s64 sampling_number{}; | 282 | s64 sampling_number{}; |
| @@ -353,37 +353,6 @@ private: | |||
| 353 | static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, | 353 | static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, |
| 354 | "NfcXcdDeviceHandleStateImpl is an invalid size"); | 354 | "NfcXcdDeviceHandleStateImpl is an invalid size"); |
| 355 | 355 | ||
| 356 | // This is nn::hid::system::AppletFooterUiAttributesSet | ||
| 357 | struct AppletFooterUiAttributes { | ||
| 358 | INSERT_PADDING_BYTES(0x4); | ||
| 359 | }; | ||
| 360 | |||
| 361 | // This is nn::hid::system::AppletFooterUiType | ||
| 362 | enum class AppletFooterUiType : u8 { | ||
| 363 | None = 0, | ||
| 364 | HandheldNone = 1, | ||
| 365 | HandheldJoyConLeftOnly = 2, | ||
| 366 | HandheldJoyConRightOnly = 3, | ||
| 367 | HandheldJoyConLeftJoyConRight = 4, | ||
| 368 | JoyDual = 5, | ||
| 369 | JoyDualLeftOnly = 6, | ||
| 370 | JoyDualRightOnly = 7, | ||
| 371 | JoyLeftHorizontal = 8, | ||
| 372 | JoyLeftVertical = 9, | ||
| 373 | JoyRightHorizontal = 10, | ||
| 374 | JoyRightVertical = 11, | ||
| 375 | SwitchProController = 12, | ||
| 376 | CompatibleProController = 13, | ||
| 377 | CompatibleJoyCon = 14, | ||
| 378 | LarkHvc1 = 15, | ||
| 379 | LarkHvc2 = 16, | ||
| 380 | LarkNesLeft = 17, | ||
| 381 | LarkNesRight = 18, | ||
| 382 | Lucia = 19, | ||
| 383 | Verification = 20, | ||
| 384 | Lagon = 21, | ||
| 385 | }; | ||
| 386 | |||
| 387 | // This is nn::hid::NpadLarkType | 356 | // This is nn::hid::NpadLarkType |
| 388 | enum class NpadLarkType : u32 { | 357 | enum class NpadLarkType : u32 { |
| 389 | Invalid, | 358 | Invalid, |
| @@ -427,12 +396,12 @@ private: | |||
| 427 | Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; | 396 | Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; |
| 428 | Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; | 397 | Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; |
| 429 | Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; | 398 | Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; |
| 430 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; | 399 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; |
| 431 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; | 400 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; |
| 432 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; | 401 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; |
| 433 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; | 402 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; |
| 434 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; | 403 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; |
| 435 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; | 404 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; |
| 436 | DeviceType device_type{}; | 405 | DeviceType device_type{}; |
| 437 | INSERT_PADDING_BYTES(0x4); // Reserved | 406 | INSERT_PADDING_BYTES(0x4); // Reserved |
| 438 | NPadSystemProperties system_properties{}; | 407 | NPadSystemProperties system_properties{}; |
| @@ -466,16 +435,6 @@ private: | |||
| 466 | std::chrono::steady_clock::time_point last_vibration_timepoint{}; | 435 | std::chrono::steady_clock::time_point last_vibration_timepoint{}; |
| 467 | }; | 436 | }; |
| 468 | 437 | ||
| 469 | struct SixaxisParameters { | ||
| 470 | bool is_fusion_enabled{true}; | ||
| 471 | bool unaltered_passtrough{false}; | ||
| 472 | Core::HID::SixAxisSensorFusionParameters fusion{}; | ||
| 473 | Core::HID::SixAxisSensorCalibrationParameter calibration{}; | ||
| 474 | Core::HID::SixAxisSensorIcInformation ic_information{}; | ||
| 475 | Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{ | ||
| 476 | Core::HID::GyroscopeZeroDriftMode::Standard}; | ||
| 477 | }; | ||
| 478 | |||
| 479 | struct NpadControllerData { | 438 | struct NpadControllerData { |
| 480 | Kernel::KEvent* styleset_changed_event{}; | 439 | Kernel::KEvent* styleset_changed_event{}; |
| 481 | NpadInternalState* shared_memory = nullptr; | 440 | NpadInternalState* shared_memory = nullptr; |
| @@ -489,27 +448,10 @@ private: | |||
| 489 | bool is_dual_left_connected{true}; | 448 | bool is_dual_left_connected{true}; |
| 490 | bool is_dual_right_connected{true}; | 449 | bool is_dual_right_connected{true}; |
| 491 | 450 | ||
| 492 | // Motion parameters | ||
| 493 | bool sixaxis_at_rest{true}; | ||
| 494 | bool sixaxis_sensor_enabled{true}; | ||
| 495 | SixaxisParameters sixaxis_fullkey{}; | ||
| 496 | SixaxisParameters sixaxis_handheld{}; | ||
| 497 | SixaxisParameters sixaxis_dual_left{}; | ||
| 498 | SixaxisParameters sixaxis_dual_right{}; | ||
| 499 | SixaxisParameters sixaxis_left{}; | ||
| 500 | SixaxisParameters sixaxis_right{}; | ||
| 501 | SixaxisParameters sixaxis_unknown{}; | ||
| 502 | |||
| 503 | // Current pad state | 451 | // Current pad state |
| 504 | NPadGenericState npad_pad_state{}; | 452 | NPadGenericState npad_pad_state{}; |
| 505 | NPadGenericState npad_libnx_state{}; | 453 | NPadGenericState npad_libnx_state{}; |
| 506 | NpadGcTriggerState npad_trigger_state{}; | 454 | NpadGcTriggerState npad_trigger_state{}; |
| 507 | SixAxisSensorState sixaxis_fullkey_state{}; | ||
| 508 | SixAxisSensorState sixaxis_handheld_state{}; | ||
| 509 | SixAxisSensorState sixaxis_dual_left_state{}; | ||
| 510 | SixAxisSensorState sixaxis_dual_right_state{}; | ||
| 511 | SixAxisSensorState sixaxis_left_lifo_state{}; | ||
| 512 | SixAxisSensorState sixaxis_right_lifo_state{}; | ||
| 513 | int callback_key{}; | 455 | int callback_key{}; |
| 514 | }; | 456 | }; |
| 515 | 457 | ||
| @@ -520,13 +462,13 @@ private: | |||
| 520 | void WriteEmptyEntry(NpadInternalState* npad); | 462 | void WriteEmptyEntry(NpadInternalState* npad); |
| 521 | 463 | ||
| 522 | NpadControllerData& GetControllerFromHandle( | 464 | NpadControllerData& GetControllerFromHandle( |
| 523 | const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 524 | const NpadControllerData& GetControllerFromHandle( | ||
| 525 | const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 526 | NpadControllerData& GetControllerFromHandle( | ||
| 527 | const Core::HID::VibrationDeviceHandle& device_handle); | 465 | const Core::HID::VibrationDeviceHandle& device_handle); |
| 528 | const NpadControllerData& GetControllerFromHandle( | 466 | const NpadControllerData& GetControllerFromHandle( |
| 529 | const Core::HID::VibrationDeviceHandle& device_handle) const; | 467 | const Core::HID::VibrationDeviceHandle& device_handle) const; |
| 468 | NpadControllerData& GetControllerFromHandle( | ||
| 469 | const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 470 | const NpadControllerData& GetControllerFromHandle( | ||
| 471 | const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 530 | NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); | 472 | NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); |
| 531 | const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; | 473 | const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; |
| 532 | 474 | ||
| @@ -534,9 +476,6 @@ private: | |||
| 534 | const Core::HID::SixAxisSensorHandle& device_handle); | 476 | const Core::HID::SixAxisSensorHandle& device_handle); |
| 535 | const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( | 477 | const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( |
| 536 | const Core::HID::SixAxisSensorHandle& device_handle) const; | 478 | const Core::HID::SixAxisSensorHandle& device_handle) const; |
| 537 | SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 538 | const SixaxisParameters& GetSixaxisState( | ||
| 539 | const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 540 | 479 | ||
| 541 | std::atomic<u64> press_state{}; | 480 | std::atomic<u64> press_state{}; |
| 542 | 481 | ||
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp index 73a2a2b91..588ff9d62 100644 --- a/src/core/hle/service/hid/controllers/palma.cpp +++ b/src/core/hle/service/hid/controllers/palma.cpp | |||
| @@ -12,43 +12,43 @@ | |||
| 12 | 12 | ||
| 13 | namespace Service::HID { | 13 | namespace Service::HID { |
| 14 | 14 | ||
| 15 | Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, | 15 | Palma::Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, |
| 16 | KernelHelpers::ServiceContext& service_context_) | 16 | KernelHelpers::ServiceContext& service_context_) |
| 17 | : ControllerBase{hid_core_}, service_context{service_context_} { | 17 | : ControllerBase{hid_core_}, service_context{service_context_} { |
| 18 | controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); | 18 | controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); |
| 19 | operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); | 19 | operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | Controller_Palma::~Controller_Palma() { | 22 | Palma::~Palma() { |
| 23 | service_context.CloseEvent(operation_complete_event); | 23 | service_context.CloseEvent(operation_complete_event); |
| 24 | }; | 24 | }; |
| 25 | 25 | ||
| 26 | void Controller_Palma::OnInit() {} | 26 | void Palma::OnInit() {} |
| 27 | 27 | ||
| 28 | void Controller_Palma::OnRelease() {} | 28 | void Palma::OnRelease() {} |
| 29 | 29 | ||
| 30 | void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 30 | void Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 31 | if (!IsControllerActivated()) { | 31 | if (!IsControllerActivated()) { |
| 32 | return; | 32 | return; |
| 33 | } | 33 | } |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, | 36 | Result Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, |
| 37 | PalmaConnectionHandle& handle) { | 37 | PalmaConnectionHandle& handle) { |
| 38 | active_handle.npad_id = npad_id; | 38 | active_handle.npad_id = npad_id; |
| 39 | handle = active_handle; | 39 | handle = active_handle; |
| 40 | return ResultSuccess; | 40 | return ResultSuccess; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) { | 43 | Result Palma::InitializePalma(const PalmaConnectionHandle& handle) { |
| 44 | if (handle.npad_id != active_handle.npad_id) { | 44 | if (handle.npad_id != active_handle.npad_id) { |
| 45 | return InvalidPalmaHandle; | 45 | return InvalidPalmaHandle; |
| 46 | } | 46 | } |
| 47 | ActivateController(); | 47 | Activate(); |
| 48 | return ResultSuccess; | 48 | return ResultSuccess; |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( | 51 | Kernel::KReadableEvent& Palma::AcquirePalmaOperationCompleteEvent( |
| 52 | const PalmaConnectionHandle& handle) const { | 52 | const PalmaConnectionHandle& handle) const { |
| 53 | if (handle.npad_id != active_handle.npad_id) { | 53 | if (handle.npad_id != active_handle.npad_id) { |
| 54 | LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); | 54 | LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); |
| @@ -56,9 +56,9 @@ Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( | |||
| 56 | return operation_complete_event->GetReadableEvent(); | 56 | return operation_complete_event->GetReadableEvent(); |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, | 59 | Result Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, |
| 60 | PalmaOperationType& operation_type, | 60 | PalmaOperationType& operation_type, |
| 61 | PalmaOperationData& data) const { | 61 | PalmaOperationData& data) const { |
| 62 | if (handle.npad_id != active_handle.npad_id) { | 62 | if (handle.npad_id != active_handle.npad_id) { |
| 63 | return InvalidPalmaHandle; | 63 | return InvalidPalmaHandle; |
| 64 | } | 64 | } |
| @@ -67,8 +67,7 @@ Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& hand | |||
| 67 | return ResultSuccess; | 67 | return ResultSuccess; |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, | 70 | Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity) { |
| 71 | u64 palma_activity) { | ||
| 72 | if (handle.npad_id != active_handle.npad_id) { | 71 | if (handle.npad_id != active_handle.npad_id) { |
| 73 | return InvalidPalmaHandle; | 72 | return InvalidPalmaHandle; |
| 74 | } | 73 | } |
| @@ -79,8 +78,7 @@ Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, | |||
| 79 | return ResultSuccess; | 78 | return ResultSuccess; |
| 80 | } | 79 | } |
| 81 | 80 | ||
| 82 | Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, | 81 | Result Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_) { |
| 83 | PalmaFrModeType fr_mode_) { | ||
| 84 | if (handle.npad_id != active_handle.npad_id) { | 82 | if (handle.npad_id != active_handle.npad_id) { |
| 85 | return InvalidPalmaHandle; | 83 | return InvalidPalmaHandle; |
| 86 | } | 84 | } |
| @@ -88,7 +86,7 @@ Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, | |||
| 88 | return ResultSuccess; | 86 | return ResultSuccess; |
| 89 | } | 87 | } |
| 90 | 88 | ||
| 91 | Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { | 89 | Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { |
| 92 | if (handle.npad_id != active_handle.npad_id) { | 90 | if (handle.npad_id != active_handle.npad_id) { |
| 93 | return InvalidPalmaHandle; | 91 | return InvalidPalmaHandle; |
| 94 | } | 92 | } |
| @@ -99,25 +97,25 @@ Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { | |||
| 99 | return ResultSuccess; | 97 | return ResultSuccess; |
| 100 | } | 98 | } |
| 101 | 99 | ||
| 102 | Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { | 100 | Result Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { |
| 103 | if (handle.npad_id != active_handle.npad_id) { | 101 | if (handle.npad_id != active_handle.npad_id) { |
| 104 | return InvalidPalmaHandle; | 102 | return InvalidPalmaHandle; |
| 105 | } | 103 | } |
| 106 | return ResultSuccess; | 104 | return ResultSuccess; |
| 107 | } | 105 | } |
| 108 | 106 | ||
| 109 | Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { | 107 | Result Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { |
| 110 | if (handle.npad_id != active_handle.npad_id) { | 108 | if (handle.npad_id != active_handle.npad_id) { |
| 111 | return InvalidPalmaHandle; | 109 | return InvalidPalmaHandle; |
| 112 | } | 110 | } |
| 113 | return ResultSuccess; | 111 | return ResultSuccess; |
| 114 | } | 112 | } |
| 115 | 113 | ||
| 116 | void Controller_Palma::ReadPalmaApplicationSection() {} | 114 | void Palma::ReadPalmaApplicationSection() {} |
| 117 | 115 | ||
| 118 | void Controller_Palma::WritePalmaApplicationSection() {} | 116 | void Palma::WritePalmaApplicationSection() {} |
| 119 | 117 | ||
| 120 | Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { | 118 | Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { |
| 121 | if (handle.npad_id != active_handle.npad_id) { | 119 | if (handle.npad_id != active_handle.npad_id) { |
| 122 | return InvalidPalmaHandle; | 120 | return InvalidPalmaHandle; |
| 123 | } | 121 | } |
| @@ -128,7 +126,7 @@ Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle | |||
| 128 | return ResultSuccess; | 126 | return ResultSuccess; |
| 129 | } | 127 | } |
| 130 | 128 | ||
| 131 | Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { | 129 | Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { |
| 132 | if (handle.npad_id != active_handle.npad_id) { | 130 | if (handle.npad_id != active_handle.npad_id) { |
| 133 | return InvalidPalmaHandle; | 131 | return InvalidPalmaHandle; |
| 134 | } | 132 | } |
| @@ -139,10 +137,9 @@ Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& | |||
| 139 | return ResultSuccess; | 137 | return ResultSuccess; |
| 140 | } | 138 | } |
| 141 | 139 | ||
| 142 | void Controller_Palma::WritePalmaActivityEntry() {} | 140 | void Palma::WritePalmaActivityEntry() {} |
| 143 | 141 | ||
| 144 | Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, | 142 | Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown) { |
| 145 | u64 unknown) { | ||
| 146 | if (handle.npad_id != active_handle.npad_id) { | 143 | if (handle.npad_id != active_handle.npad_id) { |
| 147 | return InvalidPalmaHandle; | 144 | return InvalidPalmaHandle; |
| 148 | } | 145 | } |
| @@ -153,8 +150,8 @@ Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandl | |||
| 153 | return ResultSuccess; | 150 | return ResultSuccess; |
| 154 | } | 151 | } |
| 155 | 152 | ||
| 156 | Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, | 153 | Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, |
| 157 | Common::ProcessAddress t_mem, u64 size) { | 154 | Common::ProcessAddress t_mem, u64 size) { |
| 158 | if (handle.npad_id != active_handle.npad_id) { | 155 | if (handle.npad_id != active_handle.npad_id) { |
| 159 | return InvalidPalmaHandle; | 156 | return InvalidPalmaHandle; |
| 160 | } | 157 | } |
| @@ -165,8 +162,8 @@ Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle | |||
| 165 | return ResultSuccess; | 162 | return ResultSuccess; |
| 166 | } | 163 | } |
| 167 | 164 | ||
| 168 | Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, | 165 | Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, |
| 169 | s32 database_id_version_) { | 166 | s32 database_id_version_) { |
| 170 | if (handle.npad_id != active_handle.npad_id) { | 167 | if (handle.npad_id != active_handle.npad_id) { |
| 171 | return InvalidPalmaHandle; | 168 | return InvalidPalmaHandle; |
| 172 | } | 169 | } |
| @@ -178,8 +175,7 @@ Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnec | |||
| 178 | return ResultSuccess; | 175 | return ResultSuccess; |
| 179 | } | 176 | } |
| 180 | 177 | ||
| 181 | Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( | 178 | Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle) { |
| 182 | const PalmaConnectionHandle& handle) { | ||
| 183 | if (handle.npad_id != active_handle.npad_id) { | 179 | if (handle.npad_id != active_handle.npad_id) { |
| 184 | return InvalidPalmaHandle; | 180 | return InvalidPalmaHandle; |
| 185 | } | 181 | } |
| @@ -191,26 +187,26 @@ Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( | |||
| 191 | return ResultSuccess; | 187 | return ResultSuccess; |
| 192 | } | 188 | } |
| 193 | 189 | ||
| 194 | void Controller_Palma::SuspendPalmaFeature() {} | 190 | void Palma::SuspendPalmaFeature() {} |
| 195 | 191 | ||
| 196 | Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { | 192 | Result Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { |
| 197 | if (handle.npad_id != active_handle.npad_id) { | 193 | if (handle.npad_id != active_handle.npad_id) { |
| 198 | return InvalidPalmaHandle; | 194 | return InvalidPalmaHandle; |
| 199 | } | 195 | } |
| 200 | return operation.result; | 196 | return operation.result; |
| 201 | } | 197 | } |
| 202 | void Controller_Palma::ReadPalmaPlayLog() {} | 198 | void Palma::ReadPalmaPlayLog() {} |
| 203 | 199 | ||
| 204 | void Controller_Palma::ResetPalmaPlayLog() {} | 200 | void Palma::ResetPalmaPlayLog() {} |
| 205 | 201 | ||
| 206 | void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { | 202 | void Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { |
| 207 | // If true controllers are able to be paired | 203 | // If true controllers are able to be paired |
| 208 | is_connectable = is_all_connectable; | 204 | is_connectable = is_all_connectable; |
| 209 | } | 205 | } |
| 210 | 206 | ||
| 211 | void Controller_Palma::SetIsPalmaPairedConnectable() {} | 207 | void Palma::SetIsPalmaPairedConnectable() {} |
| 212 | 208 | ||
| 213 | Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { | 209 | Result Palma::PairPalma(const PalmaConnectionHandle& handle) { |
| 214 | if (handle.npad_id != active_handle.npad_id) { | 210 | if (handle.npad_id != active_handle.npad_id) { |
| 215 | return InvalidPalmaHandle; | 211 | return InvalidPalmaHandle; |
| 216 | } | 212 | } |
| @@ -218,14 +214,14 @@ Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { | |||
| 218 | return ResultSuccess; | 214 | return ResultSuccess; |
| 219 | } | 215 | } |
| 220 | 216 | ||
| 221 | void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {} | 217 | void Palma::SetPalmaBoostMode(bool boost_mode) {} |
| 222 | 218 | ||
| 223 | void Controller_Palma::CancelWritePalmaWaveEntry() {} | 219 | void Palma::CancelWritePalmaWaveEntry() {} |
| 224 | 220 | ||
| 225 | void Controller_Palma::EnablePalmaBoostMode() {} | 221 | void Palma::EnablePalmaBoostMode() {} |
| 226 | 222 | ||
| 227 | void Controller_Palma::GetPalmaBluetoothAddress() {} | 223 | void Palma::GetPalmaBluetoothAddress() {} |
| 228 | 224 | ||
| 229 | void Controller_Palma::SetDisallowedPalmaConnection() {} | 225 | void Palma::SetDisallowedPalmaConnection() {} |
| 230 | 226 | ||
| 231 | } // namespace Service::HID | 227 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h index a0491a819..a6047f36a 100644 --- a/src/core/hle/service/hid/controllers/palma.h +++ b/src/core/hle/service/hid/controllers/palma.h | |||
| @@ -23,7 +23,7 @@ class EmulatedController; | |||
| 23 | } // namespace Core::HID | 23 | } // namespace Core::HID |
| 24 | 24 | ||
| 25 | namespace Service::HID { | 25 | namespace Service::HID { |
| 26 | class Controller_Palma final : public ControllerBase { | 26 | class Palma final : public ControllerBase { |
| 27 | public: | 27 | public: |
| 28 | using PalmaOperationData = std::array<u8, 0x140>; | 28 | using PalmaOperationData = std::array<u8, 0x140>; |
| 29 | 29 | ||
| @@ -97,9 +97,9 @@ public: | |||
| 97 | static_assert(sizeof(PalmaConnectionHandle) == 0x8, | 97 | static_assert(sizeof(PalmaConnectionHandle) == 0x8, |
| 98 | "PalmaConnectionHandle has incorrect size."); | 98 | "PalmaConnectionHandle has incorrect size."); |
| 99 | 99 | ||
| 100 | explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, | 100 | explicit Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, |
| 101 | KernelHelpers::ServiceContext& service_context_); | 101 | KernelHelpers::ServiceContext& service_context_); |
| 102 | ~Controller_Palma() override; | 102 | ~Palma() override; |
| 103 | 103 | ||
| 104 | // Called when the controller is initialized | 104 | // Called when the controller is initialized |
| 105 | void OnInit() override; | 105 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/seven_six_axis.cpp index bcb272eaf..495568484 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp +++ b/src/core/hle/service/hid/controllers/seven_six_axis.cpp | |||
| @@ -1,32 +1,29 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #include <cstring> | ||
| 5 | #include "common/common_types.h" | ||
| 4 | #include "core/core.h" | 6 | #include "core/core.h" |
| 5 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | #include "core/frontend/emu_window.h" | ||
| 6 | #include "core/hid/emulated_console.h" | 9 | #include "core/hid/emulated_console.h" |
| 10 | #include "core/hid/emulated_devices.h" | ||
| 7 | #include "core/hid/hid_core.h" | 11 | #include "core/hid/hid_core.h" |
| 8 | #include "core/hle/service/hid/controllers/console_sixaxis.h" | 12 | #include "core/hle/service/hid/controllers/seven_six_axis.h" |
| 9 | #include "core/memory.h" | 13 | #include "core/memory.h" |
| 10 | 14 | ||
| 11 | namespace Service::HID { | 15 | namespace Service::HID { |
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; | 16 | SevenSixAxis::SevenSixAxis(Core::System& system_) |
| 13 | |||
| 14 | Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_) | ||
| 15 | : ControllerBase{system_.HIDCore()}, system{system_} { | 17 | : ControllerBase{system_.HIDCore()}, system{system_} { |
| 16 | console = hid_core.GetEmulatedConsole(); | 18 | console = hid_core.GetEmulatedConsole(); |
| 17 | static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size, | ||
| 18 | "ConsoleSharedMemory is bigger than the shared memory"); | ||
| 19 | shared_memory = std::construct_at( | ||
| 20 | reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); | ||
| 21 | } | 19 | } |
| 22 | 20 | ||
| 23 | Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; | 21 | SevenSixAxis::~SevenSixAxis() = default; |
| 24 | |||
| 25 | void Controller_ConsoleSixAxis::OnInit() {} | ||
| 26 | 22 | ||
| 27 | void Controller_ConsoleSixAxis::OnRelease() {} | 23 | void SevenSixAxis::OnInit() {} |
| 24 | void SevenSixAxis::OnRelease() {} | ||
| 28 | 25 | ||
| 29 | void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 26 | void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 30 | if (!IsControllerActivated() || transfer_memory == 0) { | 27 | if (!IsControllerActivated() || transfer_memory == 0) { |
| 31 | seven_sixaxis_lifo.buffer_count = 0; | 28 | seven_sixaxis_lifo.buffer_count = 0; |
| 32 | seven_sixaxis_lifo.buffer_tail = 0; | 29 | seven_sixaxis_lifo.buffer_tail = 0; |
| @@ -53,22 +50,17 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti | |||
| 53 | -motion_status.quaternion.xyz.z, | 50 | -motion_status.quaternion.xyz.z, |
| 54 | }; | 51 | }; |
| 55 | 52 | ||
| 56 | shared_memory->sampling_number++; | ||
| 57 | shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; | ||
| 58 | shared_memory->verticalization_error = motion_status.verticalization_error; | ||
| 59 | shared_memory->gyro_bias = motion_status.gyro_bias; | ||
| 60 | |||
| 61 | // Update seven six axis transfer memory | ||
| 62 | seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); | 53 | seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); |
| 63 | system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, | 54 | system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, |
| 64 | sizeof(seven_sixaxis_lifo)); | 55 | sizeof(seven_sixaxis_lifo)); |
| 65 | } | 56 | } |
| 66 | 57 | ||
| 67 | void Controller_ConsoleSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { | 58 | void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { |
| 68 | transfer_memory = t_mem; | 59 | transfer_memory = t_mem; |
| 69 | } | 60 | } |
| 70 | 61 | ||
| 71 | void Controller_ConsoleSixAxis::ResetTimestamp() { | 62 | void SevenSixAxis::ResetTimestamp() { |
| 72 | last_saved_timestamp = last_global_timestamp; | 63 | last_saved_timestamp = last_global_timestamp; |
| 73 | } | 64 | } |
| 65 | |||
| 74 | } // namespace Service::HID | 66 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/seven_six_axis.h index 7015d924c..40e3f5d12 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.h +++ b/src/core/hle/service/hid/controllers/seven_six_axis.h | |||
| @@ -1,10 +1,9 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include "common/common_types.h" |
| 7 | |||
| 8 | #include "common/quaternion.h" | 7 | #include "common/quaternion.h" |
| 9 | #include "common/typed_address.h" | 8 | #include "common/typed_address.h" |
| 10 | #include "core/hle/service/hid/controllers/controller_base.h" | 9 | #include "core/hle/service/hid/controllers/controller_base.h" |
| @@ -19,10 +18,10 @@ class EmulatedConsole; | |||
| 19 | } // namespace Core::HID | 18 | } // namespace Core::HID |
| 20 | 19 | ||
| 21 | namespace Service::HID { | 20 | namespace Service::HID { |
| 22 | class Controller_ConsoleSixAxis final : public ControllerBase { | 21 | class SevenSixAxis final : public ControllerBase { |
| 23 | public: | 22 | public: |
| 24 | explicit Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_); | 23 | explicit SevenSixAxis(Core::System& system_); |
| 25 | ~Controller_ConsoleSixAxis() override; | 24 | ~SevenSixAxis() override; |
| 26 | 25 | ||
| 27 | // Called when the controller is initialized | 26 | // Called when the controller is initialized |
| 28 | void OnInit() override; | 27 | void OnInit() override; |
| @@ -51,28 +50,16 @@ private: | |||
| 51 | }; | 50 | }; |
| 52 | static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size"); | 51 | static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size"); |
| 53 | 52 | ||
| 54 | // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat | ||
| 55 | struct ConsoleSharedMemory { | ||
| 56 | u64 sampling_number{}; | ||
| 57 | bool is_seven_six_axis_sensor_at_rest{}; | ||
| 58 | INSERT_PADDING_BYTES(3); // padding | ||
| 59 | f32 verticalization_error{}; | ||
| 60 | Common::Vec3f gyro_bias{}; | ||
| 61 | INSERT_PADDING_BYTES(4); // padding | ||
| 62 | }; | ||
| 63 | static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size"); | ||
| 64 | |||
| 65 | Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; | 53 | Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; |
| 66 | static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); | 54 | static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); |
| 67 | 55 | ||
| 56 | u64 last_saved_timestamp{}; | ||
| 57 | u64 last_global_timestamp{}; | ||
| 58 | |||
| 68 | SevenSixAxisState next_seven_sixaxis_state{}; | 59 | SevenSixAxisState next_seven_sixaxis_state{}; |
| 69 | Common::ProcessAddress transfer_memory{}; | 60 | Common::ProcessAddress transfer_memory{}; |
| 70 | ConsoleSharedMemory* shared_memory = nullptr; | ||
| 71 | Core::HID::EmulatedConsole* console = nullptr; | 61 | Core::HID::EmulatedConsole* console = nullptr; |
| 72 | 62 | ||
| 73 | u64 last_saved_timestamp{}; | ||
| 74 | u64 last_global_timestamp{}; | ||
| 75 | |||
| 76 | Core::System& system; | 63 | Core::System& system; |
| 77 | }; | 64 | }; |
| 78 | } // namespace Service::HID | 65 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/six_axis.cpp b/src/core/hle/service/hid/controllers/six_axis.cpp new file mode 100644 index 000000000..3d24a5c04 --- /dev/null +++ b/src/core/hle/service/hid/controllers/six_axis.cpp | |||
| @@ -0,0 +1,413 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "common/common_types.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hid/emulated_controller.h" | ||
| 7 | #include "core/hid/hid_core.h" | ||
| 8 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 9 | #include "core/hle/service/hid/controllers/six_axis.h" | ||
| 10 | #include "core/hle/service/hid/errors.h" | ||
| 11 | #include "core/hle/service/hid/hid_util.h" | ||
| 12 | |||
| 13 | namespace Service::HID { | ||
| 14 | |||
| 15 | SixAxis::SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_) | ||
| 16 | : ControllerBase{hid_core_}, npad{npad_} { | ||
| 17 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | ||
| 18 | auto& controller = controller_data[i]; | ||
| 19 | controller.device = hid_core.GetEmulatedControllerByIndex(i); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | SixAxis::~SixAxis() = default; | ||
| 24 | |||
| 25 | void SixAxis::OnInit() {} | ||
| 26 | void SixAxis::OnRelease() {} | ||
| 27 | |||
| 28 | void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 29 | if (!IsControllerActivated()) { | ||
| 30 | return; | ||
| 31 | } | ||
| 32 | |||
| 33 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | ||
| 34 | auto& controller = controller_data[i]; | ||
| 35 | |||
| 36 | const auto npad_id = IndexToNpadIdType(i); | ||
| 37 | const auto& controller_type = controller.device->GetNpadStyleIndex(); | ||
| 38 | |||
| 39 | if (controller_type == Core::HID::NpadStyleIndex::None || | ||
| 40 | !controller.device->IsConnected()) { | ||
| 41 | continue; | ||
| 42 | } | ||
| 43 | |||
| 44 | const auto& motion_state = controller.device->GetMotions(); | ||
| 45 | auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; | ||
| 46 | auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; | ||
| 47 | auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; | ||
| 48 | auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; | ||
| 49 | auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; | ||
| 50 | auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; | ||
| 51 | |||
| 52 | auto& sixaxis_fullkey_lifo = npad->GetSixAxisFullkeyLifo(npad_id); | ||
| 53 | auto& sixaxis_handheld_lifo = npad->GetSixAxisHandheldLifo(npad_id); | ||
| 54 | auto& sixaxis_dual_left_lifo = npad->GetSixAxisDualLeftLifo(npad_id); | ||
| 55 | auto& sixaxis_dual_right_lifo = npad->GetSixAxisDualRightLifo(npad_id); | ||
| 56 | auto& sixaxis_left_lifo = npad->GetSixAxisLeftLifo(npad_id); | ||
| 57 | auto& sixaxis_right_lifo = npad->GetSixAxisRightLifo(npad_id); | ||
| 58 | |||
| 59 | // Clear previous state | ||
| 60 | sixaxis_fullkey_state = {}; | ||
| 61 | sixaxis_handheld_state = {}; | ||
| 62 | sixaxis_dual_left_state = {}; | ||
| 63 | sixaxis_dual_right_state = {}; | ||
| 64 | sixaxis_left_lifo_state = {}; | ||
| 65 | sixaxis_right_lifo_state = {}; | ||
| 66 | |||
| 67 | if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { | ||
| 68 | controller.sixaxis_at_rest = true; | ||
| 69 | for (std::size_t e = 0; e < motion_state.size(); ++e) { | ||
| 70 | controller.sixaxis_at_rest = | ||
| 71 | controller.sixaxis_at_rest && motion_state[e].is_at_rest; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state, | ||
| 76 | const Core::HID::ControllerMotion& hid_state) { | ||
| 77 | using namespace std::literals::chrono_literals; | ||
| 78 | static constexpr Core::HID::SixAxisSensorState default_motion_state = { | ||
| 79 | .delta_time = std::chrono::nanoseconds(5ms).count(), | ||
| 80 | .accel = {0, 0, -1.0f}, | ||
| 81 | .orientation = | ||
| 82 | { | ||
| 83 | Common::Vec3f{1.0f, 0, 0}, | ||
| 84 | Common::Vec3f{0, 1.0f, 0}, | ||
| 85 | Common::Vec3f{0, 0, 1.0f}, | ||
| 86 | }, | ||
| 87 | .attribute = {1}, | ||
| 88 | }; | ||
| 89 | if (!controller.sixaxis_sensor_enabled) { | ||
| 90 | state = default_motion_state; | ||
| 91 | return; | ||
| 92 | } | ||
| 93 | if (!Settings::values.motion_enabled.GetValue()) { | ||
| 94 | state = default_motion_state; | ||
| 95 | return; | ||
| 96 | } | ||
| 97 | state.attribute.is_connected.Assign(1); | ||
| 98 | state.delta_time = std::chrono::nanoseconds(5ms).count(); | ||
| 99 | state.accel = hid_state.accel; | ||
| 100 | state.gyro = hid_state.gyro; | ||
| 101 | state.rotation = hid_state.rotation; | ||
| 102 | state.orientation = hid_state.orientation; | ||
| 103 | }; | ||
| 104 | |||
| 105 | switch (controller_type) { | ||
| 106 | case Core::HID::NpadStyleIndex::None: | ||
| 107 | ASSERT(false); | ||
| 108 | break; | ||
| 109 | case Core::HID::NpadStyleIndex::ProController: | ||
| 110 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 111 | break; | ||
| 112 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 113 | set_motion_state(sixaxis_handheld_state, motion_state[0]); | ||
| 114 | break; | ||
| 115 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 116 | set_motion_state(sixaxis_dual_left_state, motion_state[0]); | ||
| 117 | set_motion_state(sixaxis_dual_right_state, motion_state[1]); | ||
| 118 | break; | ||
| 119 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 120 | set_motion_state(sixaxis_left_lifo_state, motion_state[0]); | ||
| 121 | break; | ||
| 122 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 123 | set_motion_state(sixaxis_right_lifo_state, motion_state[1]); | ||
| 124 | break; | ||
| 125 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 126 | using namespace std::literals::chrono_literals; | ||
| 127 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 128 | sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); | ||
| 129 | break; | ||
| 130 | default: | ||
| 131 | break; | ||
| 132 | } | ||
| 133 | |||
| 134 | sixaxis_fullkey_state.sampling_number = | ||
| 135 | sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 136 | sixaxis_handheld_state.sampling_number = | ||
| 137 | sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 138 | sixaxis_dual_left_state.sampling_number = | ||
| 139 | sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 140 | sixaxis_dual_right_state.sampling_number = | ||
| 141 | sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 142 | sixaxis_left_lifo_state.sampling_number = | ||
| 143 | sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 144 | sixaxis_right_lifo_state.sampling_number = | ||
| 145 | sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 146 | |||
| 147 | if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { | ||
| 148 | // This buffer only is updated on handheld on HW | ||
| 149 | sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); | ||
| 150 | } else { | ||
| 151 | // Handheld doesn't update this buffer on HW | ||
| 152 | sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); | ||
| 153 | } | ||
| 154 | |||
| 155 | sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); | ||
| 156 | sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); | ||
| 157 | sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); | ||
| 158 | sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | Result SixAxis::SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 163 | Core::HID::GyroscopeZeroDriftMode drift_mode) { | ||
| 164 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 165 | if (is_valid.IsError()) { | ||
| 166 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 167 | return is_valid; | ||
| 168 | } | ||
| 169 | |||
| 170 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 171 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 172 | sixaxis.gyroscope_zero_drift_mode = drift_mode; | ||
| 173 | controller.device->SetGyroscopeZeroDriftMode(drift_mode); | ||
| 174 | |||
| 175 | return ResultSuccess; | ||
| 176 | } | ||
| 177 | |||
| 178 | Result SixAxis::GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 179 | Core::HID::GyroscopeZeroDriftMode& drift_mode) const { | ||
| 180 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 181 | if (is_valid.IsError()) { | ||
| 182 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 183 | return is_valid; | ||
| 184 | } | ||
| 185 | |||
| 186 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 187 | drift_mode = sixaxis.gyroscope_zero_drift_mode; | ||
| 188 | |||
| 189 | return ResultSuccess; | ||
| 190 | } | ||
| 191 | |||
| 192 | Result SixAxis::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 193 | bool& is_at_rest) const { | ||
| 194 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 195 | if (is_valid.IsError()) { | ||
| 196 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 197 | return is_valid; | ||
| 198 | } | ||
| 199 | |||
| 200 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 201 | is_at_rest = controller.sixaxis_at_rest; | ||
| 202 | return ResultSuccess; | ||
| 203 | } | ||
| 204 | |||
| 205 | Result SixAxis::LoadSixAxisSensorCalibrationParameter( | ||
| 206 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 207 | Core::HID::SixAxisSensorCalibrationParameter& calibration) const { | ||
| 208 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 209 | if (is_valid.IsError()) { | ||
| 210 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 211 | return is_valid; | ||
| 212 | } | ||
| 213 | |||
| 214 | // TODO: Request this data to the controller. On error return 0xd8ca | ||
| 215 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 216 | calibration = sixaxis.calibration; | ||
| 217 | return ResultSuccess; | ||
| 218 | } | ||
| 219 | |||
| 220 | Result SixAxis::GetSixAxisSensorIcInformation( | ||
| 221 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 222 | Core::HID::SixAxisSensorIcInformation& ic_information) const { | ||
| 223 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 224 | if (is_valid.IsError()) { | ||
| 225 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 226 | return is_valid; | ||
| 227 | } | ||
| 228 | |||
| 229 | // TODO: Request this data to the controller. On error return 0xd8ca | ||
| 230 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 231 | ic_information = sixaxis.ic_information; | ||
| 232 | return ResultSuccess; | ||
| 233 | } | ||
| 234 | |||
| 235 | Result SixAxis::EnableSixAxisSensorUnalteredPassthrough( | ||
| 236 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) { | ||
| 237 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 238 | if (is_valid.IsError()) { | ||
| 239 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 240 | return is_valid; | ||
| 241 | } | ||
| 242 | |||
| 243 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 244 | sixaxis.unaltered_passtrough = is_enabled; | ||
| 245 | return ResultSuccess; | ||
| 246 | } | ||
| 247 | |||
| 248 | Result SixAxis::IsSixAxisSensorUnalteredPassthroughEnabled( | ||
| 249 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const { | ||
| 250 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 251 | if (is_valid.IsError()) { | ||
| 252 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 253 | return is_valid; | ||
| 254 | } | ||
| 255 | |||
| 256 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 257 | is_enabled = sixaxis.unaltered_passtrough; | ||
| 258 | return ResultSuccess; | ||
| 259 | } | ||
| 260 | |||
| 261 | Result SixAxis::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 262 | bool sixaxis_status) { | ||
| 263 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 264 | if (is_valid.IsError()) { | ||
| 265 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 266 | return is_valid; | ||
| 267 | } | ||
| 268 | |||
| 269 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 270 | controller.sixaxis_sensor_enabled = sixaxis_status; | ||
| 271 | return ResultSuccess; | ||
| 272 | } | ||
| 273 | |||
| 274 | Result SixAxis::IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 275 | bool& is_fusion_enabled) const { | ||
| 276 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 277 | if (is_valid.IsError()) { | ||
| 278 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 279 | return is_valid; | ||
| 280 | } | ||
| 281 | |||
| 282 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 283 | is_fusion_enabled = sixaxis.is_fusion_enabled; | ||
| 284 | |||
| 285 | return ResultSuccess; | ||
| 286 | } | ||
| 287 | Result SixAxis::SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 288 | bool is_fusion_enabled) { | ||
| 289 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 290 | if (is_valid.IsError()) { | ||
| 291 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 292 | return is_valid; | ||
| 293 | } | ||
| 294 | |||
| 295 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 296 | sixaxis.is_fusion_enabled = is_fusion_enabled; | ||
| 297 | |||
| 298 | return ResultSuccess; | ||
| 299 | } | ||
| 300 | |||
| 301 | Result SixAxis::SetSixAxisFusionParameters( | ||
| 302 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 303 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { | ||
| 304 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 305 | if (is_valid.IsError()) { | ||
| 306 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 307 | return is_valid; | ||
| 308 | } | ||
| 309 | |||
| 310 | const auto param1 = sixaxis_fusion_parameters.parameter1; | ||
| 311 | if (param1 < 0.0f || param1 > 1.0f) { | ||
| 312 | return InvalidSixAxisFusionRange; | ||
| 313 | } | ||
| 314 | |||
| 315 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 316 | sixaxis.fusion = sixaxis_fusion_parameters; | ||
| 317 | |||
| 318 | return ResultSuccess; | ||
| 319 | } | ||
| 320 | |||
| 321 | Result SixAxis::GetSixAxisFusionParameters( | ||
| 322 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 323 | Core::HID::SixAxisSensorFusionParameters& parameters) const { | ||
| 324 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); | ||
| 325 | if (is_valid.IsError()) { | ||
| 326 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 327 | return is_valid; | ||
| 328 | } | ||
| 329 | |||
| 330 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 331 | parameters = sixaxis.fusion; | ||
| 332 | |||
| 333 | return ResultSuccess; | ||
| 334 | } | ||
| 335 | |||
| 336 | SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( | ||
| 337 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { | ||
| 338 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 339 | switch (sixaxis_handle.npad_type) { | ||
| 340 | case Core::HID::NpadStyleIndex::ProController: | ||
| 341 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 342 | return controller.sixaxis_fullkey; | ||
| 343 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 344 | return controller.sixaxis_handheld; | ||
| 345 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 346 | if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { | ||
| 347 | return controller.sixaxis_dual_left; | ||
| 348 | } | ||
| 349 | return controller.sixaxis_dual_right; | ||
| 350 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 351 | return controller.sixaxis_left; | ||
| 352 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 353 | return controller.sixaxis_right; | ||
| 354 | default: | ||
| 355 | return controller.sixaxis_unknown; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( | ||
| 360 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { | ||
| 361 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 362 | switch (sixaxis_handle.npad_type) { | ||
| 363 | case Core::HID::NpadStyleIndex::ProController: | ||
| 364 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 365 | return controller.sixaxis_fullkey; | ||
| 366 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 367 | return controller.sixaxis_handheld; | ||
| 368 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 369 | if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { | ||
| 370 | return controller.sixaxis_dual_left; | ||
| 371 | } | ||
| 372 | return controller.sixaxis_dual_right; | ||
| 373 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 374 | return controller.sixaxis_left; | ||
| 375 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 376 | return controller.sixaxis_right; | ||
| 377 | default: | ||
| 378 | return controller.sixaxis_unknown; | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle( | ||
| 383 | const Core::HID::SixAxisSensorHandle& device_handle) { | ||
| 384 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 385 | return GetControllerFromNpadIdType(npad_id); | ||
| 386 | } | ||
| 387 | |||
| 388 | const SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle( | ||
| 389 | const Core::HID::SixAxisSensorHandle& device_handle) const { | ||
| 390 | const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); | ||
| 391 | return GetControllerFromNpadIdType(npad_id); | ||
| 392 | } | ||
| 393 | |||
| 394 | SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) { | ||
| 395 | if (!IsNpadIdValid(npad_id)) { | ||
| 396 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 397 | npad_id = Core::HID::NpadIdType::Player1; | ||
| 398 | } | ||
| 399 | const auto npad_index = NpadIdTypeToIndex(npad_id); | ||
| 400 | return controller_data[npad_index]; | ||
| 401 | } | ||
| 402 | |||
| 403 | const SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType( | ||
| 404 | Core::HID::NpadIdType npad_id) const { | ||
| 405 | if (!IsNpadIdValid(npad_id)) { | ||
| 406 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | ||
| 407 | npad_id = Core::HID::NpadIdType::Player1; | ||
| 408 | } | ||
| 409 | const auto npad_index = NpadIdTypeToIndex(npad_id); | ||
| 410 | return controller_data[npad_index]; | ||
| 411 | } | ||
| 412 | |||
| 413 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/controllers/six_axis.h b/src/core/hle/service/hid/controllers/six_axis.h new file mode 100644 index 000000000..4c4f5dc7b --- /dev/null +++ b/src/core/hle/service/hid/controllers/six_axis.h | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "core/hid/hid_types.h" | ||
| 8 | #include "core/hle/service/hid/controllers/controller_base.h" | ||
| 9 | #include "core/hle/service/hid/ring_lifo.h" | ||
| 10 | |||
| 11 | namespace Core::HID { | ||
| 12 | class EmulatedController; | ||
| 13 | } // namespace Core::HID | ||
| 14 | |||
| 15 | namespace Service::HID { | ||
| 16 | class NPad; | ||
| 17 | |||
| 18 | class SixAxis final : public ControllerBase { | ||
| 19 | public: | ||
| 20 | explicit SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_); | ||
| 21 | ~SixAxis() override; | ||
| 22 | |||
| 23 | // Called when the controller is initialized | ||
| 24 | void OnInit() override; | ||
| 25 | |||
| 26 | // When the controller is released | ||
| 27 | void OnRelease() override; | ||
| 28 | |||
| 29 | // When the controller is requesting an update for the shared memory | ||
| 30 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 31 | |||
| 32 | Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 33 | Core::HID::GyroscopeZeroDriftMode drift_mode); | ||
| 34 | Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 35 | Core::HID::GyroscopeZeroDriftMode& drift_mode) const; | ||
| 36 | Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 37 | bool& is_at_rest) const; | ||
| 38 | Result EnableSixAxisSensorUnalteredPassthrough( | ||
| 39 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled); | ||
| 40 | Result IsSixAxisSensorUnalteredPassthroughEnabled( | ||
| 41 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const; | ||
| 42 | Result LoadSixAxisSensorCalibrationParameter( | ||
| 43 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 44 | Core::HID::SixAxisSensorCalibrationParameter& calibration) const; | ||
| 45 | Result GetSixAxisSensorIcInformation( | ||
| 46 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 47 | Core::HID::SixAxisSensorIcInformation& ic_information) const; | ||
| 48 | Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 49 | bool sixaxis_status); | ||
| 50 | Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 51 | bool& is_fusion_enabled) const; | ||
| 52 | Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 53 | bool is_fusion_enabled); | ||
| 54 | Result SetSixAxisFusionParameters( | ||
| 55 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 56 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); | ||
| 57 | Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 58 | Core::HID::SixAxisSensorFusionParameters& parameters) const; | ||
| 59 | |||
| 60 | private: | ||
| 61 | static constexpr std::size_t NPAD_COUNT = 10; | ||
| 62 | |||
| 63 | struct SixaxisParameters { | ||
| 64 | bool is_fusion_enabled{true}; | ||
| 65 | bool unaltered_passtrough{false}; | ||
| 66 | Core::HID::SixAxisSensorFusionParameters fusion{}; | ||
| 67 | Core::HID::SixAxisSensorCalibrationParameter calibration{}; | ||
| 68 | Core::HID::SixAxisSensorIcInformation ic_information{}; | ||
| 69 | Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{ | ||
| 70 | Core::HID::GyroscopeZeroDriftMode::Standard}; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct NpadControllerData { | ||
| 74 | Core::HID::EmulatedController* device = nullptr; | ||
| 75 | |||
| 76 | // Motion parameters | ||
| 77 | bool sixaxis_at_rest{true}; | ||
| 78 | bool sixaxis_sensor_enabled{true}; | ||
| 79 | SixaxisParameters sixaxis_fullkey{}; | ||
| 80 | SixaxisParameters sixaxis_handheld{}; | ||
| 81 | SixaxisParameters sixaxis_dual_left{}; | ||
| 82 | SixaxisParameters sixaxis_dual_right{}; | ||
| 83 | SixaxisParameters sixaxis_left{}; | ||
| 84 | SixaxisParameters sixaxis_right{}; | ||
| 85 | SixaxisParameters sixaxis_unknown{}; | ||
| 86 | |||
| 87 | // Current pad state | ||
| 88 | Core::HID::SixAxisSensorState sixaxis_fullkey_state{}; | ||
| 89 | Core::HID::SixAxisSensorState sixaxis_handheld_state{}; | ||
| 90 | Core::HID::SixAxisSensorState sixaxis_dual_left_state{}; | ||
| 91 | Core::HID::SixAxisSensorState sixaxis_dual_right_state{}; | ||
| 92 | Core::HID::SixAxisSensorState sixaxis_left_lifo_state{}; | ||
| 93 | Core::HID::SixAxisSensorState sixaxis_right_lifo_state{}; | ||
| 94 | int callback_key{}; | ||
| 95 | }; | ||
| 96 | |||
| 97 | SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 98 | const SixaxisParameters& GetSixaxisState( | ||
| 99 | const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 100 | |||
| 101 | NpadControllerData& GetControllerFromHandle( | ||
| 102 | const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 103 | const NpadControllerData& GetControllerFromHandle( | ||
| 104 | const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 105 | NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); | ||
| 106 | const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; | ||
| 107 | |||
| 108 | std::shared_ptr<NPad> npad; | ||
| 109 | std::array<NpadControllerData, NPAD_COUNT> controller_data{}; | ||
| 110 | }; | ||
| 111 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 3ef91df4b..3bcf0ee9f 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp | |||
| @@ -15,8 +15,7 @@ | |||
| 15 | namespace Service::HID { | 15 | namespace Service::HID { |
| 16 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; | 16 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; |
| 17 | 17 | ||
| 18 | Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_, | 18 | TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) |
| 19 | u8* raw_shared_memory_) | ||
| 20 | : ControllerBase{hid_core_} { | 19 | : ControllerBase{hid_core_} { |
| 21 | static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, | 20 | static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, |
| 22 | "TouchSharedMemory is bigger than the shared memory"); | 21 | "TouchSharedMemory is bigger than the shared memory"); |
| @@ -25,13 +24,13 @@ Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_, | |||
| 25 | console = hid_core.GetEmulatedConsole(); | 24 | console = hid_core.GetEmulatedConsole(); |
| 26 | } | 25 | } |
| 27 | 26 | ||
| 28 | Controller_Touchscreen::~Controller_Touchscreen() = default; | 27 | TouchScreen::~TouchScreen() = default; |
| 29 | 28 | ||
| 30 | void Controller_Touchscreen::OnInit() {} | 29 | void TouchScreen::OnInit() {} |
| 31 | 30 | ||
| 32 | void Controller_Touchscreen::OnRelease() {} | 31 | void TouchScreen::OnRelease() {} |
| 33 | 32 | ||
| 34 | void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 33 | void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 35 | shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); | 34 | shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); |
| 36 | 35 | ||
| 37 | if (!IsControllerActivated()) { | 36 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index dd00921fd..cd342ce91 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h | |||
| @@ -14,10 +14,10 @@ class EmulatedConsole; | |||
| 14 | } // namespace Core::HID | 14 | } // namespace Core::HID |
| 15 | 15 | ||
| 16 | namespace Service::HID { | 16 | namespace Service::HID { |
| 17 | class Controller_Touchscreen final : public ControllerBase { | 17 | class TouchScreen final : public ControllerBase { |
| 18 | public: | 18 | public: |
| 19 | explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); | 19 | explicit TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); |
| 20 | ~Controller_Touchscreen() override; | 20 | ~TouchScreen() override; |
| 21 | 21 | ||
| 22 | // Called when the controller is initialized | 22 | // Called when the controller is initialized |
| 23 | void OnInit() override; | 23 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index 62119e2c5..0aaed1fa7 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp | |||
| @@ -10,20 +10,19 @@ | |||
| 10 | namespace Service::HID { | 10 | namespace Service::HID { |
| 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; | 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; |
| 12 | 12 | ||
| 13 | Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) | 13 | XPad::XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} { |
| 14 | : ControllerBase{hid_core_} { | ||
| 15 | static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size, | 14 | static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size, |
| 16 | "XpadSharedMemory is bigger than the shared memory"); | 15 | "XpadSharedMemory is bigger than the shared memory"); |
| 17 | shared_memory = std::construct_at( | 16 | shared_memory = std::construct_at( |
| 18 | reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); | 17 | reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); |
| 19 | } | 18 | } |
| 20 | Controller_XPad::~Controller_XPad() = default; | 19 | XPad::~XPad() = default; |
| 21 | 20 | ||
| 22 | void Controller_XPad::OnInit() {} | 21 | void XPad::OnInit() {} |
| 23 | 22 | ||
| 24 | void Controller_XPad::OnRelease() {} | 23 | void XPad::OnRelease() {} |
| 25 | 24 | ||
| 26 | void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 25 | void XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 27 | if (!IsControllerActivated()) { | 26 | if (!IsControllerActivated()) { |
| 28 | shared_memory->basic_xpad_lifo.buffer_count = 0; | 27 | shared_memory->basic_xpad_lifo.buffer_count = 0; |
| 29 | shared_memory->basic_xpad_lifo.buffer_tail = 0; | 28 | shared_memory->basic_xpad_lifo.buffer_tail = 0; |
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index d01dee5fc..9e63a317a 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h | |||
| @@ -10,10 +10,10 @@ | |||
| 10 | #include "core/hle/service/hid/ring_lifo.h" | 10 | #include "core/hle/service/hid/ring_lifo.h" |
| 11 | 11 | ||
| 12 | namespace Service::HID { | 12 | namespace Service::HID { |
| 13 | class Controller_XPad final : public ControllerBase { | 13 | class XPad final : public ControllerBase { |
| 14 | public: | 14 | public: |
| 15 | explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); | 15 | explicit XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); |
| 16 | ~Controller_XPad() override; | 16 | ~XPad() override; |
| 17 | 17 | ||
| 18 | // Called when the controller is initialized | 18 | // Called when the controller is initialized |
| 19 | void OnInit() override; | 19 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 929dd5f03..1b7381d8d 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -1,2864 +1,39 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <array> | ||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "common/settings.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/core_timing.h" | ||
| 10 | #include "core/hid/hid_core.h" | ||
| 11 | #include "core/hle/kernel/k_readable_event.h" | ||
| 12 | #include "core/hle/kernel/k_shared_memory.h" | ||
| 13 | #include "core/hle/kernel/k_transfer_memory.h" | ||
| 14 | #include "core/hle/kernel/kernel.h" | ||
| 15 | #include "core/hle/service/hid/errors.h" | ||
| 16 | #include "core/hle/service/hid/hid.h" | 4 | #include "core/hle/service/hid/hid.h" |
| 5 | #include "core/hle/service/hid/hid_debug_server.h" | ||
| 6 | #include "core/hle/service/hid/hid_firmware_settings.h" | ||
| 7 | #include "core/hle/service/hid/hid_server.h" | ||
| 8 | #include "core/hle/service/hid/hid_system_server.h" | ||
| 17 | #include "core/hle/service/hid/hidbus.h" | 9 | #include "core/hle/service/hid/hidbus.h" |
| 18 | #include "core/hle/service/hid/irs.h" | 10 | #include "core/hle/service/hid/irs.h" |
| 11 | #include "core/hle/service/hid/resource_manager.h" | ||
| 19 | #include "core/hle/service/hid/xcd.h" | 12 | #include "core/hle/service/hid/xcd.h" |
| 20 | #include "core/hle/service/ipc_helpers.h" | ||
| 21 | #include "core/hle/service/server_manager.h" | 13 | #include "core/hle/service/server_manager.h" |
| 22 | #include "core/memory.h" | ||
| 23 | |||
| 24 | #include "core/hle/service/hid/controllers/console_sixaxis.h" | ||
| 25 | #include "core/hle/service/hid/controllers/controller_base.h" | ||
| 26 | #include "core/hle/service/hid/controllers/debug_pad.h" | ||
| 27 | #include "core/hle/service/hid/controllers/gesture.h" | ||
| 28 | #include "core/hle/service/hid/controllers/keyboard.h" | ||
| 29 | #include "core/hle/service/hid/controllers/mouse.h" | ||
| 30 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 31 | #include "core/hle/service/hid/controllers/palma.h" | ||
| 32 | #include "core/hle/service/hid/controllers/stubbed.h" | ||
| 33 | #include "core/hle/service/hid/controllers/touchscreen.h" | ||
| 34 | #include "core/hle/service/hid/controllers/xpad.h" | ||
| 35 | 14 | ||
| 36 | namespace Service::HID { | 15 | namespace Service::HID { |
| 37 | 16 | ||
| 38 | // Updating period for each HID device. | ||
| 39 | // Period time is obtained by measuring the number of samples in a second on HW using a homebrew | ||
| 40 | // Correct npad_update_ns is 4ms this is overclocked to lower input lag | ||
| 41 | constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) | ||
| 42 | constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz) | ||
| 43 | constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) | ||
| 44 | constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) | ||
| 45 | |||
| 46 | IAppletResource::IAppletResource(Core::System& system_, | ||
| 47 | KernelHelpers::ServiceContext& service_context_) | ||
| 48 | : ServiceFramework{system_, "IAppletResource"}, service_context{service_context_} { | ||
| 49 | static const FunctionInfo functions[] = { | ||
| 50 | {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, | ||
| 51 | }; | ||
| 52 | RegisterHandlers(functions); | ||
| 53 | u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); | ||
| 54 | MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory); | ||
| 55 | MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory); | ||
| 56 | MakeController<Controller_Mouse>(HidController::Mouse, shared_memory); | ||
| 57 | MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory); | ||
| 58 | MakeController<Controller_XPad>(HidController::XPad, shared_memory); | ||
| 59 | MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory); | ||
| 60 | MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory); | ||
| 61 | MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory); | ||
| 62 | MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory); | ||
| 63 | MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory); | ||
| 64 | MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory); | ||
| 65 | MakeController<Controller_Gesture>(HidController::Gesture, shared_memory); | ||
| 66 | MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory); | ||
| 67 | MakeController<Controller_Stubbed>(HidController::DebugMouse, shared_memory); | ||
| 68 | MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory); | ||
| 69 | |||
| 70 | // Homebrew doesn't try to activate some controllers, so we activate them by default | ||
| 71 | GetController<Controller_NPad>(HidController::NPad).ActivateController(); | ||
| 72 | GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController(); | ||
| 73 | |||
| 74 | GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00); | ||
| 75 | GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00); | ||
| 76 | GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000); | ||
| 77 | GetController<Controller_Stubbed>(HidController::InputDetector).SetCommonHeaderOffset(0x5200); | ||
| 78 | GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00); | ||
| 79 | GetController<Controller_Stubbed>(HidController::DebugMouse).SetCommonHeaderOffset(0x3DC00); | ||
| 80 | |||
| 81 | // Register update callbacks | ||
| 82 | npad_update_event = Core::Timing::CreateEvent( | ||
| 83 | "HID::UpdatePadCallback", | ||
| 84 | [this](std::uintptr_t user_data, s64 time, | ||
| 85 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 86 | const auto guard = LockService(); | ||
| 87 | UpdateNpad(user_data, ns_late); | ||
| 88 | return std::nullopt; | ||
| 89 | }); | ||
| 90 | default_update_event = Core::Timing::CreateEvent( | ||
| 91 | "HID::UpdateDefaultCallback", | ||
| 92 | [this](std::uintptr_t user_data, s64 time, | ||
| 93 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 94 | const auto guard = LockService(); | ||
| 95 | UpdateControllers(user_data, ns_late); | ||
| 96 | return std::nullopt; | ||
| 97 | }); | ||
| 98 | mouse_keyboard_update_event = Core::Timing::CreateEvent( | ||
| 99 | "HID::UpdateMouseKeyboardCallback", | ||
| 100 | [this](std::uintptr_t user_data, s64 time, | ||
| 101 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 102 | const auto guard = LockService(); | ||
| 103 | UpdateMouseKeyboard(user_data, ns_late); | ||
| 104 | return std::nullopt; | ||
| 105 | }); | ||
| 106 | motion_update_event = Core::Timing::CreateEvent( | ||
| 107 | "HID::UpdateMotionCallback", | ||
| 108 | [this](std::uintptr_t user_data, s64 time, | ||
| 109 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 110 | const auto guard = LockService(); | ||
| 111 | UpdateMotion(user_data, ns_late); | ||
| 112 | return std::nullopt; | ||
| 113 | }); | ||
| 114 | |||
| 115 | system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); | ||
| 116 | system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, | ||
| 117 | default_update_event); | ||
| 118 | system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, | ||
| 119 | mouse_keyboard_update_event); | ||
| 120 | system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, | ||
| 121 | motion_update_event); | ||
| 122 | |||
| 123 | system.HIDCore().ReloadInputDevices(); | ||
| 124 | } | ||
| 125 | |||
| 126 | void IAppletResource::ActivateController(HidController controller) { | ||
| 127 | controllers[static_cast<size_t>(controller)]->ActivateController(); | ||
| 128 | } | ||
| 129 | |||
| 130 | void IAppletResource::DeactivateController(HidController controller) { | ||
| 131 | controllers[static_cast<size_t>(controller)]->DeactivateController(); | ||
| 132 | } | ||
| 133 | |||
| 134 | IAppletResource::~IAppletResource() { | ||
| 135 | system.CoreTiming().UnscheduleEvent(npad_update_event, 0); | ||
| 136 | system.CoreTiming().UnscheduleEvent(default_update_event, 0); | ||
| 137 | system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); | ||
| 138 | system.CoreTiming().UnscheduleEvent(motion_update_event, 0); | ||
| 139 | } | ||
| 140 | |||
| 141 | void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) { | ||
| 142 | LOG_DEBUG(Service_HID, "called"); | ||
| 143 | |||
| 144 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 145 | rb.Push(ResultSuccess); | ||
| 146 | rb.PushCopyObjects(&system.Kernel().GetHidSharedMem()); | ||
| 147 | } | ||
| 148 | |||
| 149 | void IAppletResource::UpdateControllers(std::uintptr_t user_data, | ||
| 150 | std::chrono::nanoseconds ns_late) { | ||
| 151 | auto& core_timing = system.CoreTiming(); | ||
| 152 | |||
| 153 | for (const auto& controller : controllers) { | ||
| 154 | // Keyboard has it's own update event | ||
| 155 | if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) { | ||
| 156 | continue; | ||
| 157 | } | ||
| 158 | // Mouse has it's own update event | ||
| 159 | if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) { | ||
| 160 | continue; | ||
| 161 | } | ||
| 162 | // Npad has it's own update event | ||
| 163 | if (controller == controllers[static_cast<size_t>(HidController::NPad)]) { | ||
| 164 | continue; | ||
| 165 | } | ||
| 166 | controller->OnUpdate(core_timing); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | void IAppletResource::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | ||
| 171 | auto& core_timing = system.CoreTiming(); | ||
| 172 | |||
| 173 | controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing); | ||
| 174 | } | ||
| 175 | |||
| 176 | void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, | ||
| 177 | std::chrono::nanoseconds ns_late) { | ||
| 178 | auto& core_timing = system.CoreTiming(); | ||
| 179 | |||
| 180 | controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); | ||
| 181 | controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); | ||
| 182 | } | ||
| 183 | |||
| 184 | void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | ||
| 185 | auto& core_timing = system.CoreTiming(); | ||
| 186 | |||
| 187 | controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); | ||
| 188 | } | ||
| 189 | |||
| 190 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { | ||
| 191 | public: | ||
| 192 | explicit IActiveVibrationDeviceList(Core::System& system_, | ||
| 193 | std::shared_ptr<IAppletResource> applet_resource_) | ||
| 194 | : ServiceFramework{system_, "IActiveVibrationDeviceList"}, | ||
| 195 | applet_resource(applet_resource_) { | ||
| 196 | // clang-format off | ||
| 197 | static const FunctionInfo functions[] = { | ||
| 198 | {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"}, | ||
| 199 | }; | ||
| 200 | // clang-format on | ||
| 201 | |||
| 202 | RegisterHandlers(functions); | ||
| 203 | } | ||
| 204 | |||
| 205 | private: | ||
| 206 | void InitializeVibrationDevice(HLERequestContext& ctx) { | ||
| 207 | IPC::RequestParser rp{ctx}; | ||
| 208 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; | ||
| 209 | |||
| 210 | if (applet_resource != nullptr) { | ||
| 211 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 212 | .InitializeVibrationDevice(vibration_device_handle); | ||
| 213 | } | ||
| 214 | |||
| 215 | LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", | ||
| 216 | vibration_device_handle.npad_type, vibration_device_handle.npad_id, | ||
| 217 | vibration_device_handle.device_index); | ||
| 218 | |||
| 219 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 220 | rb.Push(ResultSuccess); | ||
| 221 | } | ||
| 222 | |||
| 223 | std::shared_ptr<IAppletResource> applet_resource; | ||
| 224 | }; | ||
| 225 | |||
| 226 | std::shared_ptr<IAppletResource> Hid::GetAppletResource() { | ||
| 227 | if (applet_resource == nullptr) { | ||
| 228 | applet_resource = std::make_shared<IAppletResource>(system, service_context); | ||
| 229 | } | ||
| 230 | |||
| 231 | return applet_resource; | ||
| 232 | } | ||
| 233 | |||
| 234 | Hid::Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_) | ||
| 235 | : ServiceFramework{system_, "hid"}, applet_resource{applet_resource_}, service_context{ | ||
| 236 | system_, | ||
| 237 | service_name} { | ||
| 238 | // clang-format off | ||
| 239 | static const FunctionInfo functions[] = { | ||
| 240 | {0, &Hid::CreateAppletResource, "CreateAppletResource"}, | ||
| 241 | {1, &Hid::ActivateDebugPad, "ActivateDebugPad"}, | ||
| 242 | {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, | ||
| 243 | {21, &Hid::ActivateMouse, "ActivateMouse"}, | ||
| 244 | {26, nullptr, "ActivateDebugMouse"}, | ||
| 245 | {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, | ||
| 246 | {32, &Hid::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"}, | ||
| 247 | {40, nullptr, "AcquireXpadIdEventHandle"}, | ||
| 248 | {41, nullptr, "ReleaseXpadIdEventHandle"}, | ||
| 249 | {51, &Hid::ActivateXpad, "ActivateXpad"}, | ||
| 250 | {55, &Hid::GetXpadIDs, "GetXpadIds"}, | ||
| 251 | {56, nullptr, "ActivateJoyXpad"}, | ||
| 252 | {58, nullptr, "GetJoyXpadLifoHandle"}, | ||
| 253 | {59, nullptr, "GetJoyXpadIds"}, | ||
| 254 | {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"}, | ||
| 255 | {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"}, | ||
| 256 | {62, nullptr, "GetSixAxisSensorLifoHandle"}, | ||
| 257 | {63, nullptr, "ActivateJoySixAxisSensor"}, | ||
| 258 | {64, nullptr, "DeactivateJoySixAxisSensor"}, | ||
| 259 | {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, | ||
| 260 | {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, | ||
| 261 | {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, | ||
| 262 | {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"}, | ||
| 263 | {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, | ||
| 264 | {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, | ||
| 265 | {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, | ||
| 266 | {72, &Hid::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"}, | ||
| 267 | {73, nullptr, "SetAccelerometerParameters"}, | ||
| 268 | {74, nullptr, "GetAccelerometerParameters"}, | ||
| 269 | {75, nullptr, "ResetAccelerometerParameters"}, | ||
| 270 | {76, nullptr, "SetAccelerometerPlayMode"}, | ||
| 271 | {77, nullptr, "GetAccelerometerPlayMode"}, | ||
| 272 | {78, nullptr, "ResetAccelerometerPlayMode"}, | ||
| 273 | {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, | ||
| 274 | {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"}, | ||
| 275 | {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, | ||
| 276 | {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, | ||
| 277 | {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"}, | ||
| 278 | {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"}, | ||
| 279 | {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"}, | ||
| 280 | {86, nullptr, "StoreSixAxisSensorCalibrationParameter"}, | ||
| 281 | {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"}, | ||
| 282 | {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"}, | ||
| 283 | {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, | ||
| 284 | {91, &Hid::ActivateGesture, "ActivateGesture"}, | ||
| 285 | {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, | ||
| 286 | {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, | ||
| 287 | {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, | ||
| 288 | {103, &Hid::ActivateNpad, "ActivateNpad"}, | ||
| 289 | {104, &Hid::DeactivateNpad, "DeactivateNpad"}, | ||
| 290 | {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, | ||
| 291 | {107, &Hid::DisconnectNpad, "DisconnectNpad"}, | ||
| 292 | {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, | ||
| 293 | {109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"}, | ||
| 294 | {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, | ||
| 295 | {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, | ||
| 296 | {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, | ||
| 297 | {123, &Hid::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"}, | ||
| 298 | {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, | ||
| 299 | {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, | ||
| 300 | {126, &Hid::StartLrAssignmentMode, "StartLrAssignmentMode"}, | ||
| 301 | {127, &Hid::StopLrAssignmentMode, "StopLrAssignmentMode"}, | ||
| 302 | {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, | ||
| 303 | {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"}, | ||
| 304 | {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"}, | ||
| 305 | {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"}, | ||
| 306 | {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, | ||
| 307 | {133, &Hid::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"}, | ||
| 308 | {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"}, | ||
| 309 | {135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"}, | ||
| 310 | {136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"}, | ||
| 311 | {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, | ||
| 312 | {201, &Hid::SendVibrationValue, "SendVibrationValue"}, | ||
| 313 | {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, | ||
| 314 | {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, | ||
| 315 | {204, &Hid::PermitVibration, "PermitVibration"}, | ||
| 316 | {205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"}, | ||
| 317 | {206, &Hid::SendVibrationValues, "SendVibrationValues"}, | ||
| 318 | {207, &Hid::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"}, | ||
| 319 | {208, &Hid::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"}, | ||
| 320 | {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, | ||
| 321 | {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, | ||
| 322 | {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, | ||
| 323 | {212, nullptr, "SendVibrationValueInBool"}, | ||
| 324 | {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, | ||
| 325 | {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, | ||
| 326 | {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, | ||
| 327 | {303, &Hid::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"}, | ||
| 328 | {304, &Hid::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"}, | ||
| 329 | {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, | ||
| 330 | {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, | ||
| 331 | {307, &Hid::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"}, | ||
| 332 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, | ||
| 333 | {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, | ||
| 334 | {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, | ||
| 335 | {400, &Hid::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, | ||
| 336 | {401, nullptr, "EnableUsbFullKeyController"}, | ||
| 337 | {402, nullptr, "IsUsbFullKeyControllerConnected"}, | ||
| 338 | {403, nullptr, "HasBattery"}, | ||
| 339 | {404, nullptr, "HasLeftRightBattery"}, | ||
| 340 | {405, nullptr, "GetNpadInterfaceType"}, | ||
| 341 | {406, nullptr, "GetNpadLeftRightInterfaceType"}, | ||
| 342 | {407, nullptr, "GetNpadOfHighestBatteryLevel"}, | ||
| 343 | {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, | ||
| 344 | {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"}, | ||
| 345 | {501, &Hid::InitializePalma, "InitializePalma"}, | ||
| 346 | {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"}, | ||
| 347 | {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"}, | ||
| 348 | {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"}, | ||
| 349 | {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"}, | ||
| 350 | {506, &Hid::ReadPalmaStep, "ReadPalmaStep"}, | ||
| 351 | {507, &Hid::EnablePalmaStep, "EnablePalmaStep"}, | ||
| 352 | {508, &Hid::ResetPalmaStep, "ResetPalmaStep"}, | ||
| 353 | {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"}, | ||
| 354 | {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"}, | ||
| 355 | {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"}, | ||
| 356 | {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"}, | ||
| 357 | {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"}, | ||
| 358 | {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"}, | ||
| 359 | {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"}, | ||
| 360 | {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"}, | ||
| 361 | {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"}, | ||
| 362 | {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"}, | ||
| 363 | {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"}, | ||
| 364 | {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"}, | ||
| 365 | {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"}, | ||
| 366 | {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, | ||
| 367 | {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"}, | ||
| 368 | {524, &Hid::PairPalma, "PairPalma"}, | ||
| 369 | {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, | ||
| 370 | {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"}, | ||
| 371 | {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"}, | ||
| 372 | {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"}, | ||
| 373 | {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"}, | ||
| 374 | {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, | ||
| 375 | {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, | ||
| 376 | {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, | ||
| 377 | {1003, &Hid::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"}, | ||
| 378 | {2000, nullptr, "ActivateDigitizer"}, | ||
| 379 | }; | ||
| 380 | // clang-format on | ||
| 381 | |||
| 382 | RegisterHandlers(functions); | ||
| 383 | } | ||
| 384 | |||
| 385 | Hid::~Hid() = default; | ||
| 386 | |||
| 387 | void Hid::CreateAppletResource(HLERequestContext& ctx) { | ||
| 388 | IPC::RequestParser rp{ctx}; | ||
| 389 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 390 | |||
| 391 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 392 | |||
| 393 | if (applet_resource == nullptr) { | ||
| 394 | applet_resource = std::make_shared<IAppletResource>(system, service_context); | ||
| 395 | } | ||
| 396 | |||
| 397 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 398 | rb.Push(ResultSuccess); | ||
| 399 | rb.PushIpcInterface<IAppletResource>(applet_resource); | ||
| 400 | } | ||
| 401 | |||
| 402 | void Hid::ActivateDebugPad(HLERequestContext& ctx) { | ||
| 403 | IPC::RequestParser rp{ctx}; | ||
| 404 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 405 | |||
| 406 | applet_resource->ActivateController(HidController::DebugPad); | ||
| 407 | |||
| 408 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 409 | |||
| 410 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 411 | rb.Push(ResultSuccess); | ||
| 412 | } | ||
| 413 | |||
| 414 | void Hid::ActivateTouchScreen(HLERequestContext& ctx) { | ||
| 415 | IPC::RequestParser rp{ctx}; | ||
| 416 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 417 | |||
| 418 | applet_resource->ActivateController(HidController::Touchscreen); | ||
| 419 | |||
| 420 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 421 | |||
| 422 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 423 | rb.Push(ResultSuccess); | ||
| 424 | } | ||
| 425 | |||
| 426 | void Hid::ActivateMouse(HLERequestContext& ctx) { | ||
| 427 | IPC::RequestParser rp{ctx}; | ||
| 428 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 429 | |||
| 430 | applet_resource->ActivateController(HidController::Mouse); | ||
| 431 | |||
| 432 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 433 | |||
| 434 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 435 | rb.Push(ResultSuccess); | ||
| 436 | } | ||
| 437 | |||
| 438 | void Hid::ActivateKeyboard(HLERequestContext& ctx) { | ||
| 439 | IPC::RequestParser rp{ctx}; | ||
| 440 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 441 | |||
| 442 | applet_resource->ActivateController(HidController::Keyboard); | ||
| 443 | |||
| 444 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 445 | |||
| 446 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 447 | rb.Push(ResultSuccess); | ||
| 448 | } | ||
| 449 | |||
| 450 | void Hid::SendKeyboardLockKeyEvent(HLERequestContext& ctx) { | ||
| 451 | IPC::RequestParser rp{ctx}; | ||
| 452 | const auto flags{rp.Pop<u32>()}; | ||
| 453 | |||
| 454 | LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); | ||
| 455 | |||
| 456 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 457 | rb.Push(ResultSuccess); | ||
| 458 | } | ||
| 459 | |||
| 460 | void Hid::ActivateXpad(HLERequestContext& ctx) { | ||
| 461 | IPC::RequestParser rp{ctx}; | ||
| 462 | struct Parameters { | ||
| 463 | u32 basic_xpad_id; | ||
| 464 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 465 | u64 applet_resource_user_id; | ||
| 466 | }; | ||
| 467 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 468 | |||
| 469 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 470 | |||
| 471 | applet_resource->ActivateController(HidController::XPad); | ||
| 472 | |||
| 473 | LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", | ||
| 474 | parameters.basic_xpad_id, parameters.applet_resource_user_id); | ||
| 475 | |||
| 476 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 477 | rb.Push(ResultSuccess); | ||
| 478 | } | ||
| 479 | |||
| 480 | void Hid::GetXpadIDs(HLERequestContext& ctx) { | ||
| 481 | IPC::RequestParser rp{ctx}; | ||
| 482 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 483 | |||
| 484 | LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 485 | |||
| 486 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 487 | rb.Push(ResultSuccess); | ||
| 488 | rb.Push(0); | ||
| 489 | } | ||
| 490 | |||
| 491 | void Hid::ActivateSixAxisSensor(HLERequestContext& ctx) { | ||
| 492 | IPC::RequestParser rp{ctx}; | ||
| 493 | struct Parameters { | ||
| 494 | u32 basic_xpad_id; | ||
| 495 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 496 | u64 applet_resource_user_id; | ||
| 497 | }; | ||
| 498 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 499 | |||
| 500 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 501 | |||
| 502 | // This function does nothing on 10.0.0+ | ||
| 503 | |||
| 504 | LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}", | ||
| 505 | parameters.basic_xpad_id, parameters.applet_resource_user_id); | ||
| 506 | |||
| 507 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 508 | rb.Push(ResultSuccess); | ||
| 509 | } | ||
| 510 | |||
| 511 | void Hid::DeactivateSixAxisSensor(HLERequestContext& ctx) { | ||
| 512 | IPC::RequestParser rp{ctx}; | ||
| 513 | struct Parameters { | ||
| 514 | u32 basic_xpad_id; | ||
| 515 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 516 | u64 applet_resource_user_id; | ||
| 517 | }; | ||
| 518 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 519 | |||
| 520 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 521 | |||
| 522 | // This function does nothing on 10.0.0+ | ||
| 523 | |||
| 524 | LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}", | ||
| 525 | parameters.basic_xpad_id, parameters.applet_resource_user_id); | ||
| 526 | |||
| 527 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 528 | rb.Push(ResultSuccess); | ||
| 529 | } | ||
| 530 | |||
| 531 | void Hid::StartSixAxisSensor(HLERequestContext& ctx) { | ||
| 532 | IPC::RequestParser rp{ctx}; | ||
| 533 | struct Parameters { | ||
| 534 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 535 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 536 | u64 applet_resource_user_id; | ||
| 537 | }; | ||
| 538 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 539 | |||
| 540 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 541 | |||
| 542 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 543 | const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true); | ||
| 544 | |||
| 545 | LOG_DEBUG(Service_HID, | ||
| 546 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 547 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 548 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 549 | |||
| 550 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 551 | rb.Push(result); | ||
| 552 | } | ||
| 553 | |||
| 554 | void Hid::StopSixAxisSensor(HLERequestContext& ctx) { | ||
| 555 | IPC::RequestParser rp{ctx}; | ||
| 556 | struct Parameters { | ||
| 557 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 558 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 559 | u64 applet_resource_user_id; | ||
| 560 | }; | ||
| 561 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 562 | |||
| 563 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 564 | |||
| 565 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 566 | const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false); | ||
| 567 | |||
| 568 | LOG_DEBUG(Service_HID, | ||
| 569 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 570 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 571 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 572 | |||
| 573 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 574 | rb.Push(result); | ||
| 575 | } | ||
| 576 | |||
| 577 | void Hid::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) { | ||
| 578 | IPC::RequestParser rp{ctx}; | ||
| 579 | struct Parameters { | ||
| 580 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 581 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 582 | u64 applet_resource_user_id; | ||
| 583 | }; | ||
| 584 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 585 | |||
| 586 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 587 | |||
| 588 | bool is_enabled{}; | ||
| 589 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 590 | const auto result = | ||
| 591 | controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); | ||
| 592 | |||
| 593 | LOG_DEBUG(Service_HID, | ||
| 594 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 595 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 596 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 597 | |||
| 598 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 599 | rb.Push(result); | ||
| 600 | rb.Push(is_enabled); | ||
| 601 | } | ||
| 602 | |||
| 603 | void Hid::EnableSixAxisSensorFusion(HLERequestContext& ctx) { | ||
| 604 | IPC::RequestParser rp{ctx}; | ||
| 605 | struct Parameters { | ||
| 606 | bool enable_sixaxis_sensor_fusion; | ||
| 607 | INSERT_PADDING_BYTES_NOINIT(3); | ||
| 608 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 609 | u64 applet_resource_user_id; | ||
| 610 | }; | ||
| 611 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 612 | |||
| 613 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 614 | |||
| 615 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 616 | const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, | ||
| 617 | parameters.enable_sixaxis_sensor_fusion); | ||
| 618 | |||
| 619 | LOG_DEBUG(Service_HID, | ||
| 620 | "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " | ||
| 621 | "device_index={}, applet_resource_user_id={}", | ||
| 622 | parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, | ||
| 623 | parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, | ||
| 624 | parameters.applet_resource_user_id); | ||
| 625 | |||
| 626 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 627 | rb.Push(result); | ||
| 628 | } | ||
| 629 | |||
| 630 | void Hid::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) { | ||
| 631 | IPC::RequestParser rp{ctx}; | ||
| 632 | struct Parameters { | ||
| 633 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 634 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion; | ||
| 635 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 636 | u64 applet_resource_user_id; | ||
| 637 | }; | ||
| 638 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 639 | |||
| 640 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 641 | |||
| 642 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 643 | const auto result = | ||
| 644 | controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); | ||
| 645 | |||
| 646 | LOG_DEBUG(Service_HID, | ||
| 647 | "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " | ||
| 648 | "parameter2={}, applet_resource_user_id={}", | ||
| 649 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 650 | parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1, | ||
| 651 | parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); | ||
| 652 | |||
| 653 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 654 | rb.Push(result); | ||
| 655 | } | ||
| 656 | |||
| 657 | void Hid::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) { | ||
| 658 | IPC::RequestParser rp{ctx}; | ||
| 659 | struct Parameters { | ||
| 660 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 661 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 662 | u64 applet_resource_user_id; | ||
| 663 | }; | ||
| 664 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 665 | |||
| 666 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 667 | |||
| 668 | Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; | ||
| 669 | const auto& controller = | ||
| 670 | GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 671 | const auto result = | ||
| 672 | controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); | ||
| 673 | |||
| 674 | LOG_DEBUG(Service_HID, | ||
| 675 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 676 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 677 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 678 | |||
| 679 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 680 | rb.Push(result); | ||
| 681 | rb.PushRaw(fusion_parameters); | ||
| 682 | } | ||
| 683 | |||
| 684 | void Hid::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) { | ||
| 685 | IPC::RequestParser rp{ctx}; | ||
| 686 | struct Parameters { | ||
| 687 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 688 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 689 | u64 applet_resource_user_id; | ||
| 690 | }; | ||
| 691 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 692 | |||
| 693 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 694 | |||
| 695 | // Since these parameters are unknown just use what HW outputs | ||
| 696 | const Core::HID::SixAxisSensorFusionParameters fusion_parameters{ | ||
| 697 | .parameter1 = 0.03f, | ||
| 698 | .parameter2 = 0.4f, | ||
| 699 | }; | ||
| 700 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 701 | const auto result1 = | ||
| 702 | controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); | ||
| 703 | const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); | ||
| 704 | |||
| 705 | LOG_DEBUG(Service_HID, | ||
| 706 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 707 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 708 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 709 | |||
| 710 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 711 | if (result1.IsError()) { | ||
| 712 | rb.Push(result1); | ||
| 713 | return; | ||
| 714 | } | ||
| 715 | rb.Push(result2); | ||
| 716 | } | ||
| 717 | |||
| 718 | void Hid::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) { | ||
| 719 | IPC::RequestParser rp{ctx}; | ||
| 720 | const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()}; | ||
| 721 | const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()}; | ||
| 722 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 723 | |||
| 724 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 725 | const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); | ||
| 726 | |||
| 727 | LOG_DEBUG(Service_HID, | ||
| 728 | "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " | ||
| 729 | "applet_resource_user_id={}", | ||
| 730 | sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index, | ||
| 731 | drift_mode, applet_resource_user_id); | ||
| 732 | |||
| 733 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 734 | rb.Push(result); | ||
| 735 | } | ||
| 736 | |||
| 737 | void Hid::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) { | ||
| 738 | IPC::RequestParser rp{ctx}; | ||
| 739 | struct Parameters { | ||
| 740 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 741 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 742 | u64 applet_resource_user_id; | ||
| 743 | }; | ||
| 744 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 745 | |||
| 746 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 747 | |||
| 748 | auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; | ||
| 749 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 750 | const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); | ||
| 751 | |||
| 752 | LOG_DEBUG(Service_HID, | ||
| 753 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 754 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 755 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 756 | |||
| 757 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 758 | rb.Push(result); | ||
| 759 | rb.PushEnum(drift_mode); | ||
| 760 | } | ||
| 761 | |||
| 762 | void Hid::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) { | ||
| 763 | IPC::RequestParser rp{ctx}; | ||
| 764 | struct Parameters { | ||
| 765 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 766 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 767 | u64 applet_resource_user_id; | ||
| 768 | }; | ||
| 769 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 770 | |||
| 771 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 772 | |||
| 773 | const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; | ||
| 774 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 775 | const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); | ||
| 776 | |||
| 777 | LOG_DEBUG(Service_HID, | ||
| 778 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 779 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 780 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 781 | |||
| 782 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 783 | rb.Push(result); | ||
| 784 | } | ||
| 785 | |||
| 786 | void Hid::IsSixAxisSensorAtRest(HLERequestContext& ctx) { | ||
| 787 | IPC::RequestParser rp{ctx}; | ||
| 788 | struct Parameters { | ||
| 789 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 790 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 791 | u64 applet_resource_user_id; | ||
| 792 | }; | ||
| 793 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 794 | |||
| 795 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 796 | |||
| 797 | bool is_at_rest{}; | ||
| 798 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 799 | controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); | ||
| 800 | |||
| 801 | LOG_DEBUG(Service_HID, | ||
| 802 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 803 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 804 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 805 | |||
| 806 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 807 | rb.Push(ResultSuccess); | ||
| 808 | rb.Push(is_at_rest); | ||
| 809 | } | ||
| 810 | |||
| 811 | void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) { | ||
| 812 | IPC::RequestParser rp{ctx}; | ||
| 813 | struct Parameters { | ||
| 814 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 815 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 816 | u64 applet_resource_user_id; | ||
| 817 | }; | ||
| 818 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 819 | |||
| 820 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 821 | |||
| 822 | bool is_firmware_available{}; | ||
| 823 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 824 | controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, | ||
| 825 | is_firmware_available); | ||
| 826 | |||
| 827 | LOG_WARNING( | ||
| 828 | Service_HID, | ||
| 829 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 830 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 831 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 832 | |||
| 833 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 834 | rb.Push(ResultSuccess); | ||
| 835 | rb.Push(is_firmware_available); | ||
| 836 | } | ||
| 837 | |||
| 838 | void Hid::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) { | ||
| 839 | IPC::RequestParser rp{ctx}; | ||
| 840 | struct Parameters { | ||
| 841 | bool enabled; | ||
| 842 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 843 | u64 applet_resource_user_id; | ||
| 844 | }; | ||
| 845 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 846 | |||
| 847 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 848 | |||
| 849 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 850 | const auto result = controller.EnableSixAxisSensorUnalteredPassthrough( | ||
| 851 | parameters.sixaxis_handle, parameters.enabled); | ||
| 852 | |||
| 853 | LOG_DEBUG(Service_HID, | ||
| 854 | "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " | ||
| 855 | "applet_resource_user_id={}", | ||
| 856 | parameters.enabled, parameters.sixaxis_handle.npad_type, | ||
| 857 | parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, | ||
| 858 | parameters.applet_resource_user_id); | ||
| 859 | |||
| 860 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 861 | rb.Push(result); | ||
| 862 | } | ||
| 863 | |||
| 864 | void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) { | ||
| 865 | IPC::RequestParser rp{ctx}; | ||
| 866 | struct Parameters { | ||
| 867 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 868 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 869 | u64 applet_resource_user_id; | ||
| 870 | }; | ||
| 871 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 872 | |||
| 873 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 874 | |||
| 875 | bool is_unaltered_sisxaxis_enabled{}; | ||
| 876 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 877 | const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled( | ||
| 878 | parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); | ||
| 879 | |||
| 880 | LOG_DEBUG( | ||
| 881 | Service_HID, | ||
| 882 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 883 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 884 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 885 | |||
| 886 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 887 | rb.Push(result); | ||
| 888 | rb.Push(is_unaltered_sisxaxis_enabled); | ||
| 889 | } | ||
| 890 | |||
| 891 | void Hid::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) { | ||
| 892 | IPC::RequestParser rp{ctx}; | ||
| 893 | struct Parameters { | ||
| 894 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 895 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 896 | u64 applet_resource_user_id; | ||
| 897 | }; | ||
| 898 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 899 | |||
| 900 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 901 | |||
| 902 | Core::HID::SixAxisSensorCalibrationParameter calibration{}; | ||
| 903 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 904 | const auto result = | ||
| 905 | controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration); | ||
| 906 | |||
| 907 | LOG_WARNING( | ||
| 908 | Service_HID, | ||
| 909 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 910 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 911 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 912 | |||
| 913 | if (result.IsSuccess()) { | ||
| 914 | ctx.WriteBuffer(calibration); | ||
| 915 | } | ||
| 916 | |||
| 917 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 918 | rb.Push(result); | ||
| 919 | } | ||
| 920 | |||
| 921 | void Hid::GetSixAxisSensorIcInformation(HLERequestContext& ctx) { | ||
| 922 | IPC::RequestParser rp{ctx}; | ||
| 923 | struct Parameters { | ||
| 924 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 925 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 926 | u64 applet_resource_user_id; | ||
| 927 | }; | ||
| 928 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 929 | |||
| 930 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 931 | |||
| 932 | Core::HID::SixAxisSensorIcInformation ic_information{}; | ||
| 933 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 934 | const auto result = | ||
| 935 | controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information); | ||
| 936 | |||
| 937 | LOG_WARNING( | ||
| 938 | Service_HID, | ||
| 939 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 940 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 941 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 942 | |||
| 943 | if (result.IsSuccess()) { | ||
| 944 | ctx.WriteBuffer(ic_information); | ||
| 945 | } | ||
| 946 | |||
| 947 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 948 | rb.Push(result); | ||
| 949 | } | ||
| 950 | |||
| 951 | void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) { | ||
| 952 | IPC::RequestParser rp{ctx}; | ||
| 953 | struct Parameters { | ||
| 954 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 955 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 956 | u64 applet_resource_user_id; | ||
| 957 | }; | ||
| 958 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 959 | |||
| 960 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 961 | |||
| 962 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 963 | const auto result = | ||
| 964 | controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); | ||
| 965 | |||
| 966 | LOG_WARNING( | ||
| 967 | Service_HID, | ||
| 968 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 969 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 970 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 971 | |||
| 972 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 973 | rb.Push(result); | ||
| 974 | } | ||
| 975 | |||
| 976 | void Hid::ActivateGesture(HLERequestContext& ctx) { | ||
| 977 | IPC::RequestParser rp{ctx}; | ||
| 978 | struct Parameters { | ||
| 979 | u32 unknown; | ||
| 980 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 981 | u64 applet_resource_user_id; | ||
| 982 | }; | ||
| 983 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 984 | |||
| 985 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 986 | |||
| 987 | applet_resource->ActivateController(HidController::Gesture); | ||
| 988 | |||
| 989 | LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", | ||
| 990 | parameters.unknown, parameters.applet_resource_user_id); | ||
| 991 | |||
| 992 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 993 | rb.Push(ResultSuccess); | ||
| 994 | } | ||
| 995 | |||
| 996 | void Hid::SetSupportedNpadStyleSet(HLERequestContext& ctx) { | ||
| 997 | IPC::RequestParser rp{ctx}; | ||
| 998 | struct Parameters { | ||
| 999 | Core::HID::NpadStyleSet supported_styleset; | ||
| 1000 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1001 | u64 applet_resource_user_id; | ||
| 1002 | }; | ||
| 1003 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1004 | |||
| 1005 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1006 | |||
| 1007 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1008 | .SetSupportedStyleSet({parameters.supported_styleset}); | ||
| 1009 | |||
| 1010 | LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", | ||
| 1011 | parameters.supported_styleset, parameters.applet_resource_user_id); | ||
| 1012 | |||
| 1013 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1014 | rb.Push(ResultSuccess); | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | void Hid::GetSupportedNpadStyleSet(HLERequestContext& ctx) { | ||
| 1018 | IPC::RequestParser rp{ctx}; | ||
| 1019 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1020 | |||
| 1021 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1022 | |||
| 1023 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1024 | rb.Push(ResultSuccess); | ||
| 1025 | rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1026 | .GetSupportedStyleSet() | ||
| 1027 | .raw); | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | void Hid::SetSupportedNpadIdType(HLERequestContext& ctx) { | ||
| 1031 | IPC::RequestParser rp{ctx}; | ||
| 1032 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1033 | |||
| 1034 | const auto result = applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1035 | .SetSupportedNpadIdTypes(ctx.ReadBuffer()); | ||
| 1036 | |||
| 1037 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1038 | |||
| 1039 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1040 | rb.Push(result); | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | void Hid::ActivateNpad(HLERequestContext& ctx) { | ||
| 1044 | IPC::RequestParser rp{ctx}; | ||
| 1045 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1046 | |||
| 1047 | applet_resource->ActivateController(HidController::NPad); | ||
| 1048 | |||
| 1049 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1050 | |||
| 1051 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1052 | rb.Push(ResultSuccess); | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | void Hid::DeactivateNpad(HLERequestContext& ctx) { | ||
| 1056 | IPC::RequestParser rp{ctx}; | ||
| 1057 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1058 | |||
| 1059 | applet_resource->DeactivateController(HidController::NPad); | ||
| 1060 | |||
| 1061 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1062 | |||
| 1063 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1064 | rb.Push(ResultSuccess); | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | void Hid::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) { | ||
| 1068 | IPC::RequestParser rp{ctx}; | ||
| 1069 | struct Parameters { | ||
| 1070 | Core::HID::NpadIdType npad_id; | ||
| 1071 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1072 | u64 applet_resource_user_id; | ||
| 1073 | u64 unknown; | ||
| 1074 | }; | ||
| 1075 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1076 | |||
| 1077 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1078 | |||
| 1079 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", | ||
| 1080 | parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); | ||
| 1081 | |||
| 1082 | // Games expect this event to be signaled after calling this function | ||
| 1083 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1084 | .SignalStyleSetChangedEvent(parameters.npad_id); | ||
| 1085 | |||
| 1086 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 1087 | rb.Push(ResultSuccess); | ||
| 1088 | rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1089 | .GetStyleSetChangedEvent(parameters.npad_id)); | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | void Hid::DisconnectNpad(HLERequestContext& ctx) { | ||
| 1093 | IPC::RequestParser rp{ctx}; | ||
| 1094 | struct Parameters { | ||
| 1095 | Core::HID::NpadIdType npad_id; | ||
| 1096 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1097 | u64 applet_resource_user_id; | ||
| 1098 | }; | ||
| 1099 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1100 | |||
| 1101 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1102 | |||
| 1103 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1104 | controller.DisconnectNpad(parameters.npad_id); | ||
| 1105 | |||
| 1106 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | ||
| 1107 | parameters.applet_resource_user_id); | ||
| 1108 | |||
| 1109 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1110 | rb.Push(ResultSuccess); | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | void Hid::GetPlayerLedPattern(HLERequestContext& ctx) { | ||
| 1114 | IPC::RequestParser rp{ctx}; | ||
| 1115 | const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 1116 | |||
| 1117 | Core::HID::LedPattern pattern{0, 0, 0, 0}; | ||
| 1118 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1119 | const auto result = controller.GetLedPattern(npad_id, pattern); | ||
| 1120 | |||
| 1121 | LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); | ||
| 1122 | |||
| 1123 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1124 | rb.Push(result); | ||
| 1125 | rb.Push(pattern.raw); | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | void Hid::ActivateNpadWithRevision(HLERequestContext& ctx) { | ||
| 1129 | // Should have no effect with how our npad sets up the data | ||
| 1130 | IPC::RequestParser rp{ctx}; | ||
| 1131 | struct Parameters { | ||
| 1132 | s32 revision; | ||
| 1133 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1134 | u64 applet_resource_user_id; | ||
| 1135 | }; | ||
| 1136 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1137 | |||
| 1138 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1139 | |||
| 1140 | applet_resource->ActivateController(HidController::NPad); | ||
| 1141 | |||
| 1142 | LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision, | ||
| 1143 | parameters.applet_resource_user_id); | ||
| 1144 | |||
| 1145 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1146 | rb.Push(ResultSuccess); | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | void Hid::SetNpadJoyHoldType(HLERequestContext& ctx) { | ||
| 1150 | IPC::RequestParser rp{ctx}; | ||
| 1151 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1152 | const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()}; | ||
| 1153 | |||
| 1154 | applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type); | ||
| 1155 | |||
| 1156 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", | ||
| 1157 | applet_resource_user_id, hold_type); | ||
| 1158 | |||
| 1159 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1160 | rb.Push(ResultSuccess); | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | void Hid::GetNpadJoyHoldType(HLERequestContext& ctx) { | ||
| 1164 | IPC::RequestParser rp{ctx}; | ||
| 1165 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1166 | |||
| 1167 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1168 | |||
| 1169 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1170 | rb.Push(ResultSuccess); | ||
| 1171 | rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad).GetHoldType()); | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | void Hid::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) { | ||
| 1175 | IPC::RequestParser rp{ctx}; | ||
| 1176 | struct Parameters { | ||
| 1177 | Core::HID::NpadIdType npad_id; | ||
| 1178 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1179 | u64 applet_resource_user_id; | ||
| 1180 | }; | ||
| 1181 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1182 | |||
| 1183 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1184 | |||
| 1185 | Core::HID::NpadIdType new_npad_id{}; | ||
| 1186 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1187 | controller.SetNpadMode(new_npad_id, parameters.npad_id, | ||
| 1188 | Controller_NPad::NpadJoyDeviceType::Left, | ||
| 1189 | Controller_NPad::NpadJoyAssignmentMode::Single); | ||
| 1190 | |||
| 1191 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | ||
| 1192 | parameters.applet_resource_user_id); | ||
| 1193 | |||
| 1194 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1195 | rb.Push(ResultSuccess); | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | void Hid::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) { | ||
| 1199 | IPC::RequestParser rp{ctx}; | ||
| 1200 | struct Parameters { | ||
| 1201 | Core::HID::NpadIdType npad_id; | ||
| 1202 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1203 | u64 applet_resource_user_id; | ||
| 1204 | Controller_NPad::NpadJoyDeviceType npad_joy_device_type; | ||
| 1205 | }; | ||
| 1206 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1207 | |||
| 1208 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1209 | |||
| 1210 | Core::HID::NpadIdType new_npad_id{}; | ||
| 1211 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1212 | controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, | ||
| 1213 | Controller_NPad::NpadJoyAssignmentMode::Single); | ||
| 1214 | |||
| 1215 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", | ||
| 1216 | parameters.npad_id, parameters.applet_resource_user_id, | ||
| 1217 | parameters.npad_joy_device_type); | ||
| 1218 | |||
| 1219 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1220 | rb.Push(ResultSuccess); | ||
| 1221 | } | ||
| 1222 | |||
| 1223 | void Hid::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) { | ||
| 1224 | IPC::RequestParser rp{ctx}; | ||
| 1225 | struct Parameters { | ||
| 1226 | Core::HID::NpadIdType npad_id; | ||
| 1227 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1228 | u64 applet_resource_user_id; | ||
| 1229 | }; | ||
| 1230 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1231 | |||
| 1232 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1233 | |||
| 1234 | Core::HID::NpadIdType new_npad_id{}; | ||
| 1235 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1236 | controller.SetNpadMode(new_npad_id, parameters.npad_id, {}, | ||
| 1237 | Controller_NPad::NpadJoyAssignmentMode::Dual); | ||
| 1238 | |||
| 1239 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | ||
| 1240 | parameters.applet_resource_user_id); | ||
| 1241 | |||
| 1242 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1243 | rb.Push(ResultSuccess); | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | void Hid::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) { | ||
| 1247 | IPC::RequestParser rp{ctx}; | ||
| 1248 | const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 1249 | const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 1250 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1251 | |||
| 1252 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1253 | const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); | ||
| 1254 | |||
| 1255 | LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", | ||
| 1256 | npad_id_1, npad_id_2, applet_resource_user_id); | ||
| 1257 | |||
| 1258 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1259 | rb.Push(result); | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | void Hid::StartLrAssignmentMode(HLERequestContext& ctx) { | ||
| 1263 | IPC::RequestParser rp{ctx}; | ||
| 1264 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1265 | |||
| 1266 | applet_resource->GetController<Controller_NPad>(HidController::NPad).StartLRAssignmentMode(); | ||
| 1267 | |||
| 1268 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1269 | |||
| 1270 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1271 | rb.Push(ResultSuccess); | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | void Hid::StopLrAssignmentMode(HLERequestContext& ctx) { | ||
| 1275 | IPC::RequestParser rp{ctx}; | ||
| 1276 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1277 | |||
| 1278 | applet_resource->GetController<Controller_NPad>(HidController::NPad).StopLRAssignmentMode(); | ||
| 1279 | |||
| 1280 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1281 | |||
| 1282 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1283 | rb.Push(ResultSuccess); | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | void Hid::SetNpadHandheldActivationMode(HLERequestContext& ctx) { | ||
| 1287 | IPC::RequestParser rp{ctx}; | ||
| 1288 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1289 | const auto activation_mode{rp.PopEnum<Controller_NPad::NpadHandheldActivationMode>()}; | ||
| 1290 | |||
| 1291 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1292 | .SetNpadHandheldActivationMode(activation_mode); | ||
| 1293 | |||
| 1294 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", | ||
| 1295 | applet_resource_user_id, activation_mode); | ||
| 1296 | |||
| 1297 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1298 | rb.Push(ResultSuccess); | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | void Hid::GetNpadHandheldActivationMode(HLERequestContext& ctx) { | ||
| 1302 | IPC::RequestParser rp{ctx}; | ||
| 1303 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1304 | |||
| 1305 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1306 | |||
| 1307 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1308 | rb.Push(ResultSuccess); | ||
| 1309 | rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1310 | .GetNpadHandheldActivationMode()); | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | void Hid::SwapNpadAssignment(HLERequestContext& ctx) { | ||
| 1314 | IPC::RequestParser rp{ctx}; | ||
| 1315 | const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 1316 | const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 1317 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1318 | |||
| 1319 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1320 | const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2); | ||
| 1321 | |||
| 1322 | LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", | ||
| 1323 | npad_id_1, npad_id_2, applet_resource_user_id); | ||
| 1324 | |||
| 1325 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1326 | rb.Push(result); | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | void Hid::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) { | ||
| 1330 | IPC::RequestParser rp{ctx}; | ||
| 1331 | struct Parameters { | ||
| 1332 | Core::HID::NpadIdType npad_id; | ||
| 1333 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1334 | u64 applet_resource_user_id; | ||
| 1335 | }; | ||
| 1336 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1337 | |||
| 1338 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1339 | |||
| 1340 | bool is_enabled = false; | ||
| 1341 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1342 | const auto result = | ||
| 1343 | controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); | ||
| 1344 | |||
| 1345 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", | ||
| 1346 | parameters.npad_id, parameters.applet_resource_user_id); | ||
| 1347 | |||
| 1348 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1349 | rb.Push(result); | ||
| 1350 | rb.Push(is_enabled); | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) { | ||
| 1354 | IPC::RequestParser rp{ctx}; | ||
| 1355 | struct Parameters { | ||
| 1356 | bool unintended_home_button_input_protection; | ||
| 1357 | INSERT_PADDING_BYTES_NOINIT(3); | ||
| 1358 | Core::HID::NpadIdType npad_id; | ||
| 1359 | u64 applet_resource_user_id; | ||
| 1360 | }; | ||
| 1361 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1362 | |||
| 1363 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1364 | |||
| 1365 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1366 | const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled( | ||
| 1367 | parameters.unintended_home_button_input_protection, parameters.npad_id); | ||
| 1368 | |||
| 1369 | LOG_WARNING(Service_HID, | ||
| 1370 | "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," | ||
| 1371 | "applet_resource_user_id={}", | ||
| 1372 | parameters.unintended_home_button_input_protection, parameters.npad_id, | ||
| 1373 | parameters.applet_resource_user_id); | ||
| 1374 | |||
| 1375 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1376 | rb.Push(result); | ||
| 1377 | } | ||
| 1378 | |||
| 1379 | void Hid::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) { | ||
| 1380 | IPC::RequestParser rp{ctx}; | ||
| 1381 | struct Parameters { | ||
| 1382 | Core::HID::NpadIdType npad_id; | ||
| 1383 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1384 | u64 applet_resource_user_id; | ||
| 1385 | Controller_NPad::NpadJoyDeviceType npad_joy_device_type; | ||
| 1386 | }; | ||
| 1387 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1388 | |||
| 1389 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1390 | |||
| 1391 | Core::HID::NpadIdType new_npad_id{}; | ||
| 1392 | auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1393 | const auto is_reassigned = | ||
| 1394 | controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, | ||
| 1395 | Controller_NPad::NpadJoyAssignmentMode::Single); | ||
| 1396 | |||
| 1397 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", | ||
| 1398 | parameters.npad_id, parameters.applet_resource_user_id, | ||
| 1399 | parameters.npad_joy_device_type); | ||
| 1400 | |||
| 1401 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1402 | rb.Push(ResultSuccess); | ||
| 1403 | rb.Push(is_reassigned); | ||
| 1404 | rb.PushEnum(new_npad_id); | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | void Hid::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) { | ||
| 1408 | IPC::RequestParser rp{ctx}; | ||
| 1409 | struct Parameters { | ||
| 1410 | bool analog_stick_use_center_clamp; | ||
| 1411 | INSERT_PADDING_BYTES_NOINIT(7); | ||
| 1412 | u64 applet_resource_user_id; | ||
| 1413 | }; | ||
| 1414 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1415 | |||
| 1416 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1417 | |||
| 1418 | GetAppletResource() | ||
| 1419 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1420 | .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp); | ||
| 1421 | |||
| 1422 | LOG_WARNING(Service_HID, | ||
| 1423 | "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", | ||
| 1424 | parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id); | ||
| 1425 | |||
| 1426 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1427 | rb.Push(ResultSuccess); | ||
| 1428 | } | ||
| 1429 | |||
| 1430 | void Hid::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) { | ||
| 1431 | IPC::RequestParser rp{ctx}; | ||
| 1432 | struct Parameters { | ||
| 1433 | Core::HID::NpadStyleSet npad_styleset; | ||
| 1434 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1435 | u64 applet_resource_user_id; | ||
| 1436 | Core::HID::NpadButton button; | ||
| 1437 | }; | ||
| 1438 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1439 | |||
| 1440 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1441 | |||
| 1442 | LOG_WARNING(Service_HID, | ||
| 1443 | "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}", | ||
| 1444 | parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button); | ||
| 1445 | |||
| 1446 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1447 | rb.Push(ResultSuccess); | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | void Hid::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) { | ||
| 1451 | IPC::RequestParser rp{ctx}; | ||
| 1452 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1453 | |||
| 1454 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 1455 | applet_resource_user_id); | ||
| 1456 | |||
| 1457 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1458 | rb.Push(ResultSuccess); | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | void Hid::GetVibrationDeviceInfo(HLERequestContext& ctx) { | ||
| 1462 | IPC::RequestParser rp{ctx}; | ||
| 1463 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; | ||
| 1464 | const auto& controller = | ||
| 1465 | GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1466 | |||
| 1467 | Core::HID::VibrationDeviceInfo vibration_device_info; | ||
| 1468 | bool check_device_index = false; | ||
| 1469 | |||
| 1470 | switch (vibration_device_handle.npad_type) { | ||
| 1471 | case Core::HID::NpadStyleIndex::ProController: | ||
| 1472 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 1473 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 1474 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 1475 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 1476 | vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; | ||
| 1477 | check_device_index = true; | ||
| 1478 | break; | ||
| 1479 | case Core::HID::NpadStyleIndex::GameCube: | ||
| 1480 | vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; | ||
| 1481 | break; | ||
| 1482 | case Core::HID::NpadStyleIndex::N64: | ||
| 1483 | vibration_device_info.type = Core::HID::VibrationDeviceType::N64; | ||
| 1484 | break; | ||
| 1485 | default: | ||
| 1486 | vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; | ||
| 1487 | break; | ||
| 1488 | } | ||
| 1489 | |||
| 1490 | vibration_device_info.position = Core::HID::VibrationDevicePosition::None; | ||
| 1491 | if (check_device_index) { | ||
| 1492 | switch (vibration_device_handle.device_index) { | ||
| 1493 | case Core::HID::DeviceIndex::Left: | ||
| 1494 | vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; | ||
| 1495 | break; | ||
| 1496 | case Core::HID::DeviceIndex::Right: | ||
| 1497 | vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; | ||
| 1498 | break; | ||
| 1499 | case Core::HID::DeviceIndex::None: | ||
| 1500 | default: | ||
| 1501 | ASSERT_MSG(false, "DeviceIndex should never be None!"); | ||
| 1502 | break; | ||
| 1503 | } | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", | ||
| 1507 | vibration_device_info.type, vibration_device_info.position); | ||
| 1508 | |||
| 1509 | const auto result = controller.IsDeviceHandleValid(vibration_device_handle); | ||
| 1510 | if (result.IsError()) { | ||
| 1511 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1512 | rb.Push(result); | ||
| 1513 | return; | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1517 | rb.Push(ResultSuccess); | ||
| 1518 | rb.PushRaw(vibration_device_info); | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | void Hid::SendVibrationValue(HLERequestContext& ctx) { | ||
| 1522 | IPC::RequestParser rp{ctx}; | ||
| 1523 | struct Parameters { | ||
| 1524 | Core::HID::VibrationDeviceHandle vibration_device_handle; | ||
| 1525 | Core::HID::VibrationValue vibration_value; | ||
| 1526 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1527 | u64 applet_resource_user_id; | ||
| 1528 | }; | ||
| 1529 | static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); | ||
| 1530 | |||
| 1531 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1532 | |||
| 1533 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1534 | .VibrateController(parameters.vibration_device_handle, parameters.vibration_value); | ||
| 1535 | |||
| 1536 | LOG_DEBUG(Service_HID, | ||
| 1537 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 1538 | parameters.vibration_device_handle.npad_type, | ||
| 1539 | parameters.vibration_device_handle.npad_id, | ||
| 1540 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); | ||
| 1541 | |||
| 1542 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1543 | rb.Push(ResultSuccess); | ||
| 1544 | } | ||
| 1545 | |||
| 1546 | void Hid::GetActualVibrationValue(HLERequestContext& ctx) { | ||
| 1547 | IPC::RequestParser rp{ctx}; | ||
| 1548 | struct Parameters { | ||
| 1549 | Core::HID::VibrationDeviceHandle vibration_device_handle; | ||
| 1550 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1551 | u64 applet_resource_user_id; | ||
| 1552 | }; | ||
| 1553 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1554 | |||
| 1555 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1556 | |||
| 1557 | LOG_DEBUG(Service_HID, | ||
| 1558 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 1559 | parameters.vibration_device_handle.npad_type, | ||
| 1560 | parameters.vibration_device_handle.npad_id, | ||
| 1561 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); | ||
| 1562 | |||
| 1563 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 1564 | rb.Push(ResultSuccess); | ||
| 1565 | rb.PushRaw(applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1566 | .GetLastVibration(parameters.vibration_device_handle)); | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | void Hid::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { | ||
| 1570 | LOG_DEBUG(Service_HID, "called"); | ||
| 1571 | |||
| 1572 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 1573 | rb.Push(ResultSuccess); | ||
| 1574 | rb.PushIpcInterface<IActiveVibrationDeviceList>(system, applet_resource); | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | void Hid::PermitVibration(HLERequestContext& ctx) { | ||
| 1578 | IPC::RequestParser rp{ctx}; | ||
| 1579 | const auto can_vibrate{rp.Pop<bool>()}; | ||
| 1580 | |||
| 1581 | // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value | ||
| 1582 | // by converting it to a bool | ||
| 1583 | Settings::values.vibration_enabled.SetValue(can_vibrate); | ||
| 1584 | |||
| 1585 | LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); | ||
| 1586 | |||
| 1587 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1588 | rb.Push(ResultSuccess); | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | void Hid::IsVibrationPermitted(HLERequestContext& ctx) { | ||
| 1592 | LOG_DEBUG(Service_HID, "called"); | ||
| 1593 | |||
| 1594 | // nnSDK checks if a float is greater than zero. We return the bool we stored earlier | ||
| 1595 | const auto is_enabled = Settings::values.vibration_enabled.GetValue(); | ||
| 1596 | |||
| 1597 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1598 | rb.Push(ResultSuccess); | ||
| 1599 | rb.Push(is_enabled); | ||
| 1600 | } | ||
| 1601 | |||
| 1602 | void Hid::SendVibrationValues(HLERequestContext& ctx) { | ||
| 1603 | IPC::RequestParser rp{ctx}; | ||
| 1604 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1605 | |||
| 1606 | const auto handle_data = ctx.ReadBuffer(0); | ||
| 1607 | const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0); | ||
| 1608 | const auto vibration_data = ctx.ReadBuffer(1); | ||
| 1609 | const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1); | ||
| 1610 | |||
| 1611 | auto vibration_device_handles = | ||
| 1612 | std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()), | ||
| 1613 | handle_count); | ||
| 1614 | auto vibration_values = std::span( | ||
| 1615 | reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count); | ||
| 1616 | |||
| 1617 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1618 | .VibrateControllers(vibration_device_handles, vibration_values); | ||
| 1619 | |||
| 1620 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1621 | |||
| 1622 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1623 | rb.Push(ResultSuccess); | ||
| 1624 | } | ||
| 1625 | |||
| 1626 | void Hid::SendVibrationGcErmCommand(HLERequestContext& ctx) { | ||
| 1627 | IPC::RequestParser rp{ctx}; | ||
| 1628 | struct Parameters { | ||
| 1629 | Core::HID::VibrationDeviceHandle vibration_device_handle; | ||
| 1630 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1631 | u64 applet_resource_user_id; | ||
| 1632 | Core::HID::VibrationGcErmCommand gc_erm_command; | ||
| 1633 | }; | ||
| 1634 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1635 | |||
| 1636 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1637 | |||
| 1638 | /** | ||
| 1639 | * Note: This uses yuzu-specific behavior such that the StopHard command produces | ||
| 1640 | * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below, | ||
| 1641 | * in order to differentiate between Stop and StopHard commands. | ||
| 1642 | * This is done to reuse the controller vibration functions made for regular controllers. | ||
| 1643 | */ | ||
| 1644 | const auto vibration_value = [parameters] { | ||
| 1645 | switch (parameters.gc_erm_command) { | ||
| 1646 | case Core::HID::VibrationGcErmCommand::Stop: | ||
| 1647 | return Core::HID::VibrationValue{ | ||
| 1648 | .low_amplitude = 0.0f, | ||
| 1649 | .low_frequency = 160.0f, | ||
| 1650 | .high_amplitude = 0.0f, | ||
| 1651 | .high_frequency = 320.0f, | ||
| 1652 | }; | ||
| 1653 | case Core::HID::VibrationGcErmCommand::Start: | ||
| 1654 | return Core::HID::VibrationValue{ | ||
| 1655 | .low_amplitude = 1.0f, | ||
| 1656 | .low_frequency = 160.0f, | ||
| 1657 | .high_amplitude = 1.0f, | ||
| 1658 | .high_frequency = 320.0f, | ||
| 1659 | }; | ||
| 1660 | case Core::HID::VibrationGcErmCommand::StopHard: | ||
| 1661 | return Core::HID::VibrationValue{ | ||
| 1662 | .low_amplitude = 0.0f, | ||
| 1663 | .low_frequency = 0.0f, | ||
| 1664 | .high_amplitude = 0.0f, | ||
| 1665 | .high_frequency = 0.0f, | ||
| 1666 | }; | ||
| 1667 | default: | ||
| 1668 | return Core::HID::DEFAULT_VIBRATION_VALUE; | ||
| 1669 | } | ||
| 1670 | }(); | ||
| 1671 | |||
| 1672 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1673 | .VibrateController(parameters.vibration_device_handle, vibration_value); | ||
| 1674 | |||
| 1675 | LOG_DEBUG(Service_HID, | ||
| 1676 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " | ||
| 1677 | "gc_erm_command={}", | ||
| 1678 | parameters.vibration_device_handle.npad_type, | ||
| 1679 | parameters.vibration_device_handle.npad_id, | ||
| 1680 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id, | ||
| 1681 | parameters.gc_erm_command); | ||
| 1682 | |||
| 1683 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1684 | rb.Push(ResultSuccess); | ||
| 1685 | } | ||
| 1686 | |||
| 1687 | void Hid::GetActualVibrationGcErmCommand(HLERequestContext& ctx) { | ||
| 1688 | IPC::RequestParser rp{ctx}; | ||
| 1689 | struct Parameters { | ||
| 1690 | Core::HID::VibrationDeviceHandle vibration_device_handle; | ||
| 1691 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1692 | u64 applet_resource_user_id; | ||
| 1693 | }; | ||
| 1694 | |||
| 1695 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1696 | |||
| 1697 | const auto last_vibration = applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1698 | .GetLastVibration(parameters.vibration_device_handle); | ||
| 1699 | |||
| 1700 | const auto gc_erm_command = [last_vibration] { | ||
| 1701 | if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { | ||
| 1702 | return Core::HID::VibrationGcErmCommand::Start; | ||
| 1703 | } | ||
| 1704 | |||
| 1705 | /** | ||
| 1706 | * Note: This uses yuzu-specific behavior such that the StopHard command produces | ||
| 1707 | * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function | ||
| 1708 | * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. | ||
| 1709 | * This is done to reuse the controller vibration functions made for regular controllers. | ||
| 1710 | */ | ||
| 1711 | if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) { | ||
| 1712 | return Core::HID::VibrationGcErmCommand::StopHard; | ||
| 1713 | } | ||
| 1714 | |||
| 1715 | return Core::HID::VibrationGcErmCommand::Stop; | ||
| 1716 | }(); | ||
| 1717 | |||
| 1718 | LOG_DEBUG(Service_HID, | ||
| 1719 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 1720 | parameters.vibration_device_handle.npad_type, | ||
| 1721 | parameters.vibration_device_handle.npad_id, | ||
| 1722 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); | ||
| 1723 | |||
| 1724 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1725 | rb.Push(ResultSuccess); | ||
| 1726 | rb.PushEnum(gc_erm_command); | ||
| 1727 | } | ||
| 1728 | |||
| 1729 | void Hid::BeginPermitVibrationSession(HLERequestContext& ctx) { | ||
| 1730 | IPC::RequestParser rp{ctx}; | ||
| 1731 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1732 | |||
| 1733 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1734 | .SetPermitVibrationSession(true); | ||
| 1735 | |||
| 1736 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1737 | |||
| 1738 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1739 | rb.Push(ResultSuccess); | ||
| 1740 | } | ||
| 1741 | |||
| 1742 | void Hid::EndPermitVibrationSession(HLERequestContext& ctx) { | ||
| 1743 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1744 | .SetPermitVibrationSession(false); | ||
| 1745 | |||
| 1746 | LOG_DEBUG(Service_HID, "called"); | ||
| 1747 | |||
| 1748 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1749 | rb.Push(ResultSuccess); | ||
| 1750 | } | ||
| 1751 | |||
| 1752 | void Hid::IsVibrationDeviceMounted(HLERequestContext& ctx) { | ||
| 1753 | IPC::RequestParser rp{ctx}; | ||
| 1754 | struct Parameters { | ||
| 1755 | Core::HID::VibrationDeviceHandle vibration_device_handle; | ||
| 1756 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1757 | u64 applet_resource_user_id; | ||
| 1758 | }; | ||
| 1759 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1760 | |||
| 1761 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1762 | |||
| 1763 | LOG_DEBUG(Service_HID, | ||
| 1764 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 1765 | parameters.vibration_device_handle.npad_type, | ||
| 1766 | parameters.vibration_device_handle.npad_id, | ||
| 1767 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); | ||
| 1768 | |||
| 1769 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1770 | rb.Push(ResultSuccess); | ||
| 1771 | rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 1772 | .IsVibrationDeviceMounted(parameters.vibration_device_handle)); | ||
| 1773 | } | ||
| 1774 | |||
| 1775 | void Hid::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { | ||
| 1776 | IPC::RequestParser rp{ctx}; | ||
| 1777 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1778 | |||
| 1779 | applet_resource->ActivateController(HidController::ConsoleSixAxisSensor); | ||
| 1780 | |||
| 1781 | LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1782 | |||
| 1783 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1784 | rb.Push(ResultSuccess); | ||
| 1785 | } | ||
| 1786 | |||
| 1787 | void Hid::StartConsoleSixAxisSensor(HLERequestContext& ctx) { | ||
| 1788 | IPC::RequestParser rp{ctx}; | ||
| 1789 | struct Parameters { | ||
| 1790 | Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; | ||
| 1791 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1792 | u64 applet_resource_user_id; | ||
| 1793 | }; | ||
| 1794 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1795 | |||
| 1796 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1797 | |||
| 1798 | LOG_WARNING(Service_HID, | ||
| 1799 | "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", | ||
| 1800 | parameters.console_sixaxis_handle.unknown_1, | ||
| 1801 | parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); | ||
| 1802 | |||
| 1803 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1804 | rb.Push(ResultSuccess); | ||
| 1805 | } | ||
| 1806 | |||
| 1807 | void Hid::StopConsoleSixAxisSensor(HLERequestContext& ctx) { | ||
| 1808 | IPC::RequestParser rp{ctx}; | ||
| 1809 | struct Parameters { | ||
| 1810 | Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; | ||
| 1811 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1812 | u64 applet_resource_user_id; | ||
| 1813 | }; | ||
| 1814 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1815 | |||
| 1816 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1817 | |||
| 1818 | LOG_WARNING(Service_HID, | ||
| 1819 | "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", | ||
| 1820 | parameters.console_sixaxis_handle.unknown_1, | ||
| 1821 | parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); | ||
| 1822 | |||
| 1823 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1824 | rb.Push(ResultSuccess); | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | void Hid::ActivateSevenSixAxisSensor(HLERequestContext& ctx) { | ||
| 1828 | IPC::RequestParser rp{ctx}; | ||
| 1829 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1830 | |||
| 1831 | applet_resource->ActivateController(HidController::ConsoleSixAxisSensor); | ||
| 1832 | |||
| 1833 | LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1834 | |||
| 1835 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1836 | rb.Push(ResultSuccess); | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | void Hid::StartSevenSixAxisSensor(HLERequestContext& ctx) { | ||
| 1840 | IPC::RequestParser rp{ctx}; | ||
| 1841 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1842 | |||
| 1843 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 1844 | applet_resource_user_id); | ||
| 1845 | |||
| 1846 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1847 | rb.Push(ResultSuccess); | ||
| 1848 | } | ||
| 1849 | |||
| 1850 | void Hid::StopSevenSixAxisSensor(HLERequestContext& ctx) { | ||
| 1851 | IPC::RequestParser rp{ctx}; | ||
| 1852 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1853 | |||
| 1854 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 1855 | applet_resource_user_id); | ||
| 1856 | |||
| 1857 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1858 | rb.Push(ResultSuccess); | ||
| 1859 | } | ||
| 1860 | |||
| 1861 | void Hid::InitializeSevenSixAxisSensor(HLERequestContext& ctx) { | ||
| 1862 | IPC::RequestParser rp{ctx}; | ||
| 1863 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1864 | const auto t_mem_1_size{rp.Pop<u64>()}; | ||
| 1865 | const auto t_mem_2_size{rp.Pop<u64>()}; | ||
| 1866 | const auto t_mem_1_handle{ctx.GetCopyHandle(0)}; | ||
| 1867 | const auto t_mem_2_handle{ctx.GetCopyHandle(1)}; | ||
| 1868 | |||
| 1869 | ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes"); | ||
| 1870 | ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes"); | ||
| 1871 | |||
| 1872 | auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 1873 | t_mem_1_handle); | ||
| 1874 | |||
| 1875 | if (t_mem_1.IsNull()) { | ||
| 1876 | LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle); | ||
| 1877 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1878 | rb.Push(ResultUnknown); | ||
| 1879 | return; | ||
| 1880 | } | ||
| 1881 | |||
| 1882 | auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 1883 | t_mem_2_handle); | ||
| 1884 | |||
| 1885 | if (t_mem_2.IsNull()) { | ||
| 1886 | LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle); | ||
| 1887 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1888 | rb.Push(ResultUnknown); | ||
| 1889 | return; | ||
| 1890 | } | ||
| 1891 | |||
| 1892 | ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size"); | ||
| 1893 | ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size"); | ||
| 1894 | |||
| 1895 | // Activate console six axis controller | ||
| 1896 | applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) | ||
| 1897 | .ActivateController(); | ||
| 1898 | |||
| 1899 | applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) | ||
| 1900 | .SetTransferMemoryAddress(t_mem_1->GetSourceAddress()); | ||
| 1901 | |||
| 1902 | LOG_WARNING(Service_HID, | ||
| 1903 | "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, " | ||
| 1904 | "applet_resource_user_id={}", | ||
| 1905 | t_mem_1_handle, t_mem_2_handle, applet_resource_user_id); | ||
| 1906 | |||
| 1907 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1908 | rb.Push(ResultSuccess); | ||
| 1909 | } | ||
| 1910 | |||
| 1911 | void Hid::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) { | ||
| 1912 | IPC::RequestParser rp{ctx}; | ||
| 1913 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1914 | |||
| 1915 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 1916 | applet_resource_user_id); | ||
| 1917 | |||
| 1918 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1919 | rb.Push(ResultSuccess); | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | void Hid::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) { | ||
| 1923 | IPC::RequestParser rp{ctx}; | ||
| 1924 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1925 | |||
| 1926 | applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) | ||
| 1927 | .ResetTimestamp(); | ||
| 1928 | |||
| 1929 | LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1930 | |||
| 1931 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1932 | rb.Push(ResultSuccess); | ||
| 1933 | } | ||
| 1934 | |||
| 1935 | void Hid::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { | ||
| 1936 | IPC::RequestParser rp{ctx}; | ||
| 1937 | |||
| 1938 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 1939 | |||
| 1940 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1941 | rb.Push(ResultSuccess); | ||
| 1942 | rb.Push(false); | ||
| 1943 | } | ||
| 1944 | |||
| 1945 | void Hid::GetPalmaConnectionHandle(HLERequestContext& ctx) { | ||
| 1946 | IPC::RequestParser rp{ctx}; | ||
| 1947 | struct Parameters { | ||
| 1948 | Core::HID::NpadIdType npad_id; | ||
| 1949 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1950 | u64 applet_resource_user_id; | ||
| 1951 | }; | ||
| 1952 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1953 | |||
| 1954 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1955 | |||
| 1956 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", | ||
| 1957 | parameters.npad_id, parameters.applet_resource_user_id); | ||
| 1958 | |||
| 1959 | Controller_Palma::PalmaConnectionHandle handle; | ||
| 1960 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 1961 | const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle); | ||
| 1962 | |||
| 1963 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1964 | rb.Push(result); | ||
| 1965 | rb.PushRaw(handle); | ||
| 1966 | } | ||
| 1967 | |||
| 1968 | void Hid::InitializePalma(HLERequestContext& ctx) { | ||
| 1969 | IPC::RequestParser rp{ctx}; | ||
| 1970 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 1971 | |||
| 1972 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 1973 | |||
| 1974 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 1975 | const auto result = controller.InitializePalma(connection_handle); | ||
| 1976 | |||
| 1977 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1978 | rb.Push(result); | ||
| 1979 | } | ||
| 1980 | |||
| 1981 | void Hid::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) { | ||
| 1982 | IPC::RequestParser rp{ctx}; | ||
| 1983 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 1984 | |||
| 1985 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 1986 | |||
| 1987 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 1988 | |||
| 1989 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 1990 | rb.Push(ResultSuccess); | ||
| 1991 | rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle)); | ||
| 1992 | } | ||
| 1993 | |||
| 1994 | void Hid::GetPalmaOperationInfo(HLERequestContext& ctx) { | ||
| 1995 | IPC::RequestParser rp{ctx}; | ||
| 1996 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 1997 | |||
| 1998 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 1999 | |||
| 2000 | Controller_Palma::PalmaOperationType operation_type; | ||
| 2001 | Controller_Palma::PalmaOperationData data; | ||
| 2002 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 2003 | const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data); | ||
| 2004 | |||
| 2005 | if (result.IsError()) { | ||
| 2006 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2007 | rb.Push(result); | ||
| 2008 | } | ||
| 2009 | |||
| 2010 | ctx.WriteBuffer(data); | ||
| 2011 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 2012 | rb.Push(result); | ||
| 2013 | rb.Push(static_cast<u64>(operation_type)); | ||
| 2014 | } | ||
| 2015 | |||
| 2016 | void Hid::PlayPalmaActivity(HLERequestContext& ctx) { | ||
| 2017 | IPC::RequestParser rp{ctx}; | ||
| 2018 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2019 | const auto palma_activity{rp.Pop<u64>()}; | ||
| 2020 | |||
| 2021 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", | ||
| 2022 | connection_handle.npad_id, palma_activity); | ||
| 2023 | |||
| 2024 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 2025 | const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity); | ||
| 2026 | |||
| 2027 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2028 | rb.Push(result); | ||
| 2029 | } | ||
| 2030 | |||
| 2031 | void Hid::SetPalmaFrModeType(HLERequestContext& ctx) { | ||
| 2032 | IPC::RequestParser rp{ctx}; | ||
| 2033 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2034 | const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()}; | ||
| 2035 | |||
| 2036 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", | ||
| 2037 | connection_handle.npad_id, fr_mode); | ||
| 2038 | |||
| 2039 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 2040 | const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode); | ||
| 2041 | |||
| 2042 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2043 | rb.Push(result); | ||
| 2044 | } | ||
| 2045 | |||
| 2046 | void Hid::ReadPalmaStep(HLERequestContext& ctx) { | ||
| 2047 | IPC::RequestParser rp{ctx}; | ||
| 2048 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2049 | |||
| 2050 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2051 | |||
| 2052 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 2053 | const auto result = controller.ReadPalmaStep(connection_handle); | ||
| 2054 | |||
| 2055 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2056 | rb.Push(result); | ||
| 2057 | } | ||
| 2058 | |||
| 2059 | void Hid::EnablePalmaStep(HLERequestContext& ctx) { | ||
| 2060 | IPC::RequestParser rp{ctx}; | ||
| 2061 | struct Parameters { | ||
| 2062 | bool is_enabled; | ||
| 2063 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 2064 | Controller_Palma::PalmaConnectionHandle connection_handle; | ||
| 2065 | }; | ||
| 2066 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 2067 | |||
| 2068 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 2069 | |||
| 2070 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", | ||
| 2071 | parameters.connection_handle.npad_id, parameters.is_enabled); | ||
| 2072 | |||
| 2073 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 2074 | const auto result = | ||
| 2075 | controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); | ||
| 2076 | |||
| 2077 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2078 | rb.Push(result); | ||
| 2079 | } | ||
| 2080 | |||
| 2081 | void Hid::ResetPalmaStep(HLERequestContext& ctx) { | ||
| 2082 | IPC::RequestParser rp{ctx}; | ||
| 2083 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2084 | |||
| 2085 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2086 | |||
| 2087 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 2088 | const auto result = controller.ResetPalmaStep(connection_handle); | ||
| 2089 | |||
| 2090 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2091 | rb.Push(result); | ||
| 2092 | } | ||
| 2093 | |||
| 2094 | void Hid::ReadPalmaApplicationSection(HLERequestContext& ctx) { | ||
| 2095 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2096 | |||
| 2097 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2098 | rb.Push(ResultSuccess); | ||
| 2099 | } | ||
| 2100 | |||
| 2101 | void Hid::WritePalmaApplicationSection(HLERequestContext& ctx) { | ||
| 2102 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2103 | |||
| 2104 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2105 | rb.Push(ResultSuccess); | ||
| 2106 | } | ||
| 2107 | |||
| 2108 | void Hid::ReadPalmaUniqueCode(HLERequestContext& ctx) { | ||
| 2109 | IPC::RequestParser rp{ctx}; | ||
| 2110 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2111 | |||
| 2112 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2113 | |||
| 2114 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2115 | .ReadPalmaUniqueCode(connection_handle); | ||
| 2116 | |||
| 2117 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2118 | rb.Push(ResultSuccess); | ||
| 2119 | } | ||
| 2120 | |||
| 2121 | void Hid::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) { | ||
| 2122 | IPC::RequestParser rp{ctx}; | ||
| 2123 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2124 | |||
| 2125 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2126 | |||
| 2127 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2128 | .SetPalmaUniqueCodeInvalid(connection_handle); | ||
| 2129 | |||
| 2130 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2131 | rb.Push(ResultSuccess); | ||
| 2132 | } | ||
| 2133 | |||
| 2134 | void Hid::WritePalmaActivityEntry(HLERequestContext& ctx) { | ||
| 2135 | LOG_CRITICAL(Service_HID, "(STUBBED) called"); | ||
| 2136 | |||
| 2137 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2138 | rb.Push(ResultSuccess); | ||
| 2139 | } | ||
| 2140 | |||
| 2141 | void Hid::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) { | ||
| 2142 | IPC::RequestParser rp{ctx}; | ||
| 2143 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2144 | const auto unknown{rp.Pop<u64>()}; | ||
| 2145 | |||
| 2146 | [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); | ||
| 2147 | |||
| 2148 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", | ||
| 2149 | connection_handle.npad_id, unknown); | ||
| 2150 | |||
| 2151 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2152 | .WritePalmaRgbLedPatternEntry(connection_handle, unknown); | ||
| 2153 | |||
| 2154 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2155 | rb.Push(ResultSuccess); | ||
| 2156 | } | ||
| 2157 | |||
| 2158 | void Hid::WritePalmaWaveEntry(HLERequestContext& ctx) { | ||
| 2159 | IPC::RequestParser rp{ctx}; | ||
| 2160 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2161 | const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()}; | ||
| 2162 | const auto unknown{rp.Pop<u64>()}; | ||
| 2163 | const auto t_mem_size{rp.Pop<u64>()}; | ||
| 2164 | const auto t_mem_handle{ctx.GetCopyHandle(0)}; | ||
| 2165 | const auto size{rp.Pop<u64>()}; | ||
| 2166 | |||
| 2167 | ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); | ||
| 2168 | |||
| 2169 | auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 2170 | t_mem_handle); | ||
| 2171 | |||
| 2172 | if (t_mem.IsNull()) { | ||
| 2173 | LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); | ||
| 2174 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2175 | rb.Push(ResultUnknown); | ||
| 2176 | return; | ||
| 2177 | } | ||
| 2178 | |||
| 2179 | ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); | ||
| 2180 | |||
| 2181 | LOG_WARNING(Service_HID, | ||
| 2182 | "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, " | ||
| 2183 | "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", | ||
| 2184 | connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); | ||
| 2185 | |||
| 2186 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2187 | .WritePalmaWaveEntry(connection_handle, wave_set, t_mem->GetSourceAddress(), t_mem_size); | ||
| 2188 | |||
| 2189 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2190 | rb.Push(ResultSuccess); | ||
| 2191 | } | ||
| 2192 | |||
| 2193 | void Hid::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { | ||
| 2194 | IPC::RequestParser rp{ctx}; | ||
| 2195 | struct Parameters { | ||
| 2196 | s32 database_id_version; | ||
| 2197 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 2198 | Controller_Palma::PalmaConnectionHandle connection_handle; | ||
| 2199 | }; | ||
| 2200 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 2201 | |||
| 2202 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 2203 | |||
| 2204 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", | ||
| 2205 | parameters.connection_handle.npad_id, parameters.database_id_version); | ||
| 2206 | |||
| 2207 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2208 | .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle, | ||
| 2209 | parameters.database_id_version); | ||
| 2210 | |||
| 2211 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2212 | rb.Push(ResultSuccess); | ||
| 2213 | } | ||
| 2214 | |||
| 2215 | void Hid::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { | ||
| 2216 | IPC::RequestParser rp{ctx}; | ||
| 2217 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2218 | |||
| 2219 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2220 | |||
| 2221 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2222 | .GetPalmaDataBaseIdentificationVersion(connection_handle); | ||
| 2223 | |||
| 2224 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2225 | rb.Push(ResultSuccess); | ||
| 2226 | } | ||
| 2227 | |||
| 2228 | void Hid::SuspendPalmaFeature(HLERequestContext& ctx) { | ||
| 2229 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2230 | |||
| 2231 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2232 | rb.Push(ResultSuccess); | ||
| 2233 | } | ||
| 2234 | |||
| 2235 | void Hid::GetPalmaOperationResult(HLERequestContext& ctx) { | ||
| 2236 | IPC::RequestParser rp{ctx}; | ||
| 2237 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2238 | |||
| 2239 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2240 | |||
| 2241 | const auto result = applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2242 | .GetPalmaOperationResult(connection_handle); | ||
| 2243 | |||
| 2244 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2245 | rb.Push(result); | ||
| 2246 | } | ||
| 2247 | |||
| 2248 | void Hid::ReadPalmaPlayLog(HLERequestContext& ctx) { | ||
| 2249 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2250 | |||
| 2251 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2252 | rb.Push(ResultSuccess); | ||
| 2253 | } | ||
| 2254 | |||
| 2255 | void Hid::ResetPalmaPlayLog(HLERequestContext& ctx) { | ||
| 2256 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2257 | |||
| 2258 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2259 | rb.Push(ResultSuccess); | ||
| 2260 | } | ||
| 2261 | |||
| 2262 | void Hid::SetIsPalmaAllConnectable(HLERequestContext& ctx) { | ||
| 2263 | IPC::RequestParser rp{ctx}; | ||
| 2264 | struct Parameters { | ||
| 2265 | bool is_palma_all_connectable; | ||
| 2266 | INSERT_PADDING_BYTES_NOINIT(7); | ||
| 2267 | u64 applet_resource_user_id; | ||
| 2268 | }; | ||
| 2269 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 2270 | |||
| 2271 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 2272 | |||
| 2273 | LOG_WARNING(Service_HID, | ||
| 2274 | "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", | ||
| 2275 | parameters.is_palma_all_connectable, parameters.applet_resource_user_id); | ||
| 2276 | |||
| 2277 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2278 | .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); | ||
| 2279 | |||
| 2280 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2281 | rb.Push(ResultSuccess); | ||
| 2282 | } | ||
| 2283 | |||
| 2284 | void Hid::SetIsPalmaPairedConnectable(HLERequestContext& ctx) { | ||
| 2285 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2286 | |||
| 2287 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2288 | rb.Push(ResultSuccess); | ||
| 2289 | } | ||
| 2290 | |||
| 2291 | void Hid::PairPalma(HLERequestContext& ctx) { | ||
| 2292 | IPC::RequestParser rp{ctx}; | ||
| 2293 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2294 | |||
| 2295 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2296 | |||
| 2297 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2298 | .PairPalma(connection_handle); | ||
| 2299 | |||
| 2300 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2301 | rb.Push(ResultSuccess); | ||
| 2302 | } | ||
| 2303 | |||
| 2304 | void Hid::SetPalmaBoostMode(HLERequestContext& ctx) { | ||
| 2305 | IPC::RequestParser rp{ctx}; | ||
| 2306 | const auto palma_boost_mode{rp.Pop<bool>()}; | ||
| 2307 | |||
| 2308 | LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); | ||
| 2309 | |||
| 2310 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2311 | .SetPalmaBoostMode(palma_boost_mode); | ||
| 2312 | |||
| 2313 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2314 | rb.Push(ResultSuccess); | ||
| 2315 | } | ||
| 2316 | |||
| 2317 | void Hid::CancelWritePalmaWaveEntry(HLERequestContext& ctx) { | ||
| 2318 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2319 | |||
| 2320 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2321 | rb.Push(ResultSuccess); | ||
| 2322 | } | ||
| 2323 | |||
| 2324 | void Hid::EnablePalmaBoostMode(HLERequestContext& ctx) { | ||
| 2325 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2326 | |||
| 2327 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2328 | rb.Push(ResultSuccess); | ||
| 2329 | } | ||
| 2330 | |||
| 2331 | void Hid::GetPalmaBluetoothAddress(HLERequestContext& ctx) { | ||
| 2332 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2333 | |||
| 2334 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2335 | rb.Push(ResultSuccess); | ||
| 2336 | } | ||
| 2337 | |||
| 2338 | void Hid::SetDisallowedPalmaConnection(HLERequestContext& ctx) { | ||
| 2339 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2340 | |||
| 2341 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2342 | rb.Push(ResultSuccess); | ||
| 2343 | } | ||
| 2344 | |||
| 2345 | void Hid::SetNpadCommunicationMode(HLERequestContext& ctx) { | ||
| 2346 | IPC::RequestParser rp{ctx}; | ||
| 2347 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 2348 | const auto communication_mode{rp.PopEnum<Controller_NPad::NpadCommunicationMode>()}; | ||
| 2349 | |||
| 2350 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 2351 | .SetNpadCommunicationMode(communication_mode); | ||
| 2352 | |||
| 2353 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}", | ||
| 2354 | applet_resource_user_id, communication_mode); | ||
| 2355 | |||
| 2356 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2357 | rb.Push(ResultSuccess); | ||
| 2358 | } | ||
| 2359 | |||
| 2360 | void Hid::GetNpadCommunicationMode(HLERequestContext& ctx) { | ||
| 2361 | IPC::RequestParser rp{ctx}; | ||
| 2362 | |||
| 2363 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2364 | |||
| 2365 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 2366 | rb.Push(ResultSuccess); | ||
| 2367 | rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 2368 | .GetNpadCommunicationMode()); | ||
| 2369 | } | ||
| 2370 | |||
| 2371 | void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) { | ||
| 2372 | IPC::RequestParser rp{ctx}; | ||
| 2373 | const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()}; | ||
| 2374 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 2375 | |||
| 2376 | LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", | ||
| 2377 | touchscreen_mode.mode, applet_resource_user_id); | ||
| 2378 | |||
| 2379 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2380 | rb.Push(ResultSuccess); | ||
| 2381 | } | ||
| 2382 | |||
| 2383 | void Hid::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) { | ||
| 2384 | IPC::RequestParser rp{ctx}; | ||
| 2385 | struct Parameters { | ||
| 2386 | s32 unknown; | ||
| 2387 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 2388 | u64 applet_resource_user_id; | ||
| 2389 | }; | ||
| 2390 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 2391 | |||
| 2392 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 2393 | |||
| 2394 | LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", | ||
| 2395 | parameters.unknown, parameters.applet_resource_user_id); | ||
| 2396 | |||
| 2397 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 2398 | rb.Push(ResultSuccess); | ||
| 2399 | rb.Push(false); | ||
| 2400 | } | ||
| 2401 | |||
| 2402 | class HidDbg final : public ServiceFramework<HidDbg> { | ||
| 2403 | public: | ||
| 2404 | explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} { | ||
| 2405 | // clang-format off | ||
| 2406 | static const FunctionInfo functions[] = { | ||
| 2407 | {0, nullptr, "DeactivateDebugPad"}, | ||
| 2408 | {1, nullptr, "SetDebugPadAutoPilotState"}, | ||
| 2409 | {2, nullptr, "UnsetDebugPadAutoPilotState"}, | ||
| 2410 | {10, nullptr, "DeactivateTouchScreen"}, | ||
| 2411 | {11, nullptr, "SetTouchScreenAutoPilotState"}, | ||
| 2412 | {12, nullptr, "UnsetTouchScreenAutoPilotState"}, | ||
| 2413 | {13, nullptr, "GetTouchScreenConfiguration"}, | ||
| 2414 | {14, nullptr, "ProcessTouchScreenAutoTune"}, | ||
| 2415 | {15, nullptr, "ForceStopTouchScreenManagement"}, | ||
| 2416 | {16, nullptr, "ForceRestartTouchScreenManagement"}, | ||
| 2417 | {17, nullptr, "IsTouchScreenManaged"}, | ||
| 2418 | {20, nullptr, "DeactivateMouse"}, | ||
| 2419 | {21, nullptr, "SetMouseAutoPilotState"}, | ||
| 2420 | {22, nullptr, "UnsetMouseAutoPilotState"}, | ||
| 2421 | {25, nullptr, "SetDebugMouseAutoPilotState"}, | ||
| 2422 | {26, nullptr, "UnsetDebugMouseAutoPilotState"}, | ||
| 2423 | {30, nullptr, "DeactivateKeyboard"}, | ||
| 2424 | {31, nullptr, "SetKeyboardAutoPilotState"}, | ||
| 2425 | {32, nullptr, "UnsetKeyboardAutoPilotState"}, | ||
| 2426 | {50, nullptr, "DeactivateXpad"}, | ||
| 2427 | {51, nullptr, "SetXpadAutoPilotState"}, | ||
| 2428 | {52, nullptr, "UnsetXpadAutoPilotState"}, | ||
| 2429 | {53, nullptr, "DeactivateJoyXpad"}, | ||
| 2430 | {60, nullptr, "ClearNpadSystemCommonPolicy"}, | ||
| 2431 | {61, nullptr, "DeactivateNpad"}, | ||
| 2432 | {62, nullptr, "ForceDisconnectNpad"}, | ||
| 2433 | {91, nullptr, "DeactivateGesture"}, | ||
| 2434 | {110, nullptr, "DeactivateHomeButton"}, | ||
| 2435 | {111, nullptr, "SetHomeButtonAutoPilotState"}, | ||
| 2436 | {112, nullptr, "UnsetHomeButtonAutoPilotState"}, | ||
| 2437 | {120, nullptr, "DeactivateSleepButton"}, | ||
| 2438 | {121, nullptr, "SetSleepButtonAutoPilotState"}, | ||
| 2439 | {122, nullptr, "UnsetSleepButtonAutoPilotState"}, | ||
| 2440 | {123, nullptr, "DeactivateInputDetector"}, | ||
| 2441 | {130, nullptr, "DeactivateCaptureButton"}, | ||
| 2442 | {131, nullptr, "SetCaptureButtonAutoPilotState"}, | ||
| 2443 | {132, nullptr, "UnsetCaptureButtonAutoPilotState"}, | ||
| 2444 | {133, nullptr, "SetShiftAccelerometerCalibrationValue"}, | ||
| 2445 | {134, nullptr, "GetShiftAccelerometerCalibrationValue"}, | ||
| 2446 | {135, nullptr, "SetShiftGyroscopeCalibrationValue"}, | ||
| 2447 | {136, nullptr, "GetShiftGyroscopeCalibrationValue"}, | ||
| 2448 | {140, nullptr, "DeactivateConsoleSixAxisSensor"}, | ||
| 2449 | {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, | ||
| 2450 | {142, nullptr, "DeactivateSevenSixAxisSensor"}, | ||
| 2451 | {143, nullptr, "GetConsoleSixAxisSensorCountStates"}, | ||
| 2452 | {144, nullptr, "GetAccelerometerFsr"}, | ||
| 2453 | {145, nullptr, "SetAccelerometerFsr"}, | ||
| 2454 | {146, nullptr, "GetAccelerometerOdr"}, | ||
| 2455 | {147, nullptr, "SetAccelerometerOdr"}, | ||
| 2456 | {148, nullptr, "GetGyroscopeFsr"}, | ||
| 2457 | {149, nullptr, "SetGyroscopeFsr"}, | ||
| 2458 | {150, nullptr, "GetGyroscopeOdr"}, | ||
| 2459 | {151, nullptr, "SetGyroscopeOdr"}, | ||
| 2460 | {152, nullptr, "GetWhoAmI"}, | ||
| 2461 | {201, nullptr, "ActivateFirmwareUpdate"}, | ||
| 2462 | {202, nullptr, "DeactivateFirmwareUpdate"}, | ||
| 2463 | {203, nullptr, "StartFirmwareUpdate"}, | ||
| 2464 | {204, nullptr, "GetFirmwareUpdateStage"}, | ||
| 2465 | {205, nullptr, "GetFirmwareVersion"}, | ||
| 2466 | {206, nullptr, "GetDestinationFirmwareVersion"}, | ||
| 2467 | {207, nullptr, "DiscardFirmwareInfoCacheForRevert"}, | ||
| 2468 | {208, nullptr, "StartFirmwareUpdateForRevert"}, | ||
| 2469 | {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, | ||
| 2470 | {210, nullptr, "IsFirmwareUpdatingDevice"}, | ||
| 2471 | {211, nullptr, "StartFirmwareUpdateIndividual"}, | ||
| 2472 | {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"}, | ||
| 2473 | {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"}, | ||
| 2474 | {221, nullptr, "UpdateControllerColor"}, | ||
| 2475 | {222, nullptr, "ConnectUsbPadsAsync"}, | ||
| 2476 | {223, nullptr, "DisconnectUsbPadsAsync"}, | ||
| 2477 | {224, nullptr, "UpdateDesignInfo"}, | ||
| 2478 | {225, nullptr, "GetUniquePadDriverState"}, | ||
| 2479 | {226, nullptr, "GetSixAxisSensorDriverStates"}, | ||
| 2480 | {227, nullptr, "GetRxPacketHistory"}, | ||
| 2481 | {228, nullptr, "AcquireOperationEventHandle"}, | ||
| 2482 | {229, nullptr, "ReadSerialFlash"}, | ||
| 2483 | {230, nullptr, "WriteSerialFlash"}, | ||
| 2484 | {231, nullptr, "GetOperationResult"}, | ||
| 2485 | {232, nullptr, "EnableShipmentMode"}, | ||
| 2486 | {233, nullptr, "ClearPairingInfo"}, | ||
| 2487 | {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, | ||
| 2488 | {235, nullptr, "EnableAnalogStickPower"}, | ||
| 2489 | {236, nullptr, "RequestKuinaUartClockCal"}, | ||
| 2490 | {237, nullptr, "GetKuinaUartClockCal"}, | ||
| 2491 | {238, nullptr, "SetKuinaUartClockTrim"}, | ||
| 2492 | {239, nullptr, "KuinaLoopbackTest"}, | ||
| 2493 | {240, nullptr, "RequestBatteryVoltage"}, | ||
| 2494 | {241, nullptr, "GetBatteryVoltage"}, | ||
| 2495 | {242, nullptr, "GetUniquePadPowerInfo"}, | ||
| 2496 | {243, nullptr, "RebootUniquePad"}, | ||
| 2497 | {244, nullptr, "RequestKuinaFirmwareVersion"}, | ||
| 2498 | {245, nullptr, "GetKuinaFirmwareVersion"}, | ||
| 2499 | {246, nullptr, "GetVidPid"}, | ||
| 2500 | {247, nullptr, "GetAnalogStickCalibrationValue"}, | ||
| 2501 | {248, nullptr, "GetUniquePadIdsFull"}, | ||
| 2502 | {249, nullptr, "ConnectUniquePad"}, | ||
| 2503 | {250, nullptr, "IsVirtual"}, | ||
| 2504 | {251, nullptr, "GetAnalogStickModuleParam"}, | ||
| 2505 | {301, nullptr, "GetAbstractedPadHandles"}, | ||
| 2506 | {302, nullptr, "GetAbstractedPadState"}, | ||
| 2507 | {303, nullptr, "GetAbstractedPadsState"}, | ||
| 2508 | {321, nullptr, "SetAutoPilotVirtualPadState"}, | ||
| 2509 | {322, nullptr, "UnsetAutoPilotVirtualPadState"}, | ||
| 2510 | {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, | ||
| 2511 | {324, nullptr, "AttachHdlsWorkBuffer"}, | ||
| 2512 | {325, nullptr, "ReleaseHdlsWorkBuffer"}, | ||
| 2513 | {326, nullptr, "DumpHdlsNpadAssignmentState"}, | ||
| 2514 | {327, nullptr, "DumpHdlsStates"}, | ||
| 2515 | {328, nullptr, "ApplyHdlsNpadAssignmentState"}, | ||
| 2516 | {329, nullptr, "ApplyHdlsStateList"}, | ||
| 2517 | {330, nullptr, "AttachHdlsVirtualDevice"}, | ||
| 2518 | {331, nullptr, "DetachHdlsVirtualDevice"}, | ||
| 2519 | {332, nullptr, "SetHdlsState"}, | ||
| 2520 | {350, nullptr, "AddRegisteredDevice"}, | ||
| 2521 | {400, nullptr, "DisableExternalMcuOnNxDevice"}, | ||
| 2522 | {401, nullptr, "DisableRailDeviceFiltering"}, | ||
| 2523 | {402, nullptr, "EnableWiredPairing"}, | ||
| 2524 | {403, nullptr, "EnableShipmentModeAutoClear"}, | ||
| 2525 | {404, nullptr, "SetRailEnabled"}, | ||
| 2526 | {500, nullptr, "SetFactoryInt"}, | ||
| 2527 | {501, nullptr, "IsFactoryBootEnabled"}, | ||
| 2528 | {550, nullptr, "SetAnalogStickModelDataTemporarily"}, | ||
| 2529 | {551, nullptr, "GetAnalogStickModelData"}, | ||
| 2530 | {552, nullptr, "ResetAnalogStickModelData"}, | ||
| 2531 | {600, nullptr, "ConvertPadState"}, | ||
| 2532 | {650, nullptr, "AddButtonPlayData"}, | ||
| 2533 | {651, nullptr, "StartButtonPlayData"}, | ||
| 2534 | {652, nullptr, "StopButtonPlayData"}, | ||
| 2535 | {2000, nullptr, "DeactivateDigitizer"}, | ||
| 2536 | {2001, nullptr, "SetDigitizerAutoPilotState"}, | ||
| 2537 | {2002, nullptr, "UnsetDigitizerAutoPilotState"}, | ||
| 2538 | {2002, nullptr, "ReloadFirmwareDebugSettings"}, | ||
| 2539 | }; | ||
| 2540 | // clang-format on | ||
| 2541 | |||
| 2542 | RegisterHandlers(functions); | ||
| 2543 | } | ||
| 2544 | }; | ||
| 2545 | |||
| 2546 | class HidSys final : public ServiceFramework<HidSys> { | ||
| 2547 | public: | ||
| 2548 | explicit HidSys(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_) | ||
| 2549 | : ServiceFramework{system_, "hid:sys"}, service_context{system_, "hid:sys"}, | ||
| 2550 | applet_resource{applet_resource_} { | ||
| 2551 | // clang-format off | ||
| 2552 | static const FunctionInfo functions[] = { | ||
| 2553 | {31, nullptr, "SendKeyboardLockKeyEvent"}, | ||
| 2554 | {101, nullptr, "AcquireHomeButtonEventHandle"}, | ||
| 2555 | {111, nullptr, "ActivateHomeButton"}, | ||
| 2556 | {121, nullptr, "AcquireSleepButtonEventHandle"}, | ||
| 2557 | {131, nullptr, "ActivateSleepButton"}, | ||
| 2558 | {141, nullptr, "AcquireCaptureButtonEventHandle"}, | ||
| 2559 | {151, nullptr, "ActivateCaptureButton"}, | ||
| 2560 | {161, nullptr, "GetPlatformConfig"}, | ||
| 2561 | {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, | ||
| 2562 | {211, nullptr, "GetNpadsWithNfc"}, | ||
| 2563 | {212, nullptr, "AcquireNfcActivateEventHandle"}, | ||
| 2564 | {213, nullptr, "ActivateNfc"}, | ||
| 2565 | {214, nullptr, "GetXcdHandleForNpadWithNfc"}, | ||
| 2566 | {215, nullptr, "IsNfcActivated"}, | ||
| 2567 | {230, nullptr, "AcquireIrSensorEventHandle"}, | ||
| 2568 | {231, nullptr, "ActivateIrSensor"}, | ||
| 2569 | {232, nullptr, "GetIrSensorState"}, | ||
| 2570 | {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, | ||
| 2571 | {301, nullptr, "ActivateNpadSystem"}, | ||
| 2572 | {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, | ||
| 2573 | {304, nullptr, "EnableAssigningSingleOnSlSrPress"}, | ||
| 2574 | {305, nullptr, "DisableAssigningSingleOnSlSrPress"}, | ||
| 2575 | {306, &HidSys::GetLastActiveNpad, "GetLastActiveNpad"}, | ||
| 2576 | {307, nullptr, "GetNpadSystemExtStyle"}, | ||
| 2577 | {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, | ||
| 2578 | {309, nullptr, "GetNpadFullKeyGripColor"}, | ||
| 2579 | {310, nullptr, "GetMaskedSupportedNpadStyleSet"}, | ||
| 2580 | {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, | ||
| 2581 | {312, nullptr, "SetSupportedNpadStyleSetAll"}, | ||
| 2582 | {313, nullptr, "GetNpadCaptureButtonAssignment"}, | ||
| 2583 | {314, nullptr, "GetAppletFooterUiType"}, | ||
| 2584 | {315, nullptr, "GetAppletDetailedUiType"}, | ||
| 2585 | {316, nullptr, "GetNpadInterfaceType"}, | ||
| 2586 | {317, nullptr, "GetNpadLeftRightInterfaceType"}, | ||
| 2587 | {318, nullptr, "HasBattery"}, | ||
| 2588 | {319, nullptr, "HasLeftRightBattery"}, | ||
| 2589 | {321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"}, | ||
| 2590 | {322, nullptr, "GetIrSensorState"}, | ||
| 2591 | {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, | ||
| 2592 | {324, nullptr, "GetUniquePadButtonSet"}, | ||
| 2593 | {325, nullptr, "GetUniquePadColor"}, | ||
| 2594 | {326, nullptr, "GetUniquePadAppletDetailedUiType"}, | ||
| 2595 | {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, | ||
| 2596 | {328, nullptr, "AttachAbstractedPadToNpad"}, | ||
| 2597 | {329, nullptr, "DetachAbstractedPadAll"}, | ||
| 2598 | {330, nullptr, "CheckAbstractedPadConnection"}, | ||
| 2599 | {500, nullptr, "SetAppletResourceUserId"}, | ||
| 2600 | {501, nullptr, "RegisterAppletResourceUserId"}, | ||
| 2601 | {502, nullptr, "UnregisterAppletResourceUserId"}, | ||
| 2602 | {503, nullptr, "EnableAppletToGetInput"}, | ||
| 2603 | {504, nullptr, "SetAruidValidForVibration"}, | ||
| 2604 | {505, nullptr, "EnableAppletToGetSixAxisSensor"}, | ||
| 2605 | {506, nullptr, "EnableAppletToGetPadInput"}, | ||
| 2606 | {507, nullptr, "EnableAppletToGetTouchScreen"}, | ||
| 2607 | {510, nullptr, "SetVibrationMasterVolume"}, | ||
| 2608 | {511, nullptr, "GetVibrationMasterVolume"}, | ||
| 2609 | {512, nullptr, "BeginPermitVibrationSession"}, | ||
| 2610 | {513, nullptr, "EndPermitVibrationSession"}, | ||
| 2611 | {514, nullptr, "Unknown514"}, | ||
| 2612 | {520, nullptr, "EnableHandheldHids"}, | ||
| 2613 | {521, nullptr, "DisableHandheldHids"}, | ||
| 2614 | {522, nullptr, "SetJoyConRailEnabled"}, | ||
| 2615 | {523, nullptr, "IsJoyConRailEnabled"}, | ||
| 2616 | {524, nullptr, "IsHandheldHidsEnabled"}, | ||
| 2617 | {525, nullptr, "IsJoyConAttachedOnAllRail"}, | ||
| 2618 | {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, | ||
| 2619 | {541, nullptr, "GetPlayReportControllerUsages"}, | ||
| 2620 | {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, | ||
| 2621 | {543, nullptr, "GetRegisteredDevicesOld"}, | ||
| 2622 | {544, nullptr, "AcquireConnectionTriggerTimeoutEvent"}, | ||
| 2623 | {545, nullptr, "SendConnectionTrigger"}, | ||
| 2624 | {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, | ||
| 2625 | {547, nullptr, "GetAllowedBluetoothLinksCount"}, | ||
| 2626 | {548, nullptr, "GetRegisteredDevices"}, | ||
| 2627 | {549, nullptr, "GetConnectableRegisteredDevices"}, | ||
| 2628 | {700, nullptr, "ActivateUniquePad"}, | ||
| 2629 | {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, | ||
| 2630 | {703, nullptr, "GetUniquePadIds"}, | ||
| 2631 | {751, &HidSys::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"}, | ||
| 2632 | {800, nullptr, "ListSixAxisSensorHandles"}, | ||
| 2633 | {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, | ||
| 2634 | {802, nullptr, "ResetSixAxisSensorCalibrationValues"}, | ||
| 2635 | {803, nullptr, "StartSixAxisSensorUserCalibration"}, | ||
| 2636 | {804, nullptr, "CancelSixAxisSensorUserCalibration"}, | ||
| 2637 | {805, nullptr, "GetUniquePadBluetoothAddress"}, | ||
| 2638 | {806, nullptr, "DisconnectUniquePad"}, | ||
| 2639 | {807, nullptr, "GetUniquePadType"}, | ||
| 2640 | {808, nullptr, "GetUniquePadInterface"}, | ||
| 2641 | {809, nullptr, "GetUniquePadSerialNumber"}, | ||
| 2642 | {810, nullptr, "GetUniquePadControllerNumber"}, | ||
| 2643 | {811, nullptr, "GetSixAxisSensorUserCalibrationStage"}, | ||
| 2644 | {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"}, | ||
| 2645 | {821, nullptr, "StartAnalogStickManualCalibration"}, | ||
| 2646 | {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"}, | ||
| 2647 | {823, nullptr, "CancelAnalogStickManualCalibration"}, | ||
| 2648 | {824, nullptr, "ResetAnalogStickManualCalibration"}, | ||
| 2649 | {825, nullptr, "GetAnalogStickState"}, | ||
| 2650 | {826, nullptr, "GetAnalogStickManualCalibrationStage"}, | ||
| 2651 | {827, nullptr, "IsAnalogStickButtonPressed"}, | ||
| 2652 | {828, nullptr, "IsAnalogStickInReleasePosition"}, | ||
| 2653 | {829, nullptr, "IsAnalogStickInCircumference"}, | ||
| 2654 | {830, nullptr, "SetNotificationLedPattern"}, | ||
| 2655 | {831, nullptr, "SetNotificationLedPatternWithTimeout"}, | ||
| 2656 | {832, nullptr, "PrepareHidsForNotificationWake"}, | ||
| 2657 | {850, &HidSys::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, | ||
| 2658 | {851, nullptr, "EnableUsbFullKeyController"}, | ||
| 2659 | {852, nullptr, "IsUsbConnected"}, | ||
| 2660 | {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, | ||
| 2661 | {900, nullptr, "ActivateInputDetector"}, | ||
| 2662 | {901, nullptr, "NotifyInputDetector"}, | ||
| 2663 | {1000, nullptr, "InitializeFirmwareUpdate"}, | ||
| 2664 | {1001, nullptr, "GetFirmwareVersion"}, | ||
| 2665 | {1002, nullptr, "GetAvailableFirmwareVersion"}, | ||
| 2666 | {1003, nullptr, "IsFirmwareUpdateAvailable"}, | ||
| 2667 | {1004, nullptr, "CheckFirmwareUpdateRequired"}, | ||
| 2668 | {1005, nullptr, "StartFirmwareUpdate"}, | ||
| 2669 | {1006, nullptr, "AbortFirmwareUpdate"}, | ||
| 2670 | {1007, nullptr, "GetFirmwareUpdateState"}, | ||
| 2671 | {1008, nullptr, "ActivateAudioControl"}, | ||
| 2672 | {1009, nullptr, "AcquireAudioControlEventHandle"}, | ||
| 2673 | {1010, nullptr, "GetAudioControlStates"}, | ||
| 2674 | {1011, nullptr, "DeactivateAudioControl"}, | ||
| 2675 | {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"}, | ||
| 2676 | {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"}, | ||
| 2677 | {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, | ||
| 2678 | {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, | ||
| 2679 | {1100, nullptr, "GetHidbusSystemServiceObject"}, | ||
| 2680 | {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, | ||
| 2681 | {1130, nullptr, "InitializeUsbFirmwareUpdate"}, | ||
| 2682 | {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, | ||
| 2683 | {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, | ||
| 2684 | {1133, nullptr, "StartUsbFirmwareUpdate"}, | ||
| 2685 | {1134, nullptr, "GetUsbFirmwareUpdateState"}, | ||
| 2686 | {1150, nullptr, "SetTouchScreenMagnification"}, | ||
| 2687 | {1151, nullptr, "GetTouchScreenFirmwareVersion"}, | ||
| 2688 | {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, | ||
| 2689 | {1153, &HidSys::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, | ||
| 2690 | {1154, nullptr, "IsFirmwareAvailableForNotification"}, | ||
| 2691 | {1155, nullptr, "SetForceHandheldStyleVibration"}, | ||
| 2692 | {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, | ||
| 2693 | {1157, nullptr, "CancelConnectionTrigger"}, | ||
| 2694 | {1200, nullptr, "IsButtonConfigSupported"}, | ||
| 2695 | {1201, nullptr, "IsButtonConfigEmbeddedSupported"}, | ||
| 2696 | {1202, nullptr, "DeleteButtonConfig"}, | ||
| 2697 | {1203, nullptr, "DeleteButtonConfigEmbedded"}, | ||
| 2698 | {1204, nullptr, "SetButtonConfigEnabled"}, | ||
| 2699 | {1205, nullptr, "SetButtonConfigEmbeddedEnabled"}, | ||
| 2700 | {1206, nullptr, "IsButtonConfigEnabled"}, | ||
| 2701 | {1207, nullptr, "IsButtonConfigEmbeddedEnabled"}, | ||
| 2702 | {1208, nullptr, "SetButtonConfigEmbedded"}, | ||
| 2703 | {1209, nullptr, "SetButtonConfigFull"}, | ||
| 2704 | {1210, nullptr, "SetButtonConfigLeft"}, | ||
| 2705 | {1211, nullptr, "SetButtonConfigRight"}, | ||
| 2706 | {1212, nullptr, "GetButtonConfigEmbedded"}, | ||
| 2707 | {1213, nullptr, "GetButtonConfigFull"}, | ||
| 2708 | {1214, nullptr, "GetButtonConfigLeft"}, | ||
| 2709 | {1215, nullptr, "GetButtonConfigRight"}, | ||
| 2710 | {1250, nullptr, "IsCustomButtonConfigSupported"}, | ||
| 2711 | {1251, nullptr, "IsDefaultButtonConfigEmbedded"}, | ||
| 2712 | {1252, nullptr, "IsDefaultButtonConfigFull"}, | ||
| 2713 | {1253, nullptr, "IsDefaultButtonConfigLeft"}, | ||
| 2714 | {1254, nullptr, "IsDefaultButtonConfigRight"}, | ||
| 2715 | {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"}, | ||
| 2716 | {1256, nullptr, "IsButtonConfigStorageFullEmpty"}, | ||
| 2717 | {1257, nullptr, "IsButtonConfigStorageLeftEmpty"}, | ||
| 2718 | {1258, nullptr, "IsButtonConfigStorageRightEmpty"}, | ||
| 2719 | {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"}, | ||
| 2720 | {1260, nullptr, "GetButtonConfigStorageFullDeprecated"}, | ||
| 2721 | {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"}, | ||
| 2722 | {1262, nullptr, "GetButtonConfigStorageRightDeprecated"}, | ||
| 2723 | {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"}, | ||
| 2724 | {1264, nullptr, "SetButtonConfigStorageFullDeprecated"}, | ||
| 2725 | {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"}, | ||
| 2726 | {1266, nullptr, "SetButtonConfigStorageRightDeprecated"}, | ||
| 2727 | {1267, nullptr, "DeleteButtonConfigStorageEmbedded"}, | ||
| 2728 | {1268, nullptr, "DeleteButtonConfigStorageFull"}, | ||
| 2729 | {1269, nullptr, "DeleteButtonConfigStorageLeft"}, | ||
| 2730 | {1270, nullptr, "DeleteButtonConfigStorageRight"}, | ||
| 2731 | {1271, nullptr, "IsUsingCustomButtonConfig"}, | ||
| 2732 | {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, | ||
| 2733 | {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, | ||
| 2734 | {1274, nullptr, "SetDefaultButtonConfig"}, | ||
| 2735 | {1275, nullptr, "SetAllDefaultButtonConfig"}, | ||
| 2736 | {1276, nullptr, "SetHidButtonConfigEmbedded"}, | ||
| 2737 | {1277, nullptr, "SetHidButtonConfigFull"}, | ||
| 2738 | {1278, nullptr, "SetHidButtonConfigLeft"}, | ||
| 2739 | {1279, nullptr, "SetHidButtonConfigRight"}, | ||
| 2740 | {1280, nullptr, "GetHidButtonConfigEmbedded"}, | ||
| 2741 | {1281, nullptr, "GetHidButtonConfigFull"}, | ||
| 2742 | {1282, nullptr, "GetHidButtonConfigLeft"}, | ||
| 2743 | {1283, nullptr, "GetHidButtonConfigRight"}, | ||
| 2744 | {1284, nullptr, "GetButtonConfigStorageEmbedded"}, | ||
| 2745 | {1285, nullptr, "GetButtonConfigStorageFull"}, | ||
| 2746 | {1286, nullptr, "GetButtonConfigStorageLeft"}, | ||
| 2747 | {1287, nullptr, "GetButtonConfigStorageRight"}, | ||
| 2748 | {1288, nullptr, "SetButtonConfigStorageEmbedded"}, | ||
| 2749 | {1289, nullptr, "SetButtonConfigStorageFull"}, | ||
| 2750 | {1290, nullptr, "DeleteButtonConfigStorageRight"}, | ||
| 2751 | {1291, nullptr, "DeleteButtonConfigStorageRight"}, | ||
| 2752 | }; | ||
| 2753 | // clang-format on | ||
| 2754 | |||
| 2755 | RegisterHandlers(functions); | ||
| 2756 | |||
| 2757 | joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent"); | ||
| 2758 | } | ||
| 2759 | |||
| 2760 | ~HidSys() { | ||
| 2761 | service_context.CloseEvent(joy_detach_event); | ||
| 2762 | }; | ||
| 2763 | |||
| 2764 | private: | ||
| 2765 | void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { | ||
| 2766 | LOG_WARNING(Service_HID, "called"); | ||
| 2767 | |||
| 2768 | GetAppletResource() | ||
| 2769 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 2770 | .ApplyNpadSystemCommonPolicy(); | ||
| 2771 | |||
| 2772 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2773 | rb.Push(ResultSuccess); | ||
| 2774 | } | ||
| 2775 | |||
| 2776 | void GetLastActiveNpad(HLERequestContext& ctx) { | ||
| 2777 | LOG_DEBUG(Service_HID, "(STUBBED) called"); | ||
| 2778 | |||
| 2779 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 2780 | rb.Push(ResultSuccess); | ||
| 2781 | rb.PushEnum(system.HIDCore().GetLastActiveController()); | ||
| 2782 | } | ||
| 2783 | |||
| 2784 | void GetUniquePadsFromNpad(HLERequestContext& ctx) { | ||
| 2785 | IPC::RequestParser rp{ctx}; | ||
| 2786 | const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 2787 | |||
| 2788 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type); | ||
| 2789 | |||
| 2790 | const std::vector<Core::HID::UniquePadId> unique_pads{}; | ||
| 2791 | |||
| 2792 | ctx.WriteBuffer(unique_pads); | ||
| 2793 | |||
| 2794 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 2795 | rb.Push(ResultSuccess); | ||
| 2796 | rb.Push(static_cast<u32>(unique_pads.size())); | ||
| 2797 | } | ||
| 2798 | |||
| 2799 | void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) { | ||
| 2800 | LOG_INFO(Service_AM, "called"); | ||
| 2801 | |||
| 2802 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 2803 | rb.Push(ResultSuccess); | ||
| 2804 | rb.PushCopyObjects(joy_detach_event->GetReadableEvent()); | ||
| 2805 | } | ||
| 2806 | |||
| 2807 | void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { | ||
| 2808 | const bool is_enabled = false; | ||
| 2809 | |||
| 2810 | LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled); | ||
| 2811 | |||
| 2812 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 2813 | rb.Push(ResultSuccess); | ||
| 2814 | rb.Push(is_enabled); | ||
| 2815 | } | ||
| 2816 | |||
| 2817 | void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { | ||
| 2818 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2819 | |||
| 2820 | Core::HID::TouchScreenConfigurationForNx touchscreen_config{ | ||
| 2821 | .mode = Core::HID::TouchScreenModeForNx::Finger, | ||
| 2822 | }; | ||
| 2823 | |||
| 2824 | if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && | ||
| 2825 | touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { | ||
| 2826 | touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; | ||
| 2827 | } | ||
| 2828 | |||
| 2829 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 2830 | rb.Push(ResultSuccess); | ||
| 2831 | rb.PushRaw(touchscreen_config); | ||
| 2832 | } | ||
| 2833 | |||
| 2834 | std::shared_ptr<IAppletResource> GetAppletResource() { | ||
| 2835 | if (applet_resource == nullptr) { | ||
| 2836 | applet_resource = std::make_shared<IAppletResource>(system, service_context); | ||
| 2837 | } | ||
| 2838 | |||
| 2839 | return applet_resource; | ||
| 2840 | } | ||
| 2841 | |||
| 2842 | Kernel::KEvent* joy_detach_event; | ||
| 2843 | KernelHelpers::ServiceContext service_context; | ||
| 2844 | std::shared_ptr<IAppletResource> applet_resource; | ||
| 2845 | }; | ||
| 2846 | |||
| 2847 | void LoopProcess(Core::System& system) { | 17 | void LoopProcess(Core::System& system) { |
| 2848 | auto server_manager = std::make_unique<ServerManager>(system); | 18 | auto server_manager = std::make_unique<ServerManager>(system); |
| 2849 | std::shared_ptr<IAppletResource> applet_resource; | 19 | std::shared_ptr<ResourceManager> resouce_manager = std::make_shared<ResourceManager>(system); |
| 20 | std::shared_ptr<HidFirmwareSettings> firmware_settings = | ||
| 21 | std::make_shared<HidFirmwareSettings>(); | ||
| 22 | |||
| 23 | server_manager->RegisterNamedService( | ||
| 24 | "hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings)); | ||
| 25 | server_manager->RegisterNamedService( | ||
| 26 | "hid:dbg", std::make_shared<IHidDebugServer>(system, resouce_manager)); | ||
| 27 | server_manager->RegisterNamedService( | ||
| 28 | "hid:sys", std::make_shared<IHidSystemServer>(system, resouce_manager)); | ||
| 2850 | 29 | ||
| 2851 | server_manager->RegisterNamedService("hid", std::make_shared<Hid>(system, applet_resource)); | ||
| 2852 | server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); | 30 | server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); |
| 2853 | server_manager->RegisterNamedService("hid:dbg", std::make_shared<HidDbg>(system)); | ||
| 2854 | server_manager->RegisterNamedService("hid:sys", | ||
| 2855 | std::make_shared<HidSys>(system, applet_resource)); | ||
| 2856 | 31 | ||
| 2857 | server_manager->RegisterNamedService("irs", std::make_shared<Service::IRS::IRS>(system)); | 32 | server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system)); |
| 2858 | server_manager->RegisterNamedService("irs:sys", | 33 | server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system)); |
| 2859 | std::make_shared<Service::IRS::IRS_SYS>(system)); | ||
| 2860 | 34 | ||
| 2861 | server_manager->RegisterNamedService("xcd:sys", std::make_shared<XCD_SYS>(system)); | 35 | server_manager->RegisterNamedService("xcd:sys", std::make_shared<XCD_SYS>(system)); |
| 36 | |||
| 2862 | system.RunServer(std::move(server_manager)); | 37 | system.RunServer(std::move(server_manager)); |
| 2863 | } | 38 | } |
| 2864 | 39 | ||
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 0ca43de93..ec5463f4e 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -3,220 +3,12 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <chrono> | 6 | namespace Core { |
| 7 | 7 | class System; | |
| 8 | #include "core/hle/service/hid/controllers/controller_base.h" | ||
| 9 | #include "core/hle/service/kernel_helpers.h" | ||
| 10 | #include "core/hle/service/service.h" | ||
| 11 | |||
| 12 | namespace Core::Timing { | ||
| 13 | struct EventType; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Service::SM { | ||
| 17 | class ServiceManager; | ||
| 18 | } | 8 | } |
| 19 | 9 | ||
| 20 | namespace Service::HID { | 10 | namespace Service::HID { |
| 21 | 11 | ||
| 22 | enum class HidController : std::size_t { | ||
| 23 | DebugPad, | ||
| 24 | Touchscreen, | ||
| 25 | Mouse, | ||
| 26 | Keyboard, | ||
| 27 | XPad, | ||
| 28 | HomeButton, | ||
| 29 | SleepButton, | ||
| 30 | CaptureButton, | ||
| 31 | InputDetector, | ||
| 32 | UniquePad, | ||
| 33 | NPad, | ||
| 34 | Gesture, | ||
| 35 | ConsoleSixAxisSensor, | ||
| 36 | DebugMouse, | ||
| 37 | Palma, | ||
| 38 | |||
| 39 | MaxControllers, | ||
| 40 | }; | ||
| 41 | |||
| 42 | class IAppletResource final : public ServiceFramework<IAppletResource> { | ||
| 43 | public: | ||
| 44 | explicit IAppletResource(Core::System& system_, | ||
| 45 | KernelHelpers::ServiceContext& service_context_); | ||
| 46 | ~IAppletResource() override; | ||
| 47 | |||
| 48 | void ActivateController(HidController controller); | ||
| 49 | void DeactivateController(HidController controller); | ||
| 50 | |||
| 51 | template <typename T> | ||
| 52 | T& GetController(HidController controller) { | ||
| 53 | return static_cast<T&>(*controllers[static_cast<size_t>(controller)]); | ||
| 54 | } | ||
| 55 | |||
| 56 | template <typename T> | ||
| 57 | const T& GetController(HidController controller) const { | ||
| 58 | return static_cast<T&>(*controllers[static_cast<size_t>(controller)]); | ||
| 59 | } | ||
| 60 | |||
| 61 | private: | ||
| 62 | template <typename T> | ||
| 63 | void MakeController(HidController controller, u8* shared_memory) { | ||
| 64 | if constexpr (std::is_constructible_v<T, Core::System&, u8*>) { | ||
| 65 | controllers[static_cast<std::size_t>(controller)] = | ||
| 66 | std::make_unique<T>(system, shared_memory); | ||
| 67 | } else { | ||
| 68 | controllers[static_cast<std::size_t>(controller)] = | ||
| 69 | std::make_unique<T>(system.HIDCore(), shared_memory); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | template <typename T> | ||
| 74 | void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) { | ||
| 75 | controllers[static_cast<std::size_t>(controller)] = | ||
| 76 | std::make_unique<T>(system.HIDCore(), shared_memory, service_context); | ||
| 77 | } | ||
| 78 | |||
| 79 | void GetSharedMemoryHandle(HLERequestContext& ctx); | ||
| 80 | void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 81 | void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 82 | void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 83 | void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 84 | |||
| 85 | KernelHelpers::ServiceContext& service_context; | ||
| 86 | |||
| 87 | std::shared_ptr<Core::Timing::EventType> npad_update_event; | ||
| 88 | std::shared_ptr<Core::Timing::EventType> default_update_event; | ||
| 89 | std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; | ||
| 90 | std::shared_ptr<Core::Timing::EventType> motion_update_event; | ||
| 91 | |||
| 92 | std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> | ||
| 93 | controllers{}; | ||
| 94 | }; | ||
| 95 | |||
| 96 | class Hid final : public ServiceFramework<Hid> { | ||
| 97 | public: | ||
| 98 | explicit Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_); | ||
| 99 | ~Hid() override; | ||
| 100 | |||
| 101 | std::shared_ptr<IAppletResource> GetAppletResource(); | ||
| 102 | |||
| 103 | private: | ||
| 104 | void CreateAppletResource(HLERequestContext& ctx); | ||
| 105 | void ActivateDebugPad(HLERequestContext& ctx); | ||
| 106 | void ActivateTouchScreen(HLERequestContext& ctx); | ||
| 107 | void ActivateMouse(HLERequestContext& ctx); | ||
| 108 | void ActivateKeyboard(HLERequestContext& ctx); | ||
| 109 | void SendKeyboardLockKeyEvent(HLERequestContext& ctx); | ||
| 110 | void ActivateXpad(HLERequestContext& ctx); | ||
| 111 | void GetXpadIDs(HLERequestContext& ctx); | ||
| 112 | void ActivateSixAxisSensor(HLERequestContext& ctx); | ||
| 113 | void DeactivateSixAxisSensor(HLERequestContext& ctx); | ||
| 114 | void StartSixAxisSensor(HLERequestContext& ctx); | ||
| 115 | void StopSixAxisSensor(HLERequestContext& ctx); | ||
| 116 | void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx); | ||
| 117 | void EnableSixAxisSensorFusion(HLERequestContext& ctx); | ||
| 118 | void SetSixAxisSensorFusionParameters(HLERequestContext& ctx); | ||
| 119 | void GetSixAxisSensorFusionParameters(HLERequestContext& ctx); | ||
| 120 | void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx); | ||
| 121 | void SetGyroscopeZeroDriftMode(HLERequestContext& ctx); | ||
| 122 | void GetGyroscopeZeroDriftMode(HLERequestContext& ctx); | ||
| 123 | void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx); | ||
| 124 | void IsSixAxisSensorAtRest(HLERequestContext& ctx); | ||
| 125 | void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx); | ||
| 126 | void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx); | ||
| 127 | void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx); | ||
| 128 | void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx); | ||
| 129 | void GetSixAxisSensorIcInformation(HLERequestContext& ctx); | ||
| 130 | void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx); | ||
| 131 | void ActivateGesture(HLERequestContext& ctx); | ||
| 132 | void SetSupportedNpadStyleSet(HLERequestContext& ctx); | ||
| 133 | void GetSupportedNpadStyleSet(HLERequestContext& ctx); | ||
| 134 | void SetSupportedNpadIdType(HLERequestContext& ctx); | ||
| 135 | void ActivateNpad(HLERequestContext& ctx); | ||
| 136 | void DeactivateNpad(HLERequestContext& ctx); | ||
| 137 | void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx); | ||
| 138 | void DisconnectNpad(HLERequestContext& ctx); | ||
| 139 | void GetPlayerLedPattern(HLERequestContext& ctx); | ||
| 140 | void ActivateNpadWithRevision(HLERequestContext& ctx); | ||
| 141 | void SetNpadJoyHoldType(HLERequestContext& ctx); | ||
| 142 | void GetNpadJoyHoldType(HLERequestContext& ctx); | ||
| 143 | void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx); | ||
| 144 | void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx); | ||
| 145 | void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx); | ||
| 146 | void MergeSingleJoyAsDualJoy(HLERequestContext& ctx); | ||
| 147 | void StartLrAssignmentMode(HLERequestContext& ctx); | ||
| 148 | void StopLrAssignmentMode(HLERequestContext& ctx); | ||
| 149 | void SetNpadHandheldActivationMode(HLERequestContext& ctx); | ||
| 150 | void GetNpadHandheldActivationMode(HLERequestContext& ctx); | ||
| 151 | void SwapNpadAssignment(HLERequestContext& ctx); | ||
| 152 | void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx); | ||
| 153 | void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx); | ||
| 154 | void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx); | ||
| 155 | void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx); | ||
| 156 | void SetNpadCaptureButtonAssignment(HLERequestContext& ctx); | ||
| 157 | void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx); | ||
| 158 | void GetVibrationDeviceInfo(HLERequestContext& ctx); | ||
| 159 | void SendVibrationValue(HLERequestContext& ctx); | ||
| 160 | void GetActualVibrationValue(HLERequestContext& ctx); | ||
| 161 | void CreateActiveVibrationDeviceList(HLERequestContext& ctx); | ||
| 162 | void PermitVibration(HLERequestContext& ctx); | ||
| 163 | void IsVibrationPermitted(HLERequestContext& ctx); | ||
| 164 | void SendVibrationValues(HLERequestContext& ctx); | ||
| 165 | void SendVibrationGcErmCommand(HLERequestContext& ctx); | ||
| 166 | void GetActualVibrationGcErmCommand(HLERequestContext& ctx); | ||
| 167 | void BeginPermitVibrationSession(HLERequestContext& ctx); | ||
| 168 | void EndPermitVibrationSession(HLERequestContext& ctx); | ||
| 169 | void IsVibrationDeviceMounted(HLERequestContext& ctx); | ||
| 170 | void ActivateConsoleSixAxisSensor(HLERequestContext& ctx); | ||
| 171 | void StartConsoleSixAxisSensor(HLERequestContext& ctx); | ||
| 172 | void StopConsoleSixAxisSensor(HLERequestContext& ctx); | ||
| 173 | void ActivateSevenSixAxisSensor(HLERequestContext& ctx); | ||
| 174 | void StartSevenSixAxisSensor(HLERequestContext& ctx); | ||
| 175 | void StopSevenSixAxisSensor(HLERequestContext& ctx); | ||
| 176 | void InitializeSevenSixAxisSensor(HLERequestContext& ctx); | ||
| 177 | void FinalizeSevenSixAxisSensor(HLERequestContext& ctx); | ||
| 178 | void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx); | ||
| 179 | void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); | ||
| 180 | void GetPalmaConnectionHandle(HLERequestContext& ctx); | ||
| 181 | void InitializePalma(HLERequestContext& ctx); | ||
| 182 | void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx); | ||
| 183 | void GetPalmaOperationInfo(HLERequestContext& ctx); | ||
| 184 | void PlayPalmaActivity(HLERequestContext& ctx); | ||
| 185 | void SetPalmaFrModeType(HLERequestContext& ctx); | ||
| 186 | void ReadPalmaStep(HLERequestContext& ctx); | ||
| 187 | void EnablePalmaStep(HLERequestContext& ctx); | ||
| 188 | void ResetPalmaStep(HLERequestContext& ctx); | ||
| 189 | void ReadPalmaApplicationSection(HLERequestContext& ctx); | ||
| 190 | void WritePalmaApplicationSection(HLERequestContext& ctx); | ||
| 191 | void ReadPalmaUniqueCode(HLERequestContext& ctx); | ||
| 192 | void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx); | ||
| 193 | void WritePalmaActivityEntry(HLERequestContext& ctx); | ||
| 194 | void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx); | ||
| 195 | void WritePalmaWaveEntry(HLERequestContext& ctx); | ||
| 196 | void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); | ||
| 197 | void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); | ||
| 198 | void SuspendPalmaFeature(HLERequestContext& ctx); | ||
| 199 | void GetPalmaOperationResult(HLERequestContext& ctx); | ||
| 200 | void ReadPalmaPlayLog(HLERequestContext& ctx); | ||
| 201 | void ResetPalmaPlayLog(HLERequestContext& ctx); | ||
| 202 | void SetIsPalmaAllConnectable(HLERequestContext& ctx); | ||
| 203 | void SetIsPalmaPairedConnectable(HLERequestContext& ctx); | ||
| 204 | void PairPalma(HLERequestContext& ctx); | ||
| 205 | void SetPalmaBoostMode(HLERequestContext& ctx); | ||
| 206 | void CancelWritePalmaWaveEntry(HLERequestContext& ctx); | ||
| 207 | void EnablePalmaBoostMode(HLERequestContext& ctx); | ||
| 208 | void GetPalmaBluetoothAddress(HLERequestContext& ctx); | ||
| 209 | void SetDisallowedPalmaConnection(HLERequestContext& ctx); | ||
| 210 | void SetNpadCommunicationMode(HLERequestContext& ctx); | ||
| 211 | void GetNpadCommunicationMode(HLERequestContext& ctx); | ||
| 212 | void SetTouchScreenConfiguration(HLERequestContext& ctx); | ||
| 213 | void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx); | ||
| 214 | |||
| 215 | std::shared_ptr<IAppletResource> applet_resource; | ||
| 216 | |||
| 217 | KernelHelpers::ServiceContext service_context; | ||
| 218 | }; | ||
| 219 | |||
| 220 | void LoopProcess(Core::System& system); | 12 | void LoopProcess(Core::System& system); |
| 221 | 13 | ||
| 222 | } // namespace Service::HID | 14 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp new file mode 100644 index 000000000..6294f3dfb --- /dev/null +++ b/src/core/hle/service/hid/hid_debug_server.cpp | |||
| @@ -0,0 +1,159 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/hid/hid_debug_server.h" | ||
| 5 | #include "core/hle/service/hid/resource_manager.h" | ||
| 6 | #include "core/hle/service/ipc_helpers.h" | ||
| 7 | |||
| 8 | namespace Service::HID { | ||
| 9 | |||
| 10 | IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource) | ||
| 11 | : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} { | ||
| 12 | // clang-format off | ||
| 13 | static const FunctionInfo functions[] = { | ||
| 14 | {0, nullptr, "DeactivateDebugPad"}, | ||
| 15 | {1, nullptr, "SetDebugPadAutoPilotState"}, | ||
| 16 | {2, nullptr, "UnsetDebugPadAutoPilotState"}, | ||
| 17 | {10, nullptr, "DeactivateTouchScreen"}, | ||
| 18 | {11, nullptr, "SetTouchScreenAutoPilotState"}, | ||
| 19 | {12, nullptr, "UnsetTouchScreenAutoPilotState"}, | ||
| 20 | {13, nullptr, "GetTouchScreenConfiguration"}, | ||
| 21 | {14, nullptr, "ProcessTouchScreenAutoTune"}, | ||
| 22 | {15, nullptr, "ForceStopTouchScreenManagement"}, | ||
| 23 | {16, nullptr, "ForceRestartTouchScreenManagement"}, | ||
| 24 | {17, nullptr, "IsTouchScreenManaged"}, | ||
| 25 | {20, nullptr, "DeactivateMouse"}, | ||
| 26 | {21, nullptr, "SetMouseAutoPilotState"}, | ||
| 27 | {22, nullptr, "UnsetMouseAutoPilotState"}, | ||
| 28 | {25, nullptr, "SetDebugMouseAutoPilotState"}, | ||
| 29 | {26, nullptr, "UnsetDebugMouseAutoPilotState"}, | ||
| 30 | {30, nullptr, "DeactivateKeyboard"}, | ||
| 31 | {31, nullptr, "SetKeyboardAutoPilotState"}, | ||
| 32 | {32, nullptr, "UnsetKeyboardAutoPilotState"}, | ||
| 33 | {50, nullptr, "DeactivateXpad"}, | ||
| 34 | {51, nullptr, "SetXpadAutoPilotState"}, | ||
| 35 | {52, nullptr, "UnsetXpadAutoPilotState"}, | ||
| 36 | {53, nullptr, "DeactivateJoyXpad"}, | ||
| 37 | {60, nullptr, "ClearNpadSystemCommonPolicy"}, | ||
| 38 | {61, nullptr, "DeactivateNpad"}, | ||
| 39 | {62, nullptr, "ForceDisconnectNpad"}, | ||
| 40 | {91, nullptr, "DeactivateGesture"}, | ||
| 41 | {110, nullptr, "DeactivateHomeButton"}, | ||
| 42 | {111, nullptr, "SetHomeButtonAutoPilotState"}, | ||
| 43 | {112, nullptr, "UnsetHomeButtonAutoPilotState"}, | ||
| 44 | {120, nullptr, "DeactivateSleepButton"}, | ||
| 45 | {121, nullptr, "SetSleepButtonAutoPilotState"}, | ||
| 46 | {122, nullptr, "UnsetSleepButtonAutoPilotState"}, | ||
| 47 | {123, nullptr, "DeactivateInputDetector"}, | ||
| 48 | {130, nullptr, "DeactivateCaptureButton"}, | ||
| 49 | {131, nullptr, "SetCaptureButtonAutoPilotState"}, | ||
| 50 | {132, nullptr, "UnsetCaptureButtonAutoPilotState"}, | ||
| 51 | {133, nullptr, "SetShiftAccelerometerCalibrationValue"}, | ||
| 52 | {134, nullptr, "GetShiftAccelerometerCalibrationValue"}, | ||
| 53 | {135, nullptr, "SetShiftGyroscopeCalibrationValue"}, | ||
| 54 | {136, nullptr, "GetShiftGyroscopeCalibrationValue"}, | ||
| 55 | {140, nullptr, "DeactivateConsoleSixAxisSensor"}, | ||
| 56 | {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, | ||
| 57 | {142, nullptr, "DeactivateSevenSixAxisSensor"}, | ||
| 58 | {143, nullptr, "GetConsoleSixAxisSensorCountStates"}, | ||
| 59 | {144, nullptr, "GetAccelerometerFsr"}, | ||
| 60 | {145, nullptr, "SetAccelerometerFsr"}, | ||
| 61 | {146, nullptr, "GetAccelerometerOdr"}, | ||
| 62 | {147, nullptr, "SetAccelerometerOdr"}, | ||
| 63 | {148, nullptr, "GetGyroscopeFsr"}, | ||
| 64 | {149, nullptr, "SetGyroscopeFsr"}, | ||
| 65 | {150, nullptr, "GetGyroscopeOdr"}, | ||
| 66 | {151, nullptr, "SetGyroscopeOdr"}, | ||
| 67 | {152, nullptr, "GetWhoAmI"}, | ||
| 68 | {201, nullptr, "ActivateFirmwareUpdate"}, | ||
| 69 | {202, nullptr, "DeactivateFirmwareUpdate"}, | ||
| 70 | {203, nullptr, "StartFirmwareUpdate"}, | ||
| 71 | {204, nullptr, "GetFirmwareUpdateStage"}, | ||
| 72 | {205, nullptr, "GetFirmwareVersion"}, | ||
| 73 | {206, nullptr, "GetDestinationFirmwareVersion"}, | ||
| 74 | {207, nullptr, "DiscardFirmwareInfoCacheForRevert"}, | ||
| 75 | {208, nullptr, "StartFirmwareUpdateForRevert"}, | ||
| 76 | {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, | ||
| 77 | {210, nullptr, "IsFirmwareUpdatingDevice"}, | ||
| 78 | {211, nullptr, "StartFirmwareUpdateIndividual"}, | ||
| 79 | {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"}, | ||
| 80 | {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"}, | ||
| 81 | {221, nullptr, "UpdateControllerColor"}, | ||
| 82 | {222, nullptr, "ConnectUsbPadsAsync"}, | ||
| 83 | {223, nullptr, "DisconnectUsbPadsAsync"}, | ||
| 84 | {224, nullptr, "UpdateDesignInfo"}, | ||
| 85 | {225, nullptr, "GetUniquePadDriverState"}, | ||
| 86 | {226, nullptr, "GetSixAxisSensorDriverStates"}, | ||
| 87 | {227, nullptr, "GetRxPacketHistory"}, | ||
| 88 | {228, nullptr, "AcquireOperationEventHandle"}, | ||
| 89 | {229, nullptr, "ReadSerialFlash"}, | ||
| 90 | {230, nullptr, "WriteSerialFlash"}, | ||
| 91 | {231, nullptr, "GetOperationResult"}, | ||
| 92 | {232, nullptr, "EnableShipmentMode"}, | ||
| 93 | {233, nullptr, "ClearPairingInfo"}, | ||
| 94 | {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, | ||
| 95 | {235, nullptr, "EnableAnalogStickPower"}, | ||
| 96 | {236, nullptr, "RequestKuinaUartClockCal"}, | ||
| 97 | {237, nullptr, "GetKuinaUartClockCal"}, | ||
| 98 | {238, nullptr, "SetKuinaUartClockTrim"}, | ||
| 99 | {239, nullptr, "KuinaLoopbackTest"}, | ||
| 100 | {240, nullptr, "RequestBatteryVoltage"}, | ||
| 101 | {241, nullptr, "GetBatteryVoltage"}, | ||
| 102 | {242, nullptr, "GetUniquePadPowerInfo"}, | ||
| 103 | {243, nullptr, "RebootUniquePad"}, | ||
| 104 | {244, nullptr, "RequestKuinaFirmwareVersion"}, | ||
| 105 | {245, nullptr, "GetKuinaFirmwareVersion"}, | ||
| 106 | {246, nullptr, "GetVidPid"}, | ||
| 107 | {247, nullptr, "GetAnalogStickCalibrationValue"}, | ||
| 108 | {248, nullptr, "GetUniquePadIdsFull"}, | ||
| 109 | {249, nullptr, "ConnectUniquePad"}, | ||
| 110 | {250, nullptr, "IsVirtual"}, | ||
| 111 | {251, nullptr, "GetAnalogStickModuleParam"}, | ||
| 112 | {301, nullptr, "GetAbstractedPadHandles"}, | ||
| 113 | {302, nullptr, "GetAbstractedPadState"}, | ||
| 114 | {303, nullptr, "GetAbstractedPadsState"}, | ||
| 115 | {321, nullptr, "SetAutoPilotVirtualPadState"}, | ||
| 116 | {322, nullptr, "UnsetAutoPilotVirtualPadState"}, | ||
| 117 | {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, | ||
| 118 | {324, nullptr, "AttachHdlsWorkBuffer"}, | ||
| 119 | {325, nullptr, "ReleaseHdlsWorkBuffer"}, | ||
| 120 | {326, nullptr, "DumpHdlsNpadAssignmentState"}, | ||
| 121 | {327, nullptr, "DumpHdlsStates"}, | ||
| 122 | {328, nullptr, "ApplyHdlsNpadAssignmentState"}, | ||
| 123 | {329, nullptr, "ApplyHdlsStateList"}, | ||
| 124 | {330, nullptr, "AttachHdlsVirtualDevice"}, | ||
| 125 | {331, nullptr, "DetachHdlsVirtualDevice"}, | ||
| 126 | {332, nullptr, "SetHdlsState"}, | ||
| 127 | {350, nullptr, "AddRegisteredDevice"}, | ||
| 128 | {400, nullptr, "DisableExternalMcuOnNxDevice"}, | ||
| 129 | {401, nullptr, "DisableRailDeviceFiltering"}, | ||
| 130 | {402, nullptr, "EnableWiredPairing"}, | ||
| 131 | {403, nullptr, "EnableShipmentModeAutoClear"}, | ||
| 132 | {404, nullptr, "SetRailEnabled"}, | ||
| 133 | {500, nullptr, "SetFactoryInt"}, | ||
| 134 | {501, nullptr, "IsFactoryBootEnabled"}, | ||
| 135 | {550, nullptr, "SetAnalogStickModelDataTemporarily"}, | ||
| 136 | {551, nullptr, "GetAnalogStickModelData"}, | ||
| 137 | {552, nullptr, "ResetAnalogStickModelData"}, | ||
| 138 | {600, nullptr, "ConvertPadState"}, | ||
| 139 | {650, nullptr, "AddButtonPlayData"}, | ||
| 140 | {651, nullptr, "StartButtonPlayData"}, | ||
| 141 | {652, nullptr, "StopButtonPlayData"}, | ||
| 142 | {2000, nullptr, "DeactivateDigitizer"}, | ||
| 143 | {2001, nullptr, "SetDigitizerAutoPilotState"}, | ||
| 144 | {2002, nullptr, "UnsetDigitizerAutoPilotState"}, | ||
| 145 | {2002, nullptr, "ReloadFirmwareDebugSettings"}, | ||
| 146 | }; | ||
| 147 | // clang-format on | ||
| 148 | |||
| 149 | RegisterHandlers(functions); | ||
| 150 | } | ||
| 151 | |||
| 152 | IHidDebugServer::~IHidDebugServer() = default; | ||
| 153 | |||
| 154 | std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() { | ||
| 155 | resource_manager->Initialize(); | ||
| 156 | return resource_manager; | ||
| 157 | } | ||
| 158 | |||
| 159 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/hid_debug_server.h b/src/core/hle/service/hid/hid_debug_server.h new file mode 100644 index 000000000..406db2211 --- /dev/null +++ b/src/core/hle/service/hid/hid_debug_server.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/service.h" | ||
| 7 | |||
| 8 | namespace Core { | ||
| 9 | class System; | ||
| 10 | } | ||
| 11 | |||
| 12 | namespace Service::HID { | ||
| 13 | class ResourceManager; | ||
| 14 | |||
| 15 | class IHidDebugServer final : public ServiceFramework<IHidDebugServer> { | ||
| 16 | public: | ||
| 17 | explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource); | ||
| 18 | ~IHidDebugServer() override; | ||
| 19 | |||
| 20 | private: | ||
| 21 | std::shared_ptr<ResourceManager> GetResourceManager(); | ||
| 22 | |||
| 23 | std::shared_ptr<ResourceManager> resource_manager; | ||
| 24 | }; | ||
| 25 | |||
| 26 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/hid_firmware_settings.cpp b/src/core/hle/service/hid/hid_firmware_settings.cpp new file mode 100644 index 000000000..59bd6825c --- /dev/null +++ b/src/core/hle/service/hid/hid_firmware_settings.cpp | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/hid/hid_firmware_settings.h" | ||
| 5 | |||
| 6 | namespace Service::HID { | ||
| 7 | |||
| 8 | HidFirmwareSettings::HidFirmwareSettings() { | ||
| 9 | LoadSettings(true); | ||
| 10 | } | ||
| 11 | |||
| 12 | void HidFirmwareSettings::Reload() { | ||
| 13 | LoadSettings(true); | ||
| 14 | } | ||
| 15 | |||
| 16 | void HidFirmwareSettings::LoadSettings(bool reload_config) { | ||
| 17 | if (is_initalized && !reload_config) { | ||
| 18 | return; | ||
| 19 | } | ||
| 20 | |||
| 21 | // TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values | ||
| 22 | |||
| 23 | is_debug_pad_enabled = true; | ||
| 24 | is_device_managed = true; | ||
| 25 | is_touch_i2c_managed = is_device_managed; | ||
| 26 | is_future_devices_emulated = false; | ||
| 27 | is_mcu_hardware_error_emulated = false; | ||
| 28 | is_rail_enabled = true; | ||
| 29 | is_firmware_update_failure_emulated = false; | ||
| 30 | is_firmware_update_failure = {}; | ||
| 31 | is_ble_disabled = false; | ||
| 32 | is_dscale_disabled = false; | ||
| 33 | is_handheld_forced = true; | ||
| 34 | features_per_id_disabled = {}; | ||
| 35 | is_touch_firmware_auto_update_disabled = false; | ||
| 36 | is_initalized = true; | ||
| 37 | } | ||
| 38 | |||
| 39 | bool HidFirmwareSettings::IsDebugPadEnabled() { | ||
| 40 | LoadSettings(false); | ||
| 41 | return is_debug_pad_enabled; | ||
| 42 | } | ||
| 43 | |||
| 44 | bool HidFirmwareSettings::IsDeviceManaged() { | ||
| 45 | LoadSettings(false); | ||
| 46 | return is_device_managed; | ||
| 47 | } | ||
| 48 | |||
| 49 | bool HidFirmwareSettings::IsEmulateFutureDevice() { | ||
| 50 | LoadSettings(false); | ||
| 51 | return is_future_devices_emulated; | ||
| 52 | } | ||
| 53 | |||
| 54 | bool HidFirmwareSettings::IsTouchI2cManaged() { | ||
| 55 | LoadSettings(false); | ||
| 56 | return is_touch_i2c_managed; | ||
| 57 | } | ||
| 58 | |||
| 59 | bool HidFirmwareSettings::IsHandheldForced() { | ||
| 60 | LoadSettings(false); | ||
| 61 | return is_handheld_forced; | ||
| 62 | } | ||
| 63 | |||
| 64 | bool HidFirmwareSettings::IsRailEnabled() { | ||
| 65 | LoadSettings(false); | ||
| 66 | return is_rail_enabled; | ||
| 67 | } | ||
| 68 | |||
| 69 | bool HidFirmwareSettings::IsHardwareErrorEmulated() { | ||
| 70 | LoadSettings(false); | ||
| 71 | return is_mcu_hardware_error_emulated; | ||
| 72 | } | ||
| 73 | |||
| 74 | bool HidFirmwareSettings::IsBleDisabled() { | ||
| 75 | LoadSettings(false); | ||
| 76 | return is_ble_disabled; | ||
| 77 | } | ||
| 78 | |||
| 79 | bool HidFirmwareSettings::IsDscaleDisabled() { | ||
| 80 | LoadSettings(false); | ||
| 81 | return is_dscale_disabled; | ||
| 82 | } | ||
| 83 | |||
| 84 | bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() { | ||
| 85 | LoadSettings(false); | ||
| 86 | return is_touch_firmware_auto_update_disabled; | ||
| 87 | } | ||
| 88 | |||
| 89 | HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() { | ||
| 90 | LoadSettings(false); | ||
| 91 | return is_firmware_update_failure; | ||
| 92 | } | ||
| 93 | |||
| 94 | HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() { | ||
| 95 | LoadSettings(false); | ||
| 96 | return features_per_id_disabled; | ||
| 97 | } | ||
| 98 | |||
| 99 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/hid_firmware_settings.h b/src/core/hle/service/hid/hid_firmware_settings.h new file mode 100644 index 000000000..6c10c440b --- /dev/null +++ b/src/core/hle/service/hid/hid_firmware_settings.h | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | |||
| 8 | namespace Service::HID { | ||
| 9 | |||
| 10 | /// Loads firmware config from nn::settings::fwdbg | ||
| 11 | class HidFirmwareSettings { | ||
| 12 | public: | ||
| 13 | using FirmwareSetting = std::array<u8, 4>; | ||
| 14 | using FeaturesPerId = std::array<bool, 0xA8>; | ||
| 15 | |||
| 16 | HidFirmwareSettings(); | ||
| 17 | |||
| 18 | void Reload(); | ||
| 19 | void LoadSettings(bool reload_config); | ||
| 20 | |||
| 21 | bool IsDebugPadEnabled(); | ||
| 22 | bool IsDeviceManaged(); | ||
| 23 | bool IsEmulateFutureDevice(); | ||
| 24 | bool IsTouchI2cManaged(); | ||
| 25 | bool IsHandheldForced(); | ||
| 26 | bool IsRailEnabled(); | ||
| 27 | bool IsHardwareErrorEmulated(); | ||
| 28 | bool IsBleDisabled(); | ||
| 29 | bool IsDscaleDisabled(); | ||
| 30 | bool IsTouchAutoUpdateDisabled(); | ||
| 31 | |||
| 32 | FirmwareSetting GetFirmwareUpdateFailure(); | ||
| 33 | FeaturesPerId FeaturesDisabledPerId(); | ||
| 34 | |||
| 35 | private: | ||
| 36 | bool is_initalized{}; | ||
| 37 | |||
| 38 | // Debug settings | ||
| 39 | bool is_debug_pad_enabled{}; | ||
| 40 | bool is_device_managed{}; | ||
| 41 | bool is_touch_i2c_managed{}; | ||
| 42 | bool is_future_devices_emulated{}; | ||
| 43 | bool is_mcu_hardware_error_emulated{}; | ||
| 44 | bool is_rail_enabled{}; | ||
| 45 | bool is_firmware_update_failure_emulated{}; | ||
| 46 | bool is_ble_disabled{}; | ||
| 47 | bool is_dscale_disabled{}; | ||
| 48 | bool is_handheld_forced{}; | ||
| 49 | bool is_touch_firmware_auto_update_disabled{}; | ||
| 50 | FirmwareSetting is_firmware_update_failure{}; | ||
| 51 | FeaturesPerId features_per_id_disabled{}; | ||
| 52 | }; | ||
| 53 | |||
| 54 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp new file mode 100644 index 000000000..583142e35 --- /dev/null +++ b/src/core/hle/service/hid/hid_server.cpp | |||
| @@ -0,0 +1,2371 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include <array> | ||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "common/settings.h" | ||
| 8 | #include "core/hid/hid_core.h" | ||
| 9 | #include "core/hle/kernel/k_shared_memory.h" | ||
| 10 | #include "core/hle/kernel/k_transfer_memory.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | ||
| 12 | #include "core/hle/service/hid/errors.h" | ||
| 13 | #include "core/hle/service/hid/hid_firmware_settings.h" | ||
| 14 | #include "core/hle/service/hid/hid_server.h" | ||
| 15 | #include "core/hle/service/hid/hid_util.h" | ||
| 16 | #include "core/hle/service/hid/resource_manager.h" | ||
| 17 | #include "core/hle/service/ipc_helpers.h" | ||
| 18 | #include "core/memory.h" | ||
| 19 | |||
| 20 | #include "core/hle/service/hid/controllers/console_six_axis.h" | ||
| 21 | #include "core/hle/service/hid/controllers/controller_base.h" | ||
| 22 | #include "core/hle/service/hid/controllers/debug_pad.h" | ||
| 23 | #include "core/hle/service/hid/controllers/gesture.h" | ||
| 24 | #include "core/hle/service/hid/controllers/keyboard.h" | ||
| 25 | #include "core/hle/service/hid/controllers/mouse.h" | ||
| 26 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 27 | #include "core/hle/service/hid/controllers/palma.h" | ||
| 28 | #include "core/hle/service/hid/controllers/seven_six_axis.h" | ||
| 29 | #include "core/hle/service/hid/controllers/six_axis.h" | ||
| 30 | #include "core/hle/service/hid/controllers/touchscreen.h" | ||
| 31 | |||
| 32 | namespace Service::HID { | ||
| 33 | |||
| 34 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { | ||
| 35 | public: | ||
| 36 | explicit IActiveVibrationDeviceList(Core::System& system_, | ||
| 37 | std::shared_ptr<ResourceManager> resource) | ||
| 38 | : ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) { | ||
| 39 | // clang-format off | ||
| 40 | static const FunctionInfo functions[] = { | ||
| 41 | {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"}, | ||
| 42 | }; | ||
| 43 | // clang-format on | ||
| 44 | |||
| 45 | RegisterHandlers(functions); | ||
| 46 | } | ||
| 47 | |||
| 48 | private: | ||
| 49 | void InitializeVibrationDevice(HLERequestContext& ctx) { | ||
| 50 | IPC::RequestParser rp{ctx}; | ||
| 51 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; | ||
| 52 | |||
| 53 | if (resource_manager != nullptr) { | ||
| 54 | resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle); | ||
| 55 | } | ||
| 56 | |||
| 57 | LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", | ||
| 58 | vibration_device_handle.npad_type, vibration_device_handle.npad_id, | ||
| 59 | vibration_device_handle.device_index); | ||
| 60 | |||
| 61 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 62 | rb.Push(ResultSuccess); | ||
| 63 | } | ||
| 64 | |||
| 65 | std::shared_ptr<ResourceManager> resource_manager; | ||
| 66 | }; | ||
| 67 | |||
| 68 | IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, | ||
| 69 | std::shared_ptr<HidFirmwareSettings> settings) | ||
| 70 | : ServiceFramework{system_, "hid"}, resource_manager{resource}, firmware_settings{settings} { | ||
| 71 | // clang-format off | ||
| 72 | static const FunctionInfo functions[] = { | ||
| 73 | {0, &IHidServer::CreateAppletResource, "CreateAppletResource"}, | ||
| 74 | {1, &IHidServer::ActivateDebugPad, "ActivateDebugPad"}, | ||
| 75 | {11, &IHidServer::ActivateTouchScreen, "ActivateTouchScreen"}, | ||
| 76 | {21, &IHidServer::ActivateMouse, "ActivateMouse"}, | ||
| 77 | {26, nullptr, "ActivateDebugMouse"}, | ||
| 78 | {31, &IHidServer::ActivateKeyboard, "ActivateKeyboard"}, | ||
| 79 | {32, &IHidServer::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"}, | ||
| 80 | {40, &IHidServer::AcquireXpadIdEventHandle, "AcquireXpadIdEventHandle"}, | ||
| 81 | {41, &IHidServer::ReleaseXpadIdEventHandle, "ReleaseXpadIdEventHandle"}, | ||
| 82 | {51, &IHidServer::ActivateXpad, "ActivateXpad"}, | ||
| 83 | {55, &IHidServer::GetXpadIds, "GetXpadIds"}, | ||
| 84 | {56, &IHidServer::ActivateJoyXpad, "ActivateJoyXpad"}, | ||
| 85 | {58, &IHidServer::GetJoyXpadLifoHandle, "GetJoyXpadLifoHandle"}, | ||
| 86 | {59, &IHidServer::GetJoyXpadIds, "GetJoyXpadIds"}, | ||
| 87 | {60, &IHidServer::ActivateSixAxisSensor, "ActivateSixAxisSensor"}, | ||
| 88 | {61, &IHidServer::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"}, | ||
| 89 | {62, &IHidServer::GetSixAxisSensorLifoHandle, "GetSixAxisSensorLifoHandle"}, | ||
| 90 | {63, &IHidServer::ActivateJoySixAxisSensor, "ActivateJoySixAxisSensor"}, | ||
| 91 | {64, &IHidServer::DeactivateJoySixAxisSensor, "DeactivateJoySixAxisSensor"}, | ||
| 92 | {65, &IHidServer::GetJoySixAxisSensorLifoHandle, "GetJoySixAxisSensorLifoHandle"}, | ||
| 93 | {66, &IHidServer::StartSixAxisSensor, "StartSixAxisSensor"}, | ||
| 94 | {67, &IHidServer::StopSixAxisSensor, "StopSixAxisSensor"}, | ||
| 95 | {68, &IHidServer::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"}, | ||
| 96 | {69, &IHidServer::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, | ||
| 97 | {70, &IHidServer::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, | ||
| 98 | {71, &IHidServer::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, | ||
| 99 | {72, &IHidServer::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"}, | ||
| 100 | {73, nullptr, "SetAccelerometerParameters"}, | ||
| 101 | {74, nullptr, "GetAccelerometerParameters"}, | ||
| 102 | {75, nullptr, "ResetAccelerometerParameters"}, | ||
| 103 | {76, nullptr, "SetAccelerometerPlayMode"}, | ||
| 104 | {77, nullptr, "GetAccelerometerPlayMode"}, | ||
| 105 | {78, nullptr, "ResetAccelerometerPlayMode"}, | ||
| 106 | {79, &IHidServer::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, | ||
| 107 | {80, &IHidServer::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"}, | ||
| 108 | {81, &IHidServer::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, | ||
| 109 | {82, &IHidServer::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, | ||
| 110 | {83, &IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"}, | ||
| 111 | {84, &IHidServer::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"}, | ||
| 112 | {85, &IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"}, | ||
| 113 | {86, nullptr, "StoreSixAxisSensorCalibrationParameter"}, | ||
| 114 | {87, &IHidServer::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"}, | ||
| 115 | {88, &IHidServer::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"}, | ||
| 116 | {89, &IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, | ||
| 117 | {91, &IHidServer::ActivateGesture, "ActivateGesture"}, | ||
| 118 | {100, &IHidServer::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, | ||
| 119 | {101, &IHidServer::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, | ||
| 120 | {102, &IHidServer::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, | ||
| 121 | {103, &IHidServer::ActivateNpad, "ActivateNpad"}, | ||
| 122 | {104, &IHidServer::DeactivateNpad, "DeactivateNpad"}, | ||
| 123 | {106, &IHidServer::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, | ||
| 124 | {107, &IHidServer::DisconnectNpad, "DisconnectNpad"}, | ||
| 125 | {108, &IHidServer::GetPlayerLedPattern, "GetPlayerLedPattern"}, | ||
| 126 | {109, &IHidServer::ActivateNpadWithRevision, "ActivateNpadWithRevision"}, | ||
| 127 | {120, &IHidServer::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, | ||
| 128 | {121, &IHidServer::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, | ||
| 129 | {122, &IHidServer::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, | ||
| 130 | {123, &IHidServer::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"}, | ||
| 131 | {124, &IHidServer::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, | ||
| 132 | {125, &IHidServer::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, | ||
| 133 | {126, &IHidServer::StartLrAssignmentMode, "StartLrAssignmentMode"}, | ||
| 134 | {127, &IHidServer::StopLrAssignmentMode, "StopLrAssignmentMode"}, | ||
| 135 | {128, &IHidServer::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, | ||
| 136 | {129, &IHidServer::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"}, | ||
| 137 | {130, &IHidServer::SwapNpadAssignment, "SwapNpadAssignment"}, | ||
| 138 | {131, &IHidServer::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"}, | ||
| 139 | {132, &IHidServer::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, | ||
| 140 | {133, &IHidServer::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"}, | ||
| 141 | {134, &IHidServer::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"}, | ||
| 142 | {135, &IHidServer::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"}, | ||
| 143 | {136, &IHidServer::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"}, | ||
| 144 | {200, &IHidServer::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, | ||
| 145 | {201, &IHidServer::SendVibrationValue, "SendVibrationValue"}, | ||
| 146 | {202, &IHidServer::GetActualVibrationValue, "GetActualVibrationValue"}, | ||
| 147 | {203, &IHidServer::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, | ||
| 148 | {204, &IHidServer::PermitVibration, "PermitVibration"}, | ||
| 149 | {205, &IHidServer::IsVibrationPermitted, "IsVibrationPermitted"}, | ||
| 150 | {206, &IHidServer::SendVibrationValues, "SendVibrationValues"}, | ||
| 151 | {207, &IHidServer::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"}, | ||
| 152 | {208, &IHidServer::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"}, | ||
| 153 | {209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, | ||
| 154 | {210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"}, | ||
| 155 | {211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, | ||
| 156 | {212, nullptr, "SendVibrationValueInBool"}, | ||
| 157 | {300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, | ||
| 158 | {301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, | ||
| 159 | {302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, | ||
| 160 | {303, &IHidServer::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"}, | ||
| 161 | {304, &IHidServer::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"}, | ||
| 162 | {305, &IHidServer::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, | ||
| 163 | {306, &IHidServer::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, | ||
| 164 | {307, &IHidServer::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"}, | ||
| 165 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, | ||
| 166 | {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, | ||
| 167 | {310, &IHidServer::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, | ||
| 168 | {400, &IHidServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, | ||
| 169 | {401, nullptr, "EnableUsbFullKeyController"}, | ||
| 170 | {402, nullptr, "IsUsbFullKeyControllerConnected"}, | ||
| 171 | {403, nullptr, "HasBattery"}, | ||
| 172 | {404, nullptr, "HasLeftRightBattery"}, | ||
| 173 | {405, nullptr, "GetNpadInterfaceType"}, | ||
| 174 | {406, nullptr, "GetNpadLeftRightInterfaceType"}, | ||
| 175 | {407, nullptr, "GetNpadOfHighestBatteryLevel"}, | ||
| 176 | {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, | ||
| 177 | {500, &IHidServer::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"}, | ||
| 178 | {501, &IHidServer::InitializePalma, "InitializePalma"}, | ||
| 179 | {502, &IHidServer::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"}, | ||
| 180 | {503, &IHidServer::GetPalmaOperationInfo, "GetPalmaOperationInfo"}, | ||
| 181 | {504, &IHidServer::PlayPalmaActivity, "PlayPalmaActivity"}, | ||
| 182 | {505, &IHidServer::SetPalmaFrModeType, "SetPalmaFrModeType"}, | ||
| 183 | {506, &IHidServer::ReadPalmaStep, "ReadPalmaStep"}, | ||
| 184 | {507, &IHidServer::EnablePalmaStep, "EnablePalmaStep"}, | ||
| 185 | {508, &IHidServer::ResetPalmaStep, "ResetPalmaStep"}, | ||
| 186 | {509, &IHidServer::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"}, | ||
| 187 | {510, &IHidServer::WritePalmaApplicationSection, "WritePalmaApplicationSection"}, | ||
| 188 | {511, &IHidServer::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"}, | ||
| 189 | {512, &IHidServer::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"}, | ||
| 190 | {513, &IHidServer::WritePalmaActivityEntry, "WritePalmaActivityEntry"}, | ||
| 191 | {514, &IHidServer::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"}, | ||
| 192 | {515, &IHidServer::WritePalmaWaveEntry, "WritePalmaWaveEntry"}, | ||
| 193 | {516, &IHidServer::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"}, | ||
| 194 | {517, &IHidServer::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"}, | ||
| 195 | {518, &IHidServer::SuspendPalmaFeature, "SuspendPalmaFeature"}, | ||
| 196 | {519, &IHidServer::GetPalmaOperationResult, "GetPalmaOperationResult"}, | ||
| 197 | {520, &IHidServer::ReadPalmaPlayLog, "ReadPalmaPlayLog"}, | ||
| 198 | {521, &IHidServer::ResetPalmaPlayLog, "ResetPalmaPlayLog"}, | ||
| 199 | {522, &IHidServer::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, | ||
| 200 | {523, &IHidServer::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"}, | ||
| 201 | {524, &IHidServer::PairPalma, "PairPalma"}, | ||
| 202 | {525, &IHidServer::SetPalmaBoostMode, "SetPalmaBoostMode"}, | ||
| 203 | {526, &IHidServer::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"}, | ||
| 204 | {527, &IHidServer::EnablePalmaBoostMode, "EnablePalmaBoostMode"}, | ||
| 205 | {528, &IHidServer::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"}, | ||
| 206 | {529, &IHidServer::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"}, | ||
| 207 | {1000, &IHidServer::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, | ||
| 208 | {1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, | ||
| 209 | {1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, | ||
| 210 | {1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"}, | ||
| 211 | {2000, nullptr, "ActivateDigitizer"}, | ||
| 212 | }; | ||
| 213 | // clang-format on | ||
| 214 | |||
| 215 | RegisterHandlers(functions); | ||
| 216 | } | ||
| 217 | |||
| 218 | IHidServer::~IHidServer() = default; | ||
| 219 | |||
| 220 | void IHidServer::CreateAppletResource(HLERequestContext& ctx) { | ||
| 221 | IPC::RequestParser rp{ctx}; | ||
| 222 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 223 | |||
| 224 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 225 | |||
| 226 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 227 | rb.Push(ResultSuccess); | ||
| 228 | rb.PushIpcInterface<IAppletResource>(system, resource_manager); | ||
| 229 | } | ||
| 230 | |||
| 231 | void IHidServer::ActivateDebugPad(HLERequestContext& ctx) { | ||
| 232 | IPC::RequestParser rp{ctx}; | ||
| 233 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 234 | |||
| 235 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 236 | |||
| 237 | Result result = ResultSuccess; | ||
| 238 | auto debug_pad = GetResourceManager()->GetDebugPad(); | ||
| 239 | |||
| 240 | if (!firmware_settings->IsDeviceManaged()) { | ||
| 241 | result = debug_pad->Activate(); | ||
| 242 | } | ||
| 243 | |||
| 244 | if (result.IsSuccess()) { | ||
| 245 | result = debug_pad->Activate(applet_resource_user_id); | ||
| 246 | } | ||
| 247 | |||
| 248 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 249 | rb.Push(result); | ||
| 250 | } | ||
| 251 | |||
| 252 | void IHidServer::ActivateTouchScreen(HLERequestContext& ctx) { | ||
| 253 | IPC::RequestParser rp{ctx}; | ||
| 254 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 255 | |||
| 256 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 257 | |||
| 258 | Result result = ResultSuccess; | ||
| 259 | auto touch_screen = GetResourceManager()->GetTouchScreen(); | ||
| 260 | |||
| 261 | if (!firmware_settings->IsDeviceManaged()) { | ||
| 262 | result = touch_screen->Activate(); | ||
| 263 | } | ||
| 264 | |||
| 265 | if (result.IsSuccess()) { | ||
| 266 | result = touch_screen->Activate(applet_resource_user_id); | ||
| 267 | } | ||
| 268 | |||
| 269 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 270 | rb.Push(result); | ||
| 271 | } | ||
| 272 | |||
| 273 | void IHidServer::ActivateMouse(HLERequestContext& ctx) { | ||
| 274 | IPC::RequestParser rp{ctx}; | ||
| 275 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 276 | |||
| 277 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 278 | |||
| 279 | Result result = ResultSuccess; | ||
| 280 | auto mouse = GetResourceManager()->GetMouse(); | ||
| 281 | |||
| 282 | if (!firmware_settings->IsDeviceManaged()) { | ||
| 283 | result = mouse->Activate(); | ||
| 284 | } | ||
| 285 | |||
| 286 | if (result.IsSuccess()) { | ||
| 287 | result = mouse->Activate(applet_resource_user_id); | ||
| 288 | } | ||
| 289 | |||
| 290 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 291 | rb.Push(result); | ||
| 292 | } | ||
| 293 | |||
| 294 | void IHidServer::ActivateKeyboard(HLERequestContext& ctx) { | ||
| 295 | IPC::RequestParser rp{ctx}; | ||
| 296 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 297 | |||
| 298 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 299 | |||
| 300 | Result result = ResultSuccess; | ||
| 301 | auto keyboard = GetResourceManager()->GetKeyboard(); | ||
| 302 | |||
| 303 | if (!firmware_settings->IsDeviceManaged()) { | ||
| 304 | result = keyboard->Activate(); | ||
| 305 | } | ||
| 306 | |||
| 307 | if (result.IsSuccess()) { | ||
| 308 | result = keyboard->Activate(applet_resource_user_id); | ||
| 309 | } | ||
| 310 | |||
| 311 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 312 | rb.Push(result); | ||
| 313 | } | ||
| 314 | |||
| 315 | void IHidServer::SendKeyboardLockKeyEvent(HLERequestContext& ctx) { | ||
| 316 | IPC::RequestParser rp{ctx}; | ||
| 317 | const auto flags{rp.Pop<u32>()}; | ||
| 318 | |||
| 319 | LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); | ||
| 320 | |||
| 321 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 322 | rb.Push(ResultSuccess); | ||
| 323 | } | ||
| 324 | |||
| 325 | void IHidServer::AcquireXpadIdEventHandle(HLERequestContext& ctx) { | ||
| 326 | IPC::RequestParser rp{ctx}; | ||
| 327 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 328 | |||
| 329 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 330 | |||
| 331 | // This function has been stubbed since 10.0.0+ | ||
| 332 | |||
| 333 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 334 | rb.Push(ResultSuccess); | ||
| 335 | // Handle returned is null here | ||
| 336 | } | ||
| 337 | |||
| 338 | void IHidServer::ReleaseXpadIdEventHandle(HLERequestContext& ctx) { | ||
| 339 | IPC::RequestParser rp{ctx}; | ||
| 340 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 341 | |||
| 342 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 343 | |||
| 344 | // This function has been stubbed since 10.0.0+ | ||
| 345 | |||
| 346 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 347 | rb.Push(ResultSuccess); | ||
| 348 | } | ||
| 349 | |||
| 350 | void IHidServer::ActivateXpad(HLERequestContext& ctx) { | ||
| 351 | IPC::RequestParser rp{ctx}; | ||
| 352 | struct Parameters { | ||
| 353 | u32 basic_xpad_id; | ||
| 354 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 355 | u64 applet_resource_user_id; | ||
| 356 | }; | ||
| 357 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 358 | |||
| 359 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 360 | |||
| 361 | LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", | ||
| 362 | parameters.basic_xpad_id, parameters.applet_resource_user_id); | ||
| 363 | |||
| 364 | // This function has been stubbed since 10.0.0+ | ||
| 365 | |||
| 366 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 367 | rb.Push(ResultSuccess); | ||
| 368 | } | ||
| 369 | |||
| 370 | void IHidServer::GetXpadIds(HLERequestContext& ctx) { | ||
| 371 | LOG_DEBUG(Service_HID, "called"); | ||
| 372 | |||
| 373 | // This function has been hardcoded since 10.0.0+ | ||
| 374 | const std::array<u32, 4> basic_xpad_id{0, 1, 2, 3}; | ||
| 375 | ctx.WriteBuffer(basic_xpad_id); | ||
| 376 | |||
| 377 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 378 | rb.Push(ResultSuccess); | ||
| 379 | rb.Push<s64>(basic_xpad_id.size()); | ||
| 380 | } | ||
| 381 | |||
| 382 | void IHidServer::ActivateJoyXpad(HLERequestContext& ctx) { | ||
| 383 | IPC::RequestParser rp{ctx}; | ||
| 384 | const auto joy_xpad_id{rp.Pop<u32>()}; | ||
| 385 | |||
| 386 | LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); | ||
| 387 | |||
| 388 | // This function has been stubbed since 10.0.0+ | ||
| 389 | |||
| 390 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 391 | rb.Push(ResultSuccess); | ||
| 392 | } | ||
| 393 | |||
| 394 | void IHidServer::GetJoyXpadLifoHandle(HLERequestContext& ctx) { | ||
| 395 | IPC::RequestParser rp{ctx}; | ||
| 396 | const auto joy_xpad_id{rp.Pop<u32>()}; | ||
| 397 | |||
| 398 | LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); | ||
| 399 | |||
| 400 | // This function has been stubbed since 10.0.0+ | ||
| 401 | |||
| 402 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 403 | rb.Push(ResultSuccess); | ||
| 404 | // Handle returned is null here | ||
| 405 | } | ||
| 406 | |||
| 407 | void IHidServer::GetJoyXpadIds(HLERequestContext& ctx) { | ||
| 408 | LOG_DEBUG(Service_HID, "called"); | ||
| 409 | |||
| 410 | // This function has been hardcoded since 10.0.0+ | ||
| 411 | const s64 basic_xpad_id_count{}; | ||
| 412 | |||
| 413 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 414 | rb.Push(ResultSuccess); | ||
| 415 | rb.Push(basic_xpad_id_count); | ||
| 416 | } | ||
| 417 | |||
| 418 | void IHidServer::ActivateSixAxisSensor(HLERequestContext& ctx) { | ||
| 419 | IPC::RequestParser rp{ctx}; | ||
| 420 | const auto joy_xpad_id{rp.Pop<u32>()}; | ||
| 421 | |||
| 422 | LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); | ||
| 423 | |||
| 424 | // This function has been stubbed since 10.0.0+ | ||
| 425 | |||
| 426 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 427 | rb.Push(ResultSuccess); | ||
| 428 | } | ||
| 429 | |||
| 430 | void IHidServer::DeactivateSixAxisSensor(HLERequestContext& ctx) { | ||
| 431 | IPC::RequestParser rp{ctx}; | ||
| 432 | const auto joy_xpad_id{rp.Pop<u32>()}; | ||
| 433 | |||
| 434 | LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); | ||
| 435 | |||
| 436 | // This function has been stubbed since 10.0.0+ | ||
| 437 | |||
| 438 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 439 | rb.Push(ResultSuccess); | ||
| 440 | } | ||
| 441 | |||
| 442 | void IHidServer::GetSixAxisSensorLifoHandle(HLERequestContext& ctx) { | ||
| 443 | IPC::RequestParser rp{ctx}; | ||
| 444 | const auto joy_xpad_id{rp.Pop<u32>()}; | ||
| 445 | |||
| 446 | LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); | ||
| 447 | |||
| 448 | // This function has been stubbed since 10.0.0+ | ||
| 449 | |||
| 450 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 451 | rb.Push(ResultSuccess); | ||
| 452 | } | ||
| 453 | |||
| 454 | void IHidServer::ActivateJoySixAxisSensor(HLERequestContext& ctx) { | ||
| 455 | IPC::RequestParser rp{ctx}; | ||
| 456 | const auto joy_xpad_id{rp.Pop<u32>()}; | ||
| 457 | |||
| 458 | LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); | ||
| 459 | |||
| 460 | // This function has been stubbed since 10.0.0+ | ||
| 461 | |||
| 462 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 463 | rb.Push(ResultSuccess); | ||
| 464 | } | ||
| 465 | |||
| 466 | void IHidServer::DeactivateJoySixAxisSensor(HLERequestContext& ctx) { | ||
| 467 | IPC::RequestParser rp{ctx}; | ||
| 468 | const auto joy_xpad_id{rp.Pop<u32>()}; | ||
| 469 | |||
| 470 | LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); | ||
| 471 | |||
| 472 | // This function has been stubbed since 10.0.0+ | ||
| 473 | |||
| 474 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 475 | rb.Push(ResultSuccess); | ||
| 476 | } | ||
| 477 | |||
| 478 | void IHidServer::GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx) { | ||
| 479 | IPC::RequestParser rp{ctx}; | ||
| 480 | const auto joy_xpad_id{rp.Pop<u32>()}; | ||
| 481 | |||
| 482 | LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id); | ||
| 483 | |||
| 484 | // This function has been stubbed since 10.0.0+ | ||
| 485 | |||
| 486 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 487 | rb.Push(ResultSuccess); | ||
| 488 | // Handle returned is null here | ||
| 489 | } | ||
| 490 | |||
| 491 | void IHidServer::StartSixAxisSensor(HLERequestContext& ctx) { | ||
| 492 | IPC::RequestParser rp{ctx}; | ||
| 493 | struct Parameters { | ||
| 494 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 495 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 496 | u64 applet_resource_user_id; | ||
| 497 | }; | ||
| 498 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 499 | |||
| 500 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 501 | |||
| 502 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 503 | const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, true); | ||
| 504 | |||
| 505 | LOG_DEBUG(Service_HID, | ||
| 506 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 507 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 508 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 509 | |||
| 510 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 511 | rb.Push(result); | ||
| 512 | } | ||
| 513 | |||
| 514 | void IHidServer::StopSixAxisSensor(HLERequestContext& ctx) { | ||
| 515 | IPC::RequestParser rp{ctx}; | ||
| 516 | struct Parameters { | ||
| 517 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 518 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 519 | u64 applet_resource_user_id; | ||
| 520 | }; | ||
| 521 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 522 | |||
| 523 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 524 | |||
| 525 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 526 | const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, false); | ||
| 527 | |||
| 528 | LOG_DEBUG(Service_HID, | ||
| 529 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 530 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 531 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 532 | |||
| 533 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 534 | rb.Push(result); | ||
| 535 | } | ||
| 536 | |||
| 537 | void IHidServer::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) { | ||
| 538 | IPC::RequestParser rp{ctx}; | ||
| 539 | struct Parameters { | ||
| 540 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 541 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 542 | u64 applet_resource_user_id; | ||
| 543 | }; | ||
| 544 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 545 | |||
| 546 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 547 | |||
| 548 | bool is_enabled{}; | ||
| 549 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 550 | const auto result = | ||
| 551 | six_axis->IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); | ||
| 552 | |||
| 553 | LOG_DEBUG(Service_HID, | ||
| 554 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 555 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 556 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 557 | |||
| 558 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 559 | rb.Push(result); | ||
| 560 | rb.Push(is_enabled); | ||
| 561 | } | ||
| 562 | |||
| 563 | void IHidServer::EnableSixAxisSensorFusion(HLERequestContext& ctx) { | ||
| 564 | IPC::RequestParser rp{ctx}; | ||
| 565 | struct Parameters { | ||
| 566 | bool enable_sixaxis_sensor_fusion; | ||
| 567 | INSERT_PADDING_BYTES_NOINIT(3); | ||
| 568 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 569 | u64 applet_resource_user_id; | ||
| 570 | }; | ||
| 571 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 572 | |||
| 573 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 574 | |||
| 575 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 576 | const auto result = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle, | ||
| 577 | parameters.enable_sixaxis_sensor_fusion); | ||
| 578 | |||
| 579 | LOG_DEBUG(Service_HID, | ||
| 580 | "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " | ||
| 581 | "device_index={}, applet_resource_user_id={}", | ||
| 582 | parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, | ||
| 583 | parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, | ||
| 584 | parameters.applet_resource_user_id); | ||
| 585 | |||
| 586 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 587 | rb.Push(result); | ||
| 588 | } | ||
| 589 | |||
| 590 | void IHidServer::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) { | ||
| 591 | IPC::RequestParser rp{ctx}; | ||
| 592 | struct Parameters { | ||
| 593 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 594 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion; | ||
| 595 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 596 | u64 applet_resource_user_id; | ||
| 597 | }; | ||
| 598 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 599 | |||
| 600 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 601 | |||
| 602 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 603 | const auto result = | ||
| 604 | six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); | ||
| 605 | |||
| 606 | LOG_DEBUG(Service_HID, | ||
| 607 | "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " | ||
| 608 | "parameter2={}, applet_resource_user_id={}", | ||
| 609 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 610 | parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1, | ||
| 611 | parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); | ||
| 612 | |||
| 613 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 614 | rb.Push(result); | ||
| 615 | } | ||
| 616 | |||
| 617 | void IHidServer::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) { | ||
| 618 | IPC::RequestParser rp{ctx}; | ||
| 619 | struct Parameters { | ||
| 620 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 621 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 622 | u64 applet_resource_user_id; | ||
| 623 | }; | ||
| 624 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 625 | |||
| 626 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 627 | |||
| 628 | Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; | ||
| 629 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 630 | const auto result = | ||
| 631 | six_axis->GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); | ||
| 632 | |||
| 633 | LOG_DEBUG(Service_HID, | ||
| 634 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 635 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 636 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 637 | |||
| 638 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 639 | rb.Push(result); | ||
| 640 | rb.PushRaw(fusion_parameters); | ||
| 641 | } | ||
| 642 | |||
| 643 | void IHidServer::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) { | ||
| 644 | IPC::RequestParser rp{ctx}; | ||
| 645 | struct Parameters { | ||
| 646 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 647 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 648 | u64 applet_resource_user_id; | ||
| 649 | }; | ||
| 650 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 651 | |||
| 652 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 653 | |||
| 654 | // Since these parameters are unknown just use what HW outputs | ||
| 655 | const Core::HID::SixAxisSensorFusionParameters fusion_parameters{ | ||
| 656 | .parameter1 = 0.03f, | ||
| 657 | .parameter2 = 0.4f, | ||
| 658 | }; | ||
| 659 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 660 | const auto result1 = | ||
| 661 | six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); | ||
| 662 | const auto result2 = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); | ||
| 663 | |||
| 664 | LOG_DEBUG(Service_HID, | ||
| 665 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 666 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 667 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 668 | |||
| 669 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 670 | if (result1.IsError()) { | ||
| 671 | rb.Push(result1); | ||
| 672 | return; | ||
| 673 | } | ||
| 674 | rb.Push(result2); | ||
| 675 | } | ||
| 676 | |||
| 677 | void IHidServer::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) { | ||
| 678 | IPC::RequestParser rp{ctx}; | ||
| 679 | const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()}; | ||
| 680 | const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()}; | ||
| 681 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 682 | |||
| 683 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 684 | const auto result = six_axis->SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); | ||
| 685 | |||
| 686 | LOG_DEBUG(Service_HID, | ||
| 687 | "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " | ||
| 688 | "applet_resource_user_id={}", | ||
| 689 | sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index, | ||
| 690 | drift_mode, applet_resource_user_id); | ||
| 691 | |||
| 692 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 693 | rb.Push(result); | ||
| 694 | } | ||
| 695 | |||
| 696 | void IHidServer::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) { | ||
| 697 | IPC::RequestParser rp{ctx}; | ||
| 698 | struct Parameters { | ||
| 699 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 700 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 701 | u64 applet_resource_user_id; | ||
| 702 | }; | ||
| 703 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 704 | |||
| 705 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 706 | |||
| 707 | auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; | ||
| 708 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 709 | const auto result = six_axis->GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); | ||
| 710 | |||
| 711 | LOG_DEBUG(Service_HID, | ||
| 712 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 713 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 714 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 715 | |||
| 716 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 717 | rb.Push(result); | ||
| 718 | rb.PushEnum(drift_mode); | ||
| 719 | } | ||
| 720 | |||
| 721 | void IHidServer::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) { | ||
| 722 | IPC::RequestParser rp{ctx}; | ||
| 723 | struct Parameters { | ||
| 724 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 725 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 726 | u64 applet_resource_user_id; | ||
| 727 | }; | ||
| 728 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 729 | |||
| 730 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 731 | |||
| 732 | const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; | ||
| 733 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 734 | const auto result = six_axis->SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); | ||
| 735 | |||
| 736 | LOG_DEBUG(Service_HID, | ||
| 737 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 738 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 739 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 740 | |||
| 741 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 742 | rb.Push(result); | ||
| 743 | } | ||
| 744 | |||
| 745 | void IHidServer::IsSixAxisSensorAtRest(HLERequestContext& ctx) { | ||
| 746 | IPC::RequestParser rp{ctx}; | ||
| 747 | struct Parameters { | ||
| 748 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 749 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 750 | u64 applet_resource_user_id; | ||
| 751 | }; | ||
| 752 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 753 | |||
| 754 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 755 | |||
| 756 | bool is_at_rest{}; | ||
| 757 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 758 | six_axis->IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); | ||
| 759 | |||
| 760 | LOG_DEBUG(Service_HID, | ||
| 761 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 762 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 763 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 764 | |||
| 765 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 766 | rb.Push(ResultSuccess); | ||
| 767 | rb.Push(is_at_rest); | ||
| 768 | } | ||
| 769 | |||
| 770 | void IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) { | ||
| 771 | IPC::RequestParser rp{ctx}; | ||
| 772 | struct Parameters { | ||
| 773 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 774 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 775 | u64 applet_resource_user_id; | ||
| 776 | }; | ||
| 777 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 778 | |||
| 779 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 780 | |||
| 781 | bool is_firmware_available{}; | ||
| 782 | auto controller = GetResourceManager()->GetNpad(); | ||
| 783 | controller->IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, | ||
| 784 | is_firmware_available); | ||
| 785 | |||
| 786 | LOG_WARNING( | ||
| 787 | Service_HID, | ||
| 788 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 789 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 790 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 791 | |||
| 792 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 793 | rb.Push(ResultSuccess); | ||
| 794 | rb.Push(is_firmware_available); | ||
| 795 | } | ||
| 796 | |||
| 797 | void IHidServer::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) { | ||
| 798 | IPC::RequestParser rp{ctx}; | ||
| 799 | struct Parameters { | ||
| 800 | bool enabled; | ||
| 801 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 802 | u64 applet_resource_user_id; | ||
| 803 | }; | ||
| 804 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 805 | |||
| 806 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 807 | |||
| 808 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 809 | const auto result = six_axis->EnableSixAxisSensorUnalteredPassthrough(parameters.sixaxis_handle, | ||
| 810 | parameters.enabled); | ||
| 811 | |||
| 812 | LOG_DEBUG(Service_HID, | ||
| 813 | "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " | ||
| 814 | "applet_resource_user_id={}", | ||
| 815 | parameters.enabled, parameters.sixaxis_handle.npad_type, | ||
| 816 | parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, | ||
| 817 | parameters.applet_resource_user_id); | ||
| 818 | |||
| 819 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 820 | rb.Push(result); | ||
| 821 | } | ||
| 822 | |||
| 823 | void IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) { | ||
| 824 | IPC::RequestParser rp{ctx}; | ||
| 825 | struct Parameters { | ||
| 826 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 827 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 828 | u64 applet_resource_user_id; | ||
| 829 | }; | ||
| 830 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 831 | |||
| 832 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 833 | |||
| 834 | bool is_unaltered_sisxaxis_enabled{}; | ||
| 835 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 836 | const auto result = six_axis->IsSixAxisSensorUnalteredPassthroughEnabled( | ||
| 837 | parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); | ||
| 838 | |||
| 839 | LOG_DEBUG( | ||
| 840 | Service_HID, | ||
| 841 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 842 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 843 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 844 | |||
| 845 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 846 | rb.Push(result); | ||
| 847 | rb.Push(is_unaltered_sisxaxis_enabled); | ||
| 848 | } | ||
| 849 | |||
| 850 | void IHidServer::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) { | ||
| 851 | IPC::RequestParser rp{ctx}; | ||
| 852 | struct Parameters { | ||
| 853 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 854 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 855 | u64 applet_resource_user_id; | ||
| 856 | }; | ||
| 857 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 858 | |||
| 859 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 860 | |||
| 861 | Core::HID::SixAxisSensorCalibrationParameter calibration{}; | ||
| 862 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 863 | const auto result = | ||
| 864 | six_axis->LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration); | ||
| 865 | |||
| 866 | LOG_WARNING( | ||
| 867 | Service_HID, | ||
| 868 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 869 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 870 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 871 | |||
| 872 | if (result.IsSuccess()) { | ||
| 873 | ctx.WriteBuffer(calibration); | ||
| 874 | } | ||
| 875 | |||
| 876 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 877 | rb.Push(result); | ||
| 878 | } | ||
| 879 | |||
| 880 | void IHidServer::GetSixAxisSensorIcInformation(HLERequestContext& ctx) { | ||
| 881 | IPC::RequestParser rp{ctx}; | ||
| 882 | struct Parameters { | ||
| 883 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 884 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 885 | u64 applet_resource_user_id; | ||
| 886 | }; | ||
| 887 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 888 | |||
| 889 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 890 | |||
| 891 | Core::HID::SixAxisSensorIcInformation ic_information{}; | ||
| 892 | auto six_axis = GetResourceManager()->GetSixAxis(); | ||
| 893 | const auto result = | ||
| 894 | six_axis->GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information); | ||
| 895 | |||
| 896 | LOG_WARNING( | ||
| 897 | Service_HID, | ||
| 898 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 899 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 900 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 901 | |||
| 902 | if (result.IsSuccess()) { | ||
| 903 | ctx.WriteBuffer(ic_information); | ||
| 904 | } | ||
| 905 | |||
| 906 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 907 | rb.Push(result); | ||
| 908 | } | ||
| 909 | |||
| 910 | void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) { | ||
| 911 | IPC::RequestParser rp{ctx}; | ||
| 912 | struct Parameters { | ||
| 913 | Core::HID::SixAxisSensorHandle sixaxis_handle; | ||
| 914 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 915 | u64 applet_resource_user_id; | ||
| 916 | }; | ||
| 917 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 918 | |||
| 919 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 920 | |||
| 921 | auto controller = GetResourceManager()->GetNpad(); | ||
| 922 | const auto result = | ||
| 923 | controller->ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); | ||
| 924 | |||
| 925 | LOG_WARNING( | ||
| 926 | Service_HID, | ||
| 927 | "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 928 | parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, | ||
| 929 | parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); | ||
| 930 | |||
| 931 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 932 | rb.Push(result); | ||
| 933 | } | ||
| 934 | |||
| 935 | void IHidServer::ActivateGesture(HLERequestContext& ctx) { | ||
| 936 | IPC::RequestParser rp{ctx}; | ||
| 937 | struct Parameters { | ||
| 938 | u32 basic_gesture_id; | ||
| 939 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 940 | u64 applet_resource_user_id; | ||
| 941 | }; | ||
| 942 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 943 | |||
| 944 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 945 | |||
| 946 | LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}", | ||
| 947 | parameters.basic_gesture_id, parameters.applet_resource_user_id); | ||
| 948 | |||
| 949 | Result result = ResultSuccess; | ||
| 950 | auto gesture = GetResourceManager()->GetGesture(); | ||
| 951 | |||
| 952 | if (!firmware_settings->IsDeviceManaged()) { | ||
| 953 | result = gesture->Activate(); | ||
| 954 | } | ||
| 955 | |||
| 956 | if (result.IsSuccess()) { | ||
| 957 | // TODO: Use gesture id here | ||
| 958 | result = gesture->Activate(parameters.applet_resource_user_id); | ||
| 959 | } | ||
| 960 | |||
| 961 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 962 | rb.Push(result); | ||
| 963 | } | ||
| 964 | |||
| 965 | void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) { | ||
| 966 | IPC::RequestParser rp{ctx}; | ||
| 967 | struct Parameters { | ||
| 968 | Core::HID::NpadStyleSet supported_styleset; | ||
| 969 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 970 | u64 applet_resource_user_id; | ||
| 971 | }; | ||
| 972 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 973 | |||
| 974 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 975 | |||
| 976 | GetResourceManager()->GetNpad()->SetSupportedStyleSet({parameters.supported_styleset}); | ||
| 977 | |||
| 978 | LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", | ||
| 979 | parameters.supported_styleset, parameters.applet_resource_user_id); | ||
| 980 | |||
| 981 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 982 | rb.Push(ResultSuccess); | ||
| 983 | } | ||
| 984 | |||
| 985 | void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) { | ||
| 986 | IPC::RequestParser rp{ctx}; | ||
| 987 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 988 | |||
| 989 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 990 | |||
| 991 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 992 | rb.Push(ResultSuccess); | ||
| 993 | rb.PushEnum(GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw); | ||
| 994 | } | ||
| 995 | |||
| 996 | void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) { | ||
| 997 | IPC::RequestParser rp{ctx}; | ||
| 998 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 999 | |||
| 1000 | const auto result = GetResourceManager()->GetNpad()->SetSupportedNpadIdTypes(ctx.ReadBuffer()); | ||
| 1001 | |||
| 1002 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1003 | |||
| 1004 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1005 | rb.Push(result); | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | void IHidServer::ActivateNpad(HLERequestContext& ctx) { | ||
| 1009 | IPC::RequestParser rp{ctx}; | ||
| 1010 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1011 | |||
| 1012 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1013 | |||
| 1014 | auto npad = GetResourceManager()->GetNpad(); | ||
| 1015 | |||
| 1016 | // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0); | ||
| 1017 | const Result result = npad->Activate(applet_resource_user_id); | ||
| 1018 | |||
| 1019 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1020 | rb.Push(result); | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | void IHidServer::DeactivateNpad(HLERequestContext& ctx) { | ||
| 1024 | IPC::RequestParser rp{ctx}; | ||
| 1025 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1026 | |||
| 1027 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1028 | |||
| 1029 | // This function does nothing since 10.0.0+ | ||
| 1030 | |||
| 1031 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1032 | rb.Push(ResultSuccess); | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | void IHidServer::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) { | ||
| 1036 | IPC::RequestParser rp{ctx}; | ||
| 1037 | struct Parameters { | ||
| 1038 | Core::HID::NpadIdType npad_id; | ||
| 1039 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1040 | u64 applet_resource_user_id; | ||
| 1041 | u64 unknown; | ||
| 1042 | }; | ||
| 1043 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1044 | |||
| 1045 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1046 | |||
| 1047 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", | ||
| 1048 | parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); | ||
| 1049 | |||
| 1050 | // Games expect this event to be signaled after calling this function | ||
| 1051 | GetResourceManager()->GetNpad()->SignalStyleSetChangedEvent(parameters.npad_id); | ||
| 1052 | |||
| 1053 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 1054 | rb.Push(ResultSuccess); | ||
| 1055 | rb.PushCopyObjects( | ||
| 1056 | GetResourceManager()->GetNpad()->GetStyleSetChangedEvent(parameters.npad_id)); | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | void IHidServer::DisconnectNpad(HLERequestContext& ctx) { | ||
| 1060 | IPC::RequestParser rp{ctx}; | ||
| 1061 | struct Parameters { | ||
| 1062 | Core::HID::NpadIdType npad_id; | ||
| 1063 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1064 | u64 applet_resource_user_id; | ||
| 1065 | }; | ||
| 1066 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1067 | |||
| 1068 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1069 | |||
| 1070 | auto controller = GetResourceManager()->GetNpad(); | ||
| 1071 | controller->DisconnectNpad(parameters.npad_id); | ||
| 1072 | |||
| 1073 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | ||
| 1074 | parameters.applet_resource_user_id); | ||
| 1075 | |||
| 1076 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1077 | rb.Push(ResultSuccess); | ||
| 1078 | } | ||
| 1079 | |||
| 1080 | void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) { | ||
| 1081 | IPC::RequestParser rp{ctx}; | ||
| 1082 | const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 1083 | |||
| 1084 | Core::HID::LedPattern pattern{0, 0, 0, 0}; | ||
| 1085 | auto controller = GetResourceManager()->GetNpad(); | ||
| 1086 | const auto result = controller->GetLedPattern(npad_id, pattern); | ||
| 1087 | |||
| 1088 | LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); | ||
| 1089 | |||
| 1090 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1091 | rb.Push(result); | ||
| 1092 | rb.Push(pattern.raw); | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) { | ||
| 1096 | IPC::RequestParser rp{ctx}; | ||
| 1097 | struct Parameters { | ||
| 1098 | NPad::NpadRevision revision; | ||
| 1099 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1100 | u64 applet_resource_user_id; | ||
| 1101 | }; | ||
| 1102 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1103 | |||
| 1104 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1105 | |||
| 1106 | LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision, | ||
| 1107 | parameters.applet_resource_user_id); | ||
| 1108 | |||
| 1109 | auto npad = GetResourceManager()->GetNpad(); | ||
| 1110 | |||
| 1111 | // TODO: npad->SetRevision(applet_resource_user_id, revision); | ||
| 1112 | const auto result = npad->Activate(parameters.applet_resource_user_id); | ||
| 1113 | |||
| 1114 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1115 | rb.Push(result); | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) { | ||
| 1119 | IPC::RequestParser rp{ctx}; | ||
| 1120 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1121 | const auto hold_type{rp.PopEnum<NPad::NpadJoyHoldType>()}; | ||
| 1122 | |||
| 1123 | GetResourceManager()->GetNpad()->SetHoldType(hold_type); | ||
| 1124 | |||
| 1125 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", | ||
| 1126 | applet_resource_user_id, hold_type); | ||
| 1127 | |||
| 1128 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1129 | rb.Push(ResultSuccess); | ||
| 1130 | } | ||
| 1131 | |||
| 1132 | void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) { | ||
| 1133 | IPC::RequestParser rp{ctx}; | ||
| 1134 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1135 | |||
| 1136 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1137 | |||
| 1138 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1139 | rb.Push(ResultSuccess); | ||
| 1140 | rb.PushEnum(GetResourceManager()->GetNpad()->GetHoldType()); | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) { | ||
| 1144 | IPC::RequestParser rp{ctx}; | ||
| 1145 | struct Parameters { | ||
| 1146 | Core::HID::NpadIdType npad_id; | ||
| 1147 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1148 | u64 applet_resource_user_id; | ||
| 1149 | }; | ||
| 1150 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1151 | |||
| 1152 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1153 | |||
| 1154 | Core::HID::NpadIdType new_npad_id{}; | ||
| 1155 | auto controller = GetResourceManager()->GetNpad(); | ||
| 1156 | controller->SetNpadMode(new_npad_id, parameters.npad_id, NPad::NpadJoyDeviceType::Left, | ||
| 1157 | NPad::NpadJoyAssignmentMode::Single); | ||
| 1158 | |||
| 1159 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | ||
| 1160 | parameters.applet_resource_user_id); | ||
| 1161 | |||
| 1162 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1163 | rb.Push(ResultSuccess); | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) { | ||
| 1167 | IPC::RequestParser rp{ctx}; | ||
| 1168 | struct Parameters { | ||
| 1169 | Core::HID::NpadIdType npad_id; | ||
| 1170 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1171 | u64 applet_resource_user_id; | ||
| 1172 | NPad::NpadJoyDeviceType npad_joy_device_type; | ||
| 1173 | }; | ||
| 1174 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1175 | |||
| 1176 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1177 | |||
| 1178 | Core::HID::NpadIdType new_npad_id{}; | ||
| 1179 | auto controller = GetResourceManager()->GetNpad(); | ||
| 1180 | controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, | ||
| 1181 | NPad::NpadJoyAssignmentMode::Single); | ||
| 1182 | |||
| 1183 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", | ||
| 1184 | parameters.npad_id, parameters.applet_resource_user_id, | ||
| 1185 | parameters.npad_joy_device_type); | ||
| 1186 | |||
| 1187 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1188 | rb.Push(ResultSuccess); | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) { | ||
| 1192 | IPC::RequestParser rp{ctx}; | ||
| 1193 | struct Parameters { | ||
| 1194 | Core::HID::NpadIdType npad_id; | ||
| 1195 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1196 | u64 applet_resource_user_id; | ||
| 1197 | }; | ||
| 1198 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1199 | |||
| 1200 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1201 | |||
| 1202 | Core::HID::NpadIdType new_npad_id{}; | ||
| 1203 | auto controller = GetResourceManager()->GetNpad(); | ||
| 1204 | controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NPad::NpadJoyAssignmentMode::Dual); | ||
| 1205 | |||
| 1206 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | ||
| 1207 | parameters.applet_resource_user_id); // Spams a lot when controller applet is open | ||
| 1208 | |||
| 1209 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1210 | rb.Push(ResultSuccess); | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | void IHidServer::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) { | ||
| 1214 | IPC::RequestParser rp{ctx}; | ||
| 1215 | const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 1216 | const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 1217 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1218 | |||
| 1219 | auto controller = GetResourceManager()->GetNpad(); | ||
| 1220 | const auto result = controller->MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); | ||
| 1221 | |||
| 1222 | LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", | ||
| 1223 | npad_id_1, npad_id_2, applet_resource_user_id); | ||
| 1224 | |||
| 1225 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1226 | rb.Push(result); | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | void IHidServer::StartLrAssignmentMode(HLERequestContext& ctx) { | ||
| 1230 | IPC::RequestParser rp{ctx}; | ||
| 1231 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1232 | |||
| 1233 | GetResourceManager()->GetNpad()->StartLRAssignmentMode(); | ||
| 1234 | |||
| 1235 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1236 | |||
| 1237 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1238 | rb.Push(ResultSuccess); | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) { | ||
| 1242 | IPC::RequestParser rp{ctx}; | ||
| 1243 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1244 | |||
| 1245 | GetResourceManager()->GetNpad()->StopLRAssignmentMode(); | ||
| 1246 | |||
| 1247 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1248 | |||
| 1249 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1250 | rb.Push(ResultSuccess); | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) { | ||
| 1254 | IPC::RequestParser rp{ctx}; | ||
| 1255 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1256 | const auto activation_mode{rp.PopEnum<NPad::NpadHandheldActivationMode>()}; | ||
| 1257 | |||
| 1258 | GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode); | ||
| 1259 | |||
| 1260 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", | ||
| 1261 | applet_resource_user_id, activation_mode); | ||
| 1262 | |||
| 1263 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1264 | rb.Push(ResultSuccess); | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) { | ||
| 1268 | IPC::RequestParser rp{ctx}; | ||
| 1269 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1270 | |||
| 1271 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1272 | |||
| 1273 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1274 | rb.Push(ResultSuccess); | ||
| 1275 | rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode()); | ||
| 1276 | } | ||
| 1277 | |||
| 1278 | void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) { | ||
| 1279 | IPC::RequestParser rp{ctx}; | ||
| 1280 | const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 1281 | const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 1282 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1283 | |||
| 1284 | auto controller = GetResourceManager()->GetNpad(); | ||
| 1285 | const auto result = controller->SwapNpadAssignment(npad_id_1, npad_id_2); | ||
| 1286 | |||
| 1287 | LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", | ||
| 1288 | npad_id_1, npad_id_2, applet_resource_user_id); | ||
| 1289 | |||
| 1290 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1291 | rb.Push(result); | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | void IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) { | ||
| 1295 | IPC::RequestParser rp{ctx}; | ||
| 1296 | struct Parameters { | ||
| 1297 | Core::HID::NpadIdType npad_id; | ||
| 1298 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1299 | u64 applet_resource_user_id; | ||
| 1300 | }; | ||
| 1301 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1302 | |||
| 1303 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1304 | |||
| 1305 | bool is_enabled = false; | ||
| 1306 | auto controller = GetResourceManager()->GetNpad(); | ||
| 1307 | const auto result = | ||
| 1308 | controller->IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); | ||
| 1309 | |||
| 1310 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", | ||
| 1311 | parameters.npad_id, parameters.applet_resource_user_id); | ||
| 1312 | |||
| 1313 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1314 | rb.Push(result); | ||
| 1315 | rb.Push(is_enabled); | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) { | ||
| 1319 | IPC::RequestParser rp{ctx}; | ||
| 1320 | struct Parameters { | ||
| 1321 | bool is_enabled; | ||
| 1322 | INSERT_PADDING_BYTES_NOINIT(3); | ||
| 1323 | Core::HID::NpadIdType npad_id; | ||
| 1324 | u64 applet_resource_user_id; | ||
| 1325 | }; | ||
| 1326 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1327 | |||
| 1328 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1329 | |||
| 1330 | auto controller = GetResourceManager()->GetNpad(); | ||
| 1331 | const auto result = controller->SetUnintendedHomeButtonInputProtectionEnabled( | ||
| 1332 | parameters.is_enabled, parameters.npad_id); | ||
| 1333 | |||
| 1334 | LOG_DEBUG(Service_HID, | ||
| 1335 | "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}", | ||
| 1336 | parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); | ||
| 1337 | |||
| 1338 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1339 | rb.Push(result); | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) { | ||
| 1343 | IPC::RequestParser rp{ctx}; | ||
| 1344 | struct Parameters { | ||
| 1345 | Core::HID::NpadIdType npad_id; | ||
| 1346 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1347 | u64 applet_resource_user_id; | ||
| 1348 | NPad::NpadJoyDeviceType npad_joy_device_type; | ||
| 1349 | }; | ||
| 1350 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1351 | |||
| 1352 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1353 | |||
| 1354 | Core::HID::NpadIdType new_npad_id{}; | ||
| 1355 | auto controller = GetResourceManager()->GetNpad(); | ||
| 1356 | const auto is_reassigned = | ||
| 1357 | controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, | ||
| 1358 | NPad::NpadJoyAssignmentMode::Single); | ||
| 1359 | |||
| 1360 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", | ||
| 1361 | parameters.npad_id, parameters.applet_resource_user_id, | ||
| 1362 | parameters.npad_joy_device_type); | ||
| 1363 | |||
| 1364 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1365 | rb.Push(ResultSuccess); | ||
| 1366 | rb.Push(is_reassigned); | ||
| 1367 | rb.PushEnum(new_npad_id); | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) { | ||
| 1371 | IPC::RequestParser rp{ctx}; | ||
| 1372 | struct Parameters { | ||
| 1373 | bool analog_stick_use_center_clamp; | ||
| 1374 | INSERT_PADDING_BYTES_NOINIT(7); | ||
| 1375 | u64 applet_resource_user_id; | ||
| 1376 | }; | ||
| 1377 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1378 | |||
| 1379 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1380 | |||
| 1381 | GetResourceManager()->GetNpad()->SetAnalogStickUseCenterClamp( | ||
| 1382 | parameters.analog_stick_use_center_clamp); | ||
| 1383 | |||
| 1384 | LOG_WARNING(Service_HID, | ||
| 1385 | "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", | ||
| 1386 | parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id); | ||
| 1387 | |||
| 1388 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1389 | rb.Push(ResultSuccess); | ||
| 1390 | } | ||
| 1391 | |||
| 1392 | void IHidServer::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) { | ||
| 1393 | IPC::RequestParser rp{ctx}; | ||
| 1394 | struct Parameters { | ||
| 1395 | Core::HID::NpadStyleSet npad_styleset; | ||
| 1396 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1397 | u64 applet_resource_user_id; | ||
| 1398 | Core::HID::NpadButton button; | ||
| 1399 | }; | ||
| 1400 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1401 | |||
| 1402 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1403 | |||
| 1404 | LOG_WARNING(Service_HID, | ||
| 1405 | "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}", | ||
| 1406 | parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button); | ||
| 1407 | |||
| 1408 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1409 | rb.Push(ResultSuccess); | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) { | ||
| 1413 | IPC::RequestParser rp{ctx}; | ||
| 1414 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1415 | |||
| 1416 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 1417 | applet_resource_user_id); | ||
| 1418 | |||
| 1419 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1420 | rb.Push(ResultSuccess); | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) { | ||
| 1424 | IPC::RequestParser rp{ctx}; | ||
| 1425 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; | ||
| 1426 | const auto controller = GetResourceManager()->GetNpad(); | ||
| 1427 | |||
| 1428 | Core::HID::VibrationDeviceInfo vibration_device_info; | ||
| 1429 | bool check_device_index = false; | ||
| 1430 | |||
| 1431 | switch (vibration_device_handle.npad_type) { | ||
| 1432 | case Core::HID::NpadStyleIndex::ProController: | ||
| 1433 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 1434 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 1435 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 1436 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 1437 | vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; | ||
| 1438 | check_device_index = true; | ||
| 1439 | break; | ||
| 1440 | case Core::HID::NpadStyleIndex::GameCube: | ||
| 1441 | vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; | ||
| 1442 | break; | ||
| 1443 | case Core::HID::NpadStyleIndex::N64: | ||
| 1444 | vibration_device_info.type = Core::HID::VibrationDeviceType::N64; | ||
| 1445 | break; | ||
| 1446 | default: | ||
| 1447 | vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; | ||
| 1448 | break; | ||
| 1449 | } | ||
| 1450 | |||
| 1451 | vibration_device_info.position = Core::HID::VibrationDevicePosition::None; | ||
| 1452 | if (check_device_index) { | ||
| 1453 | switch (vibration_device_handle.device_index) { | ||
| 1454 | case Core::HID::DeviceIndex::Left: | ||
| 1455 | vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; | ||
| 1456 | break; | ||
| 1457 | case Core::HID::DeviceIndex::Right: | ||
| 1458 | vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; | ||
| 1459 | break; | ||
| 1460 | case Core::HID::DeviceIndex::None: | ||
| 1461 | default: | ||
| 1462 | ASSERT_MSG(false, "DeviceIndex should never be None!"); | ||
| 1463 | break; | ||
| 1464 | } | ||
| 1465 | } | ||
| 1466 | |||
| 1467 | LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", | ||
| 1468 | vibration_device_info.type, vibration_device_info.position); | ||
| 1469 | |||
| 1470 | const auto result = IsVibrationHandleValid(vibration_device_handle); | ||
| 1471 | if (result.IsError()) { | ||
| 1472 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1473 | rb.Push(result); | ||
| 1474 | return; | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1478 | rb.Push(ResultSuccess); | ||
| 1479 | rb.PushRaw(vibration_device_info); | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | void IHidServer::SendVibrationValue(HLERequestContext& ctx) { | ||
| 1483 | IPC::RequestParser rp{ctx}; | ||
| 1484 | struct Parameters { | ||
| 1485 | Core::HID::VibrationDeviceHandle vibration_device_handle; | ||
| 1486 | Core::HID::VibrationValue vibration_value; | ||
| 1487 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1488 | u64 applet_resource_user_id; | ||
| 1489 | }; | ||
| 1490 | static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); | ||
| 1491 | |||
| 1492 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1493 | |||
| 1494 | GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle, | ||
| 1495 | parameters.vibration_value); | ||
| 1496 | |||
| 1497 | LOG_DEBUG(Service_HID, | ||
| 1498 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 1499 | parameters.vibration_device_handle.npad_type, | ||
| 1500 | parameters.vibration_device_handle.npad_id, | ||
| 1501 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); | ||
| 1502 | |||
| 1503 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1504 | rb.Push(ResultSuccess); | ||
| 1505 | } | ||
| 1506 | |||
| 1507 | void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) { | ||
| 1508 | IPC::RequestParser rp{ctx}; | ||
| 1509 | struct Parameters { | ||
| 1510 | Core::HID::VibrationDeviceHandle vibration_device_handle; | ||
| 1511 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1512 | u64 applet_resource_user_id; | ||
| 1513 | }; | ||
| 1514 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1515 | |||
| 1516 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1517 | |||
| 1518 | LOG_DEBUG(Service_HID, | ||
| 1519 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 1520 | parameters.vibration_device_handle.npad_type, | ||
| 1521 | parameters.vibration_device_handle.npad_id, | ||
| 1522 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); | ||
| 1523 | |||
| 1524 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 1525 | rb.Push(ResultSuccess); | ||
| 1526 | rb.PushRaw( | ||
| 1527 | GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle)); | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { | ||
| 1531 | LOG_DEBUG(Service_HID, "called"); | ||
| 1532 | |||
| 1533 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 1534 | rb.Push(ResultSuccess); | ||
| 1535 | rb.PushIpcInterface<IActiveVibrationDeviceList>(system, GetResourceManager()); | ||
| 1536 | } | ||
| 1537 | |||
| 1538 | void IHidServer::PermitVibration(HLERequestContext& ctx) { | ||
| 1539 | IPC::RequestParser rp{ctx}; | ||
| 1540 | const auto can_vibrate{rp.Pop<bool>()}; | ||
| 1541 | |||
| 1542 | // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value | ||
| 1543 | // by converting it to a bool | ||
| 1544 | Settings::values.vibration_enabled.SetValue(can_vibrate); | ||
| 1545 | |||
| 1546 | LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); | ||
| 1547 | |||
| 1548 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1549 | rb.Push(ResultSuccess); | ||
| 1550 | } | ||
| 1551 | |||
| 1552 | void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) { | ||
| 1553 | LOG_DEBUG(Service_HID, "called"); | ||
| 1554 | |||
| 1555 | // nnSDK checks if a float is greater than zero. We return the bool we stored earlier | ||
| 1556 | const auto is_enabled = Settings::values.vibration_enabled.GetValue(); | ||
| 1557 | |||
| 1558 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1559 | rb.Push(ResultSuccess); | ||
| 1560 | rb.Push(is_enabled); | ||
| 1561 | } | ||
| 1562 | |||
| 1563 | void IHidServer::SendVibrationValues(HLERequestContext& ctx) { | ||
| 1564 | IPC::RequestParser rp{ctx}; | ||
| 1565 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1566 | |||
| 1567 | const auto handle_data = ctx.ReadBuffer(0); | ||
| 1568 | const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0); | ||
| 1569 | const auto vibration_data = ctx.ReadBuffer(1); | ||
| 1570 | const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1); | ||
| 1571 | |||
| 1572 | auto vibration_device_handles = | ||
| 1573 | std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()), | ||
| 1574 | handle_count); | ||
| 1575 | auto vibration_values = std::span( | ||
| 1576 | reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count); | ||
| 1577 | |||
| 1578 | GetResourceManager()->GetNpad()->VibrateControllers(vibration_device_handles, vibration_values); | ||
| 1579 | |||
| 1580 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1581 | |||
| 1582 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1583 | rb.Push(ResultSuccess); | ||
| 1584 | } | ||
| 1585 | |||
| 1586 | void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) { | ||
| 1587 | IPC::RequestParser rp{ctx}; | ||
| 1588 | struct Parameters { | ||
| 1589 | Core::HID::VibrationDeviceHandle vibration_device_handle; | ||
| 1590 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1591 | u64 applet_resource_user_id; | ||
| 1592 | Core::HID::VibrationGcErmCommand gc_erm_command; | ||
| 1593 | }; | ||
| 1594 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||
| 1595 | |||
| 1596 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1597 | |||
| 1598 | /** | ||
| 1599 | * Note: This uses yuzu-specific behavior such that the StopHard command produces | ||
| 1600 | * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below, | ||
| 1601 | * in order to differentiate between Stop and StopHard commands. | ||
| 1602 | * This is done to reuse the controller vibration functions made for regular controllers. | ||
| 1603 | */ | ||
| 1604 | const auto vibration_value = [parameters] { | ||
| 1605 | switch (parameters.gc_erm_command) { | ||
| 1606 | case Core::HID::VibrationGcErmCommand::Stop: | ||
| 1607 | return Core::HID::VibrationValue{ | ||
| 1608 | .low_amplitude = 0.0f, | ||
| 1609 | .low_frequency = 160.0f, | ||
| 1610 | .high_amplitude = 0.0f, | ||
| 1611 | .high_frequency = 320.0f, | ||
| 1612 | }; | ||
| 1613 | case Core::HID::VibrationGcErmCommand::Start: | ||
| 1614 | return Core::HID::VibrationValue{ | ||
| 1615 | .low_amplitude = 1.0f, | ||
| 1616 | .low_frequency = 160.0f, | ||
| 1617 | .high_amplitude = 1.0f, | ||
| 1618 | .high_frequency = 320.0f, | ||
| 1619 | }; | ||
| 1620 | case Core::HID::VibrationGcErmCommand::StopHard: | ||
| 1621 | return Core::HID::VibrationValue{ | ||
| 1622 | .low_amplitude = 0.0f, | ||
| 1623 | .low_frequency = 0.0f, | ||
| 1624 | .high_amplitude = 0.0f, | ||
| 1625 | .high_frequency = 0.0f, | ||
| 1626 | }; | ||
| 1627 | default: | ||
| 1628 | return Core::HID::DEFAULT_VIBRATION_VALUE; | ||
| 1629 | } | ||
| 1630 | }(); | ||
| 1631 | |||
| 1632 | GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle, | ||
| 1633 | vibration_value); | ||
| 1634 | |||
| 1635 | LOG_DEBUG(Service_HID, | ||
| 1636 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " | ||
| 1637 | "gc_erm_command={}", | ||
| 1638 | parameters.vibration_device_handle.npad_type, | ||
| 1639 | parameters.vibration_device_handle.npad_id, | ||
| 1640 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id, | ||
| 1641 | parameters.gc_erm_command); | ||
| 1642 | |||
| 1643 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1644 | rb.Push(ResultSuccess); | ||
| 1645 | } | ||
| 1646 | |||
| 1647 | void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) { | ||
| 1648 | IPC::RequestParser rp{ctx}; | ||
| 1649 | struct Parameters { | ||
| 1650 | Core::HID::VibrationDeviceHandle vibration_device_handle; | ||
| 1651 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1652 | u64 applet_resource_user_id; | ||
| 1653 | }; | ||
| 1654 | |||
| 1655 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1656 | |||
| 1657 | const auto last_vibration = | ||
| 1658 | GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle); | ||
| 1659 | |||
| 1660 | const auto gc_erm_command = [last_vibration] { | ||
| 1661 | if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { | ||
| 1662 | return Core::HID::VibrationGcErmCommand::Start; | ||
| 1663 | } | ||
| 1664 | |||
| 1665 | /** | ||
| 1666 | * Note: This uses yuzu-specific behavior such that the StopHard command produces | ||
| 1667 | * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function | ||
| 1668 | * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. | ||
| 1669 | * This is done to reuse the controller vibration functions made for regular controllers. | ||
| 1670 | */ | ||
| 1671 | if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) { | ||
| 1672 | return Core::HID::VibrationGcErmCommand::StopHard; | ||
| 1673 | } | ||
| 1674 | |||
| 1675 | return Core::HID::VibrationGcErmCommand::Stop; | ||
| 1676 | }(); | ||
| 1677 | |||
| 1678 | LOG_DEBUG(Service_HID, | ||
| 1679 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 1680 | parameters.vibration_device_handle.npad_type, | ||
| 1681 | parameters.vibration_device_handle.npad_id, | ||
| 1682 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); | ||
| 1683 | |||
| 1684 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1685 | rb.Push(ResultSuccess); | ||
| 1686 | rb.PushEnum(gc_erm_command); | ||
| 1687 | } | ||
| 1688 | |||
| 1689 | void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) { | ||
| 1690 | IPC::RequestParser rp{ctx}; | ||
| 1691 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1692 | |||
| 1693 | GetResourceManager()->GetNpad()->SetPermitVibrationSession(true); | ||
| 1694 | |||
| 1695 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1696 | |||
| 1697 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1698 | rb.Push(ResultSuccess); | ||
| 1699 | } | ||
| 1700 | |||
| 1701 | void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) { | ||
| 1702 | GetResourceManager()->GetNpad()->SetPermitVibrationSession(false); | ||
| 1703 | |||
| 1704 | LOG_DEBUG(Service_HID, "called"); | ||
| 1705 | |||
| 1706 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1707 | rb.Push(ResultSuccess); | ||
| 1708 | } | ||
| 1709 | |||
| 1710 | void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) { | ||
| 1711 | IPC::RequestParser rp{ctx}; | ||
| 1712 | struct Parameters { | ||
| 1713 | Core::HID::VibrationDeviceHandle vibration_device_handle; | ||
| 1714 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1715 | u64 applet_resource_user_id; | ||
| 1716 | }; | ||
| 1717 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1718 | |||
| 1719 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1720 | |||
| 1721 | LOG_DEBUG(Service_HID, | ||
| 1722 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | ||
| 1723 | parameters.vibration_device_handle.npad_type, | ||
| 1724 | parameters.vibration_device_handle.npad_id, | ||
| 1725 | parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); | ||
| 1726 | |||
| 1727 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1728 | rb.Push(ResultSuccess); | ||
| 1729 | rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted( | ||
| 1730 | parameters.vibration_device_handle)); | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { | ||
| 1734 | IPC::RequestParser rp{ctx}; | ||
| 1735 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1736 | |||
| 1737 | LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1738 | |||
| 1739 | Result result = ResultSuccess; | ||
| 1740 | auto console_sixaxis = GetResourceManager()->GetConsoleSixAxis(); | ||
| 1741 | |||
| 1742 | if (!firmware_settings->IsDeviceManaged()) { | ||
| 1743 | result = console_sixaxis->Activate(); | ||
| 1744 | } | ||
| 1745 | |||
| 1746 | if (result.IsSuccess()) { | ||
| 1747 | result = console_sixaxis->Activate(applet_resource_user_id); | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1751 | rb.Push(result); | ||
| 1752 | } | ||
| 1753 | |||
| 1754 | void IHidServer::StartConsoleSixAxisSensor(HLERequestContext& ctx) { | ||
| 1755 | IPC::RequestParser rp{ctx}; | ||
| 1756 | struct Parameters { | ||
| 1757 | Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; | ||
| 1758 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1759 | u64 applet_resource_user_id; | ||
| 1760 | }; | ||
| 1761 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1762 | |||
| 1763 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1764 | |||
| 1765 | LOG_WARNING(Service_HID, | ||
| 1766 | "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", | ||
| 1767 | parameters.console_sixaxis_handle.unknown_1, | ||
| 1768 | parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); | ||
| 1769 | |||
| 1770 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1771 | rb.Push(ResultSuccess); | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | void IHidServer::StopConsoleSixAxisSensor(HLERequestContext& ctx) { | ||
| 1775 | IPC::RequestParser rp{ctx}; | ||
| 1776 | struct Parameters { | ||
| 1777 | Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; | ||
| 1778 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1779 | u64 applet_resource_user_id; | ||
| 1780 | }; | ||
| 1781 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1782 | |||
| 1783 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1784 | |||
| 1785 | LOG_WARNING(Service_HID, | ||
| 1786 | "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", | ||
| 1787 | parameters.console_sixaxis_handle.unknown_1, | ||
| 1788 | parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); | ||
| 1789 | |||
| 1790 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1791 | rb.Push(ResultSuccess); | ||
| 1792 | } | ||
| 1793 | |||
| 1794 | void IHidServer::ActivateSevenSixAxisSensor(HLERequestContext& ctx) { | ||
| 1795 | IPC::RequestParser rp{ctx}; | ||
| 1796 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1797 | |||
| 1798 | LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1799 | |||
| 1800 | Result result = ResultSuccess; | ||
| 1801 | auto seven_sixaxis = GetResourceManager()->GetSevenSixAxis(); | ||
| 1802 | |||
| 1803 | if (!firmware_settings->IsDeviceManaged()) { | ||
| 1804 | result = seven_sixaxis->Activate(); | ||
| 1805 | } | ||
| 1806 | |||
| 1807 | if (result.IsSuccess()) { | ||
| 1808 | seven_sixaxis->Activate(applet_resource_user_id); | ||
| 1809 | } | ||
| 1810 | |||
| 1811 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1812 | rb.Push(ResultSuccess); | ||
| 1813 | } | ||
| 1814 | |||
| 1815 | void IHidServer::StartSevenSixAxisSensor(HLERequestContext& ctx) { | ||
| 1816 | IPC::RequestParser rp{ctx}; | ||
| 1817 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1818 | |||
| 1819 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 1820 | applet_resource_user_id); | ||
| 1821 | |||
| 1822 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1823 | rb.Push(ResultSuccess); | ||
| 1824 | } | ||
| 1825 | |||
| 1826 | void IHidServer::StopSevenSixAxisSensor(HLERequestContext& ctx) { | ||
| 1827 | IPC::RequestParser rp{ctx}; | ||
| 1828 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1829 | |||
| 1830 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 1831 | applet_resource_user_id); | ||
| 1832 | |||
| 1833 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1834 | rb.Push(ResultSuccess); | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) { | ||
| 1838 | IPC::RequestParser rp{ctx}; | ||
| 1839 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1840 | const auto t_mem_1_size{rp.Pop<u64>()}; | ||
| 1841 | const auto t_mem_2_size{rp.Pop<u64>()}; | ||
| 1842 | const auto t_mem_1_handle{ctx.GetCopyHandle(0)}; | ||
| 1843 | const auto t_mem_2_handle{ctx.GetCopyHandle(1)}; | ||
| 1844 | |||
| 1845 | ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes"); | ||
| 1846 | ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes"); | ||
| 1847 | |||
| 1848 | auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 1849 | t_mem_1_handle); | ||
| 1850 | |||
| 1851 | if (t_mem_1.IsNull()) { | ||
| 1852 | LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle); | ||
| 1853 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1854 | rb.Push(ResultUnknown); | ||
| 1855 | return; | ||
| 1856 | } | ||
| 1857 | |||
| 1858 | auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 1859 | t_mem_2_handle); | ||
| 1860 | |||
| 1861 | if (t_mem_2.IsNull()) { | ||
| 1862 | LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle); | ||
| 1863 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1864 | rb.Push(ResultUnknown); | ||
| 1865 | return; | ||
| 1866 | } | ||
| 1867 | |||
| 1868 | ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size"); | ||
| 1869 | ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size"); | ||
| 1870 | |||
| 1871 | // Activate console six axis controller | ||
| 1872 | GetResourceManager()->GetConsoleSixAxis()->Activate(); | ||
| 1873 | GetResourceManager()->GetSevenSixAxis()->Activate(); | ||
| 1874 | |||
| 1875 | GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress()); | ||
| 1876 | |||
| 1877 | LOG_WARNING(Service_HID, | ||
| 1878 | "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, " | ||
| 1879 | "applet_resource_user_id={}", | ||
| 1880 | t_mem_1_handle, t_mem_2_handle, applet_resource_user_id); | ||
| 1881 | |||
| 1882 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1883 | rb.Push(ResultSuccess); | ||
| 1884 | } | ||
| 1885 | |||
| 1886 | void IHidServer::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) { | ||
| 1887 | IPC::RequestParser rp{ctx}; | ||
| 1888 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1889 | |||
| 1890 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 1891 | applet_resource_user_id); | ||
| 1892 | |||
| 1893 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1894 | rb.Push(ResultSuccess); | ||
| 1895 | } | ||
| 1896 | |||
| 1897 | void IHidServer::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) { | ||
| 1898 | IPC::RequestParser rp{ctx}; | ||
| 1899 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1900 | |||
| 1901 | GetResourceManager()->GetSevenSixAxis()->ResetTimestamp(); | ||
| 1902 | |||
| 1903 | LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 1904 | |||
| 1905 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1906 | rb.Push(ResultSuccess); | ||
| 1907 | } | ||
| 1908 | |||
| 1909 | void IHidServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { | ||
| 1910 | IPC::RequestParser rp{ctx}; | ||
| 1911 | |||
| 1912 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 1913 | |||
| 1914 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1915 | rb.Push(ResultSuccess); | ||
| 1916 | rb.Push(false); | ||
| 1917 | } | ||
| 1918 | |||
| 1919 | void IHidServer::GetPalmaConnectionHandle(HLERequestContext& ctx) { | ||
| 1920 | IPC::RequestParser rp{ctx}; | ||
| 1921 | struct Parameters { | ||
| 1922 | Core::HID::NpadIdType npad_id; | ||
| 1923 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1924 | u64 applet_resource_user_id; | ||
| 1925 | }; | ||
| 1926 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1927 | |||
| 1928 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1929 | |||
| 1930 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", | ||
| 1931 | parameters.npad_id, parameters.applet_resource_user_id); | ||
| 1932 | |||
| 1933 | Palma::PalmaConnectionHandle handle; | ||
| 1934 | auto controller = GetResourceManager()->GetPalma(); | ||
| 1935 | const auto result = controller->GetPalmaConnectionHandle(parameters.npad_id, handle); | ||
| 1936 | |||
| 1937 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1938 | rb.Push(result); | ||
| 1939 | rb.PushRaw(handle); | ||
| 1940 | } | ||
| 1941 | |||
| 1942 | void IHidServer::InitializePalma(HLERequestContext& ctx) { | ||
| 1943 | IPC::RequestParser rp{ctx}; | ||
| 1944 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 1945 | |||
| 1946 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 1947 | |||
| 1948 | auto controller = GetResourceManager()->GetPalma(); | ||
| 1949 | const auto result = controller->InitializePalma(connection_handle); | ||
| 1950 | |||
| 1951 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1952 | rb.Push(result); | ||
| 1953 | } | ||
| 1954 | |||
| 1955 | void IHidServer::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) { | ||
| 1956 | IPC::RequestParser rp{ctx}; | ||
| 1957 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 1958 | |||
| 1959 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 1960 | |||
| 1961 | auto controller = GetResourceManager()->GetPalma(); | ||
| 1962 | |||
| 1963 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 1964 | rb.Push(ResultSuccess); | ||
| 1965 | rb.PushCopyObjects(controller->AcquirePalmaOperationCompleteEvent(connection_handle)); | ||
| 1966 | } | ||
| 1967 | |||
| 1968 | void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) { | ||
| 1969 | IPC::RequestParser rp{ctx}; | ||
| 1970 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 1971 | |||
| 1972 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 1973 | |||
| 1974 | Palma::PalmaOperationType operation_type; | ||
| 1975 | Palma::PalmaOperationData data; | ||
| 1976 | auto controller = GetResourceManager()->GetPalma(); | ||
| 1977 | const auto result = controller->GetPalmaOperationInfo(connection_handle, operation_type, data); | ||
| 1978 | |||
| 1979 | if (result.IsError()) { | ||
| 1980 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1981 | rb.Push(result); | ||
| 1982 | } | ||
| 1983 | |||
| 1984 | ctx.WriteBuffer(data); | ||
| 1985 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1986 | rb.Push(result); | ||
| 1987 | rb.Push(static_cast<u64>(operation_type)); | ||
| 1988 | } | ||
| 1989 | |||
| 1990 | void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) { | ||
| 1991 | IPC::RequestParser rp{ctx}; | ||
| 1992 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 1993 | const auto palma_activity{rp.Pop<u64>()}; | ||
| 1994 | |||
| 1995 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", | ||
| 1996 | connection_handle.npad_id, palma_activity); | ||
| 1997 | |||
| 1998 | auto controller = GetResourceManager()->GetPalma(); | ||
| 1999 | const auto result = controller->PlayPalmaActivity(connection_handle, palma_activity); | ||
| 2000 | |||
| 2001 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2002 | rb.Push(result); | ||
| 2003 | } | ||
| 2004 | |||
| 2005 | void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) { | ||
| 2006 | IPC::RequestParser rp{ctx}; | ||
| 2007 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 2008 | const auto fr_mode{rp.PopEnum<Palma::PalmaFrModeType>()}; | ||
| 2009 | |||
| 2010 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", | ||
| 2011 | connection_handle.npad_id, fr_mode); | ||
| 2012 | |||
| 2013 | auto controller = GetResourceManager()->GetPalma(); | ||
| 2014 | const auto result = controller->SetPalmaFrModeType(connection_handle, fr_mode); | ||
| 2015 | |||
| 2016 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2017 | rb.Push(result); | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | void IHidServer::ReadPalmaStep(HLERequestContext& ctx) { | ||
| 2021 | IPC::RequestParser rp{ctx}; | ||
| 2022 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 2023 | |||
| 2024 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2025 | |||
| 2026 | auto controller = GetResourceManager()->GetPalma(); | ||
| 2027 | const auto result = controller->ReadPalmaStep(connection_handle); | ||
| 2028 | |||
| 2029 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2030 | rb.Push(result); | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | void IHidServer::EnablePalmaStep(HLERequestContext& ctx) { | ||
| 2034 | IPC::RequestParser rp{ctx}; | ||
| 2035 | struct Parameters { | ||
| 2036 | bool is_enabled; | ||
| 2037 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 2038 | Palma::PalmaConnectionHandle connection_handle; | ||
| 2039 | }; | ||
| 2040 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 2041 | |||
| 2042 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 2043 | |||
| 2044 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", | ||
| 2045 | parameters.connection_handle.npad_id, parameters.is_enabled); | ||
| 2046 | |||
| 2047 | auto controller = GetResourceManager()->GetPalma(); | ||
| 2048 | const auto result = | ||
| 2049 | controller->EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); | ||
| 2050 | |||
| 2051 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2052 | rb.Push(result); | ||
| 2053 | } | ||
| 2054 | |||
| 2055 | void IHidServer::ResetPalmaStep(HLERequestContext& ctx) { | ||
| 2056 | IPC::RequestParser rp{ctx}; | ||
| 2057 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 2058 | |||
| 2059 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2060 | |||
| 2061 | auto controller = GetResourceManager()->GetPalma(); | ||
| 2062 | const auto result = controller->ResetPalmaStep(connection_handle); | ||
| 2063 | |||
| 2064 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2065 | rb.Push(result); | ||
| 2066 | } | ||
| 2067 | |||
| 2068 | void IHidServer::ReadPalmaApplicationSection(HLERequestContext& ctx) { | ||
| 2069 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2070 | |||
| 2071 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2072 | rb.Push(ResultSuccess); | ||
| 2073 | } | ||
| 2074 | |||
| 2075 | void IHidServer::WritePalmaApplicationSection(HLERequestContext& ctx) { | ||
| 2076 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2077 | |||
| 2078 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2079 | rb.Push(ResultSuccess); | ||
| 2080 | } | ||
| 2081 | |||
| 2082 | void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) { | ||
| 2083 | IPC::RequestParser rp{ctx}; | ||
| 2084 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 2085 | |||
| 2086 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2087 | |||
| 2088 | GetResourceManager()->GetPalma()->ReadPalmaUniqueCode(connection_handle); | ||
| 2089 | |||
| 2090 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2091 | rb.Push(ResultSuccess); | ||
| 2092 | } | ||
| 2093 | |||
| 2094 | void IHidServer::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) { | ||
| 2095 | IPC::RequestParser rp{ctx}; | ||
| 2096 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 2097 | |||
| 2098 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2099 | |||
| 2100 | GetResourceManager()->GetPalma()->SetPalmaUniqueCodeInvalid(connection_handle); | ||
| 2101 | |||
| 2102 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2103 | rb.Push(ResultSuccess); | ||
| 2104 | } | ||
| 2105 | |||
| 2106 | void IHidServer::WritePalmaActivityEntry(HLERequestContext& ctx) { | ||
| 2107 | LOG_CRITICAL(Service_HID, "(STUBBED) called"); | ||
| 2108 | |||
| 2109 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2110 | rb.Push(ResultSuccess); | ||
| 2111 | } | ||
| 2112 | |||
| 2113 | void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) { | ||
| 2114 | IPC::RequestParser rp{ctx}; | ||
| 2115 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 2116 | const auto unknown{rp.Pop<u64>()}; | ||
| 2117 | |||
| 2118 | [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); | ||
| 2119 | |||
| 2120 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", | ||
| 2121 | connection_handle.npad_id, unknown); | ||
| 2122 | |||
| 2123 | GetResourceManager()->GetPalma()->WritePalmaRgbLedPatternEntry(connection_handle, unknown); | ||
| 2124 | |||
| 2125 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2126 | rb.Push(ResultSuccess); | ||
| 2127 | } | ||
| 2128 | |||
| 2129 | void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) { | ||
| 2130 | IPC::RequestParser rp{ctx}; | ||
| 2131 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 2132 | const auto wave_set{rp.PopEnum<Palma::PalmaWaveSet>()}; | ||
| 2133 | const auto unknown{rp.Pop<u64>()}; | ||
| 2134 | const auto t_mem_size{rp.Pop<u64>()}; | ||
| 2135 | const auto t_mem_handle{ctx.GetCopyHandle(0)}; | ||
| 2136 | const auto size{rp.Pop<u64>()}; | ||
| 2137 | |||
| 2138 | ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); | ||
| 2139 | |||
| 2140 | auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 2141 | t_mem_handle); | ||
| 2142 | |||
| 2143 | if (t_mem.IsNull()) { | ||
| 2144 | LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); | ||
| 2145 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2146 | rb.Push(ResultUnknown); | ||
| 2147 | return; | ||
| 2148 | } | ||
| 2149 | |||
| 2150 | ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); | ||
| 2151 | |||
| 2152 | LOG_WARNING(Service_HID, | ||
| 2153 | "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, " | ||
| 2154 | "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", | ||
| 2155 | connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); | ||
| 2156 | |||
| 2157 | GetResourceManager()->GetPalma()->WritePalmaWaveEntry(connection_handle, wave_set, | ||
| 2158 | t_mem->GetSourceAddress(), t_mem_size); | ||
| 2159 | |||
| 2160 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2161 | rb.Push(ResultSuccess); | ||
| 2162 | } | ||
| 2163 | |||
| 2164 | void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { | ||
| 2165 | IPC::RequestParser rp{ctx}; | ||
| 2166 | struct Parameters { | ||
| 2167 | s32 database_id_version; | ||
| 2168 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 2169 | Palma::PalmaConnectionHandle connection_handle; | ||
| 2170 | }; | ||
| 2171 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 2172 | |||
| 2173 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 2174 | |||
| 2175 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", | ||
| 2176 | parameters.connection_handle.npad_id, parameters.database_id_version); | ||
| 2177 | |||
| 2178 | GetResourceManager()->GetPalma()->SetPalmaDataBaseIdentificationVersion( | ||
| 2179 | parameters.connection_handle, parameters.database_id_version); | ||
| 2180 | |||
| 2181 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2182 | rb.Push(ResultSuccess); | ||
| 2183 | } | ||
| 2184 | |||
| 2185 | void IHidServer::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { | ||
| 2186 | IPC::RequestParser rp{ctx}; | ||
| 2187 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 2188 | |||
| 2189 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2190 | |||
| 2191 | GetResourceManager()->GetPalma()->GetPalmaDataBaseIdentificationVersion(connection_handle); | ||
| 2192 | |||
| 2193 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2194 | rb.Push(ResultSuccess); | ||
| 2195 | } | ||
| 2196 | |||
| 2197 | void IHidServer::SuspendPalmaFeature(HLERequestContext& ctx) { | ||
| 2198 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2199 | |||
| 2200 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2201 | rb.Push(ResultSuccess); | ||
| 2202 | } | ||
| 2203 | |||
| 2204 | void IHidServer::GetPalmaOperationResult(HLERequestContext& ctx) { | ||
| 2205 | IPC::RequestParser rp{ctx}; | ||
| 2206 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 2207 | |||
| 2208 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2209 | |||
| 2210 | const auto result = | ||
| 2211 | GetResourceManager()->GetPalma()->GetPalmaOperationResult(connection_handle); | ||
| 2212 | |||
| 2213 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2214 | rb.Push(result); | ||
| 2215 | } | ||
| 2216 | |||
| 2217 | void IHidServer::ReadPalmaPlayLog(HLERequestContext& ctx) { | ||
| 2218 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2219 | |||
| 2220 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2221 | rb.Push(ResultSuccess); | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | void IHidServer::ResetPalmaPlayLog(HLERequestContext& ctx) { | ||
| 2225 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2226 | |||
| 2227 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2228 | rb.Push(ResultSuccess); | ||
| 2229 | } | ||
| 2230 | |||
| 2231 | void IHidServer::SetIsPalmaAllConnectable(HLERequestContext& ctx) { | ||
| 2232 | IPC::RequestParser rp{ctx}; | ||
| 2233 | struct Parameters { | ||
| 2234 | bool is_palma_all_connectable; | ||
| 2235 | INSERT_PADDING_BYTES_NOINIT(7); | ||
| 2236 | u64 applet_resource_user_id; | ||
| 2237 | }; | ||
| 2238 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 2239 | |||
| 2240 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 2241 | |||
| 2242 | LOG_WARNING(Service_HID, | ||
| 2243 | "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", | ||
| 2244 | parameters.is_palma_all_connectable, parameters.applet_resource_user_id); | ||
| 2245 | |||
| 2246 | GetResourceManager()->GetPalma()->SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); | ||
| 2247 | |||
| 2248 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2249 | rb.Push(ResultSuccess); | ||
| 2250 | } | ||
| 2251 | |||
| 2252 | void IHidServer::SetIsPalmaPairedConnectable(HLERequestContext& ctx) { | ||
| 2253 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2254 | |||
| 2255 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2256 | rb.Push(ResultSuccess); | ||
| 2257 | } | ||
| 2258 | |||
| 2259 | void IHidServer::PairPalma(HLERequestContext& ctx) { | ||
| 2260 | IPC::RequestParser rp{ctx}; | ||
| 2261 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; | ||
| 2262 | |||
| 2263 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2264 | |||
| 2265 | GetResourceManager()->GetPalma()->PairPalma(connection_handle); | ||
| 2266 | |||
| 2267 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2268 | rb.Push(ResultSuccess); | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | void IHidServer::SetPalmaBoostMode(HLERequestContext& ctx) { | ||
| 2272 | IPC::RequestParser rp{ctx}; | ||
| 2273 | const auto palma_boost_mode{rp.Pop<bool>()}; | ||
| 2274 | |||
| 2275 | LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); | ||
| 2276 | |||
| 2277 | GetResourceManager()->GetPalma()->SetPalmaBoostMode(palma_boost_mode); | ||
| 2278 | |||
| 2279 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2280 | rb.Push(ResultSuccess); | ||
| 2281 | } | ||
| 2282 | |||
| 2283 | void IHidServer::CancelWritePalmaWaveEntry(HLERequestContext& ctx) { | ||
| 2284 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2285 | |||
| 2286 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2287 | rb.Push(ResultSuccess); | ||
| 2288 | } | ||
| 2289 | |||
| 2290 | void IHidServer::EnablePalmaBoostMode(HLERequestContext& ctx) { | ||
| 2291 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2292 | |||
| 2293 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2294 | rb.Push(ResultSuccess); | ||
| 2295 | } | ||
| 2296 | |||
| 2297 | void IHidServer::GetPalmaBluetoothAddress(HLERequestContext& ctx) { | ||
| 2298 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2299 | |||
| 2300 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2301 | rb.Push(ResultSuccess); | ||
| 2302 | } | ||
| 2303 | |||
| 2304 | void IHidServer::SetDisallowedPalmaConnection(HLERequestContext& ctx) { | ||
| 2305 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2306 | |||
| 2307 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2308 | rb.Push(ResultSuccess); | ||
| 2309 | } | ||
| 2310 | |||
| 2311 | void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) { | ||
| 2312 | IPC::RequestParser rp{ctx}; | ||
| 2313 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 2314 | const auto communication_mode{rp.PopEnum<NPad::NpadCommunicationMode>()}; | ||
| 2315 | |||
| 2316 | GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode); | ||
| 2317 | |||
| 2318 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}", | ||
| 2319 | applet_resource_user_id, communication_mode); | ||
| 2320 | |||
| 2321 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2322 | rb.Push(ResultSuccess); | ||
| 2323 | } | ||
| 2324 | |||
| 2325 | void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) { | ||
| 2326 | IPC::RequestParser rp{ctx}; | ||
| 2327 | |||
| 2328 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2329 | |||
| 2330 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 2331 | rb.Push(ResultSuccess); | ||
| 2332 | rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadCommunicationMode()); | ||
| 2333 | } | ||
| 2334 | |||
| 2335 | void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { | ||
| 2336 | IPC::RequestParser rp{ctx}; | ||
| 2337 | const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()}; | ||
| 2338 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 2339 | |||
| 2340 | LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", | ||
| 2341 | touchscreen_mode.mode, applet_resource_user_id); | ||
| 2342 | |||
| 2343 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2344 | rb.Push(ResultSuccess); | ||
| 2345 | } | ||
| 2346 | |||
| 2347 | void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) { | ||
| 2348 | IPC::RequestParser rp{ctx}; | ||
| 2349 | struct Parameters { | ||
| 2350 | s32 unknown; | ||
| 2351 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 2352 | u64 applet_resource_user_id; | ||
| 2353 | }; | ||
| 2354 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 2355 | |||
| 2356 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 2357 | |||
| 2358 | LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", | ||
| 2359 | parameters.unknown, parameters.applet_resource_user_id); | ||
| 2360 | |||
| 2361 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 2362 | rb.Push(ResultSuccess); | ||
| 2363 | rb.Push(false); | ||
| 2364 | } | ||
| 2365 | |||
| 2366 | std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() { | ||
| 2367 | resource_manager->Initialize(); | ||
| 2368 | return resource_manager; | ||
| 2369 | } | ||
| 2370 | |||
| 2371 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h new file mode 100644 index 000000000..eb2e8e7f4 --- /dev/null +++ b/src/core/hle/service/hid/hid_server.h | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/service.h" | ||
| 7 | |||
| 8 | namespace Core { | ||
| 9 | class System; | ||
| 10 | } | ||
| 11 | |||
| 12 | namespace Service::HID { | ||
| 13 | class ResourceManager; | ||
| 14 | class HidFirmwareSettings; | ||
| 15 | |||
| 16 | class IHidServer final : public ServiceFramework<IHidServer> { | ||
| 17 | public: | ||
| 18 | explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, | ||
| 19 | std::shared_ptr<HidFirmwareSettings> settings); | ||
| 20 | ~IHidServer() override; | ||
| 21 | |||
| 22 | std::shared_ptr<ResourceManager> GetResourceManager(); | ||
| 23 | |||
| 24 | private: | ||
| 25 | void CreateAppletResource(HLERequestContext& ctx); | ||
| 26 | void ActivateDebugPad(HLERequestContext& ctx); | ||
| 27 | void ActivateTouchScreen(HLERequestContext& ctx); | ||
| 28 | void ActivateMouse(HLERequestContext& ctx); | ||
| 29 | void ActivateKeyboard(HLERequestContext& ctx); | ||
| 30 | void SendKeyboardLockKeyEvent(HLERequestContext& ctx); | ||
| 31 | void AcquireXpadIdEventHandle(HLERequestContext& ctx); | ||
| 32 | void ReleaseXpadIdEventHandle(HLERequestContext& ctx); | ||
| 33 | void ActivateXpad(HLERequestContext& ctx); | ||
| 34 | void GetXpadIds(HLERequestContext& ctx); | ||
| 35 | void ActivateJoyXpad(HLERequestContext& ctx); | ||
| 36 | void GetJoyXpadLifoHandle(HLERequestContext& ctx); | ||
| 37 | void GetJoyXpadIds(HLERequestContext& ctx); | ||
| 38 | void ActivateSixAxisSensor(HLERequestContext& ctx); | ||
| 39 | void DeactivateSixAxisSensor(HLERequestContext& ctx); | ||
| 40 | void GetSixAxisSensorLifoHandle(HLERequestContext& ctx); | ||
| 41 | void ActivateJoySixAxisSensor(HLERequestContext& ctx); | ||
| 42 | void DeactivateJoySixAxisSensor(HLERequestContext& ctx); | ||
| 43 | void GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx); | ||
| 44 | void StartSixAxisSensor(HLERequestContext& ctx); | ||
| 45 | void StopSixAxisSensor(HLERequestContext& ctx); | ||
| 46 | void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx); | ||
| 47 | void EnableSixAxisSensorFusion(HLERequestContext& ctx); | ||
| 48 | void SetSixAxisSensorFusionParameters(HLERequestContext& ctx); | ||
| 49 | void GetSixAxisSensorFusionParameters(HLERequestContext& ctx); | ||
| 50 | void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx); | ||
| 51 | void SetGyroscopeZeroDriftMode(HLERequestContext& ctx); | ||
| 52 | void GetGyroscopeZeroDriftMode(HLERequestContext& ctx); | ||
| 53 | void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx); | ||
| 54 | void IsSixAxisSensorAtRest(HLERequestContext& ctx); | ||
| 55 | void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx); | ||
| 56 | void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx); | ||
| 57 | void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx); | ||
| 58 | void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx); | ||
| 59 | void GetSixAxisSensorIcInformation(HLERequestContext& ctx); | ||
| 60 | void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx); | ||
| 61 | void ActivateGesture(HLERequestContext& ctx); | ||
| 62 | void SetSupportedNpadStyleSet(HLERequestContext& ctx); | ||
| 63 | void GetSupportedNpadStyleSet(HLERequestContext& ctx); | ||
| 64 | void SetSupportedNpadIdType(HLERequestContext& ctx); | ||
| 65 | void ActivateNpad(HLERequestContext& ctx); | ||
| 66 | void DeactivateNpad(HLERequestContext& ctx); | ||
| 67 | void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx); | ||
| 68 | void DisconnectNpad(HLERequestContext& ctx); | ||
| 69 | void GetPlayerLedPattern(HLERequestContext& ctx); | ||
| 70 | void ActivateNpadWithRevision(HLERequestContext& ctx); | ||
| 71 | void SetNpadJoyHoldType(HLERequestContext& ctx); | ||
| 72 | void GetNpadJoyHoldType(HLERequestContext& ctx); | ||
| 73 | void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx); | ||
| 74 | void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx); | ||
| 75 | void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx); | ||
| 76 | void MergeSingleJoyAsDualJoy(HLERequestContext& ctx); | ||
| 77 | void StartLrAssignmentMode(HLERequestContext& ctx); | ||
| 78 | void StopLrAssignmentMode(HLERequestContext& ctx); | ||
| 79 | void SetNpadHandheldActivationMode(HLERequestContext& ctx); | ||
| 80 | void GetNpadHandheldActivationMode(HLERequestContext& ctx); | ||
| 81 | void SwapNpadAssignment(HLERequestContext& ctx); | ||
| 82 | void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx); | ||
| 83 | void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx); | ||
| 84 | void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx); | ||
| 85 | void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx); | ||
| 86 | void SetNpadCaptureButtonAssignment(HLERequestContext& ctx); | ||
| 87 | void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx); | ||
| 88 | void GetVibrationDeviceInfo(HLERequestContext& ctx); | ||
| 89 | void SendVibrationValue(HLERequestContext& ctx); | ||
| 90 | void GetActualVibrationValue(HLERequestContext& ctx); | ||
| 91 | void CreateActiveVibrationDeviceList(HLERequestContext& ctx); | ||
| 92 | void PermitVibration(HLERequestContext& ctx); | ||
| 93 | void IsVibrationPermitted(HLERequestContext& ctx); | ||
| 94 | void SendVibrationValues(HLERequestContext& ctx); | ||
| 95 | void SendVibrationGcErmCommand(HLERequestContext& ctx); | ||
| 96 | void GetActualVibrationGcErmCommand(HLERequestContext& ctx); | ||
| 97 | void BeginPermitVibrationSession(HLERequestContext& ctx); | ||
| 98 | void EndPermitVibrationSession(HLERequestContext& ctx); | ||
| 99 | void IsVibrationDeviceMounted(HLERequestContext& ctx); | ||
| 100 | void ActivateConsoleSixAxisSensor(HLERequestContext& ctx); | ||
| 101 | void StartConsoleSixAxisSensor(HLERequestContext& ctx); | ||
| 102 | void StopConsoleSixAxisSensor(HLERequestContext& ctx); | ||
| 103 | void ActivateSevenSixAxisSensor(HLERequestContext& ctx); | ||
| 104 | void StartSevenSixAxisSensor(HLERequestContext& ctx); | ||
| 105 | void StopSevenSixAxisSensor(HLERequestContext& ctx); | ||
| 106 | void InitializeSevenSixAxisSensor(HLERequestContext& ctx); | ||
| 107 | void FinalizeSevenSixAxisSensor(HLERequestContext& ctx); | ||
| 108 | void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx); | ||
| 109 | void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); | ||
| 110 | void GetPalmaConnectionHandle(HLERequestContext& ctx); | ||
| 111 | void InitializePalma(HLERequestContext& ctx); | ||
| 112 | void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx); | ||
| 113 | void GetPalmaOperationInfo(HLERequestContext& ctx); | ||
| 114 | void PlayPalmaActivity(HLERequestContext& ctx); | ||
| 115 | void SetPalmaFrModeType(HLERequestContext& ctx); | ||
| 116 | void ReadPalmaStep(HLERequestContext& ctx); | ||
| 117 | void EnablePalmaStep(HLERequestContext& ctx); | ||
| 118 | void ResetPalmaStep(HLERequestContext& ctx); | ||
| 119 | void ReadPalmaApplicationSection(HLERequestContext& ctx); | ||
| 120 | void WritePalmaApplicationSection(HLERequestContext& ctx); | ||
| 121 | void ReadPalmaUniqueCode(HLERequestContext& ctx); | ||
| 122 | void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx); | ||
| 123 | void WritePalmaActivityEntry(HLERequestContext& ctx); | ||
| 124 | void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx); | ||
| 125 | void WritePalmaWaveEntry(HLERequestContext& ctx); | ||
| 126 | void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); | ||
| 127 | void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); | ||
| 128 | void SuspendPalmaFeature(HLERequestContext& ctx); | ||
| 129 | void GetPalmaOperationResult(HLERequestContext& ctx); | ||
| 130 | void ReadPalmaPlayLog(HLERequestContext& ctx); | ||
| 131 | void ResetPalmaPlayLog(HLERequestContext& ctx); | ||
| 132 | void SetIsPalmaAllConnectable(HLERequestContext& ctx); | ||
| 133 | void SetIsPalmaPairedConnectable(HLERequestContext& ctx); | ||
| 134 | void PairPalma(HLERequestContext& ctx); | ||
| 135 | void SetPalmaBoostMode(HLERequestContext& ctx); | ||
| 136 | void CancelWritePalmaWaveEntry(HLERequestContext& ctx); | ||
| 137 | void EnablePalmaBoostMode(HLERequestContext& ctx); | ||
| 138 | void GetPalmaBluetoothAddress(HLERequestContext& ctx); | ||
| 139 | void SetDisallowedPalmaConnection(HLERequestContext& ctx); | ||
| 140 | void SetNpadCommunicationMode(HLERequestContext& ctx); | ||
| 141 | void GetNpadCommunicationMode(HLERequestContext& ctx); | ||
| 142 | void SetTouchScreenConfiguration(HLERequestContext& ctx); | ||
| 143 | void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx); | ||
| 144 | |||
| 145 | std::shared_ptr<ResourceManager> resource_manager; | ||
| 146 | std::shared_ptr<HidFirmwareSettings> firmware_settings; | ||
| 147 | }; | ||
| 148 | |||
| 149 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp new file mode 100644 index 000000000..b56d0347a --- /dev/null +++ b/src/core/hle/service/hid/hid_system_server.cpp | |||
| @@ -0,0 +1,539 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hid/hid_core.h" | ||
| 5 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 6 | #include "core/hle/service/hid/controllers/touchscreen.h" | ||
| 7 | #include "core/hle/service/hid/errors.h" | ||
| 8 | #include "core/hle/service/hid/hid_system_server.h" | ||
| 9 | #include "core/hle/service/hid/resource_manager.h" | ||
| 10 | #include "core/hle/service/ipc_helpers.h" | ||
| 11 | |||
| 12 | namespace Service::HID { | ||
| 13 | |||
| 14 | IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource) | ||
| 15 | : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name}, | ||
| 16 | resource_manager{resource} { | ||
| 17 | // clang-format off | ||
| 18 | static const FunctionInfo functions[] = { | ||
| 19 | {31, nullptr, "SendKeyboardLockKeyEvent"}, | ||
| 20 | {101, nullptr, "AcquireHomeButtonEventHandle"}, | ||
| 21 | {111, nullptr, "ActivateHomeButton"}, | ||
| 22 | {121, nullptr, "AcquireSleepButtonEventHandle"}, | ||
| 23 | {131, nullptr, "ActivateSleepButton"}, | ||
| 24 | {141, nullptr, "AcquireCaptureButtonEventHandle"}, | ||
| 25 | {151, nullptr, "ActivateCaptureButton"}, | ||
| 26 | {161, nullptr, "GetPlatformConfig"}, | ||
| 27 | {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, | ||
| 28 | {211, nullptr, "GetNpadsWithNfc"}, | ||
| 29 | {212, nullptr, "AcquireNfcActivateEventHandle"}, | ||
| 30 | {213, nullptr, "ActivateNfc"}, | ||
| 31 | {214, nullptr, "GetXcdHandleForNpadWithNfc"}, | ||
| 32 | {215, nullptr, "IsNfcActivated"}, | ||
| 33 | {230, nullptr, "AcquireIrSensorEventHandle"}, | ||
| 34 | {231, nullptr, "ActivateIrSensor"}, | ||
| 35 | {232, nullptr, "GetIrSensorState"}, | ||
| 36 | {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, | ||
| 37 | {301, nullptr, "ActivateNpadSystem"}, | ||
| 38 | {303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, | ||
| 39 | {304, &IHidSystemServer::EnableAssigningSingleOnSlSrPress, "EnableAssigningSingleOnSlSrPress"}, | ||
| 40 | {305, &IHidSystemServer::DisableAssigningSingleOnSlSrPress, "DisableAssigningSingleOnSlSrPress"}, | ||
| 41 | {306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"}, | ||
| 42 | {307, nullptr, "GetNpadSystemExtStyle"}, | ||
| 43 | {308, &IHidSystemServer::ApplyNpadSystemCommonPolicyFull, "ApplyNpadSystemCommonPolicyFull"}, | ||
| 44 | {309, &IHidSystemServer::GetNpadFullKeyGripColor, "GetNpadFullKeyGripColor"}, | ||
| 45 | {310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"}, | ||
| 46 | {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, | ||
| 47 | {312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"}, | ||
| 48 | {313, nullptr, "GetNpadCaptureButtonAssignment"}, | ||
| 49 | {314, nullptr, "GetAppletFooterUiType"}, | ||
| 50 | {315, &IHidSystemServer::GetAppletDetailedUiType, "GetAppletDetailedUiType"}, | ||
| 51 | {316, &IHidSystemServer::GetNpadInterfaceType, "GetNpadInterfaceType"}, | ||
| 52 | {317, &IHidSystemServer::GetNpadLeftRightInterfaceType, "GetNpadLeftRightInterfaceType"}, | ||
| 53 | {318, &IHidSystemServer::HasBattery, "HasBattery"}, | ||
| 54 | {319, &IHidSystemServer::HasLeftRightBattery, "HasLeftRightBattery"}, | ||
| 55 | {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"}, | ||
| 56 | {322, &IHidSystemServer::GetIrSensorState, "GetIrSensorState"}, | ||
| 57 | {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, | ||
| 58 | {324, nullptr, "GetUniquePadButtonSet"}, | ||
| 59 | {325, nullptr, "GetUniquePadColor"}, | ||
| 60 | {326, nullptr, "GetUniquePadAppletDetailedUiType"}, | ||
| 61 | {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, | ||
| 62 | {328, nullptr, "AttachAbstractedPadToNpad"}, | ||
| 63 | {329, nullptr, "DetachAbstractedPadAll"}, | ||
| 64 | {330, nullptr, "CheckAbstractedPadConnection"}, | ||
| 65 | {500, nullptr, "SetAppletResourceUserId"}, | ||
| 66 | {501, nullptr, "RegisterAppletResourceUserId"}, | ||
| 67 | {502, nullptr, "UnregisterAppletResourceUserId"}, | ||
| 68 | {503, nullptr, "EnableAppletToGetInput"}, | ||
| 69 | {504, nullptr, "SetAruidValidForVibration"}, | ||
| 70 | {505, nullptr, "EnableAppletToGetSixAxisSensor"}, | ||
| 71 | {506, nullptr, "EnableAppletToGetPadInput"}, | ||
| 72 | {507, nullptr, "EnableAppletToGetTouchScreen"}, | ||
| 73 | {510, nullptr, "SetVibrationMasterVolume"}, | ||
| 74 | {511, nullptr, "GetVibrationMasterVolume"}, | ||
| 75 | {512, nullptr, "BeginPermitVibrationSession"}, | ||
| 76 | {513, nullptr, "EndPermitVibrationSession"}, | ||
| 77 | {514, nullptr, "Unknown514"}, | ||
| 78 | {520, nullptr, "EnableHandheldHids"}, | ||
| 79 | {521, nullptr, "DisableHandheldHids"}, | ||
| 80 | {522, nullptr, "SetJoyConRailEnabled"}, | ||
| 81 | {523, nullptr, "IsJoyConRailEnabled"}, | ||
| 82 | {524, nullptr, "IsHandheldHidsEnabled"}, | ||
| 83 | {525, nullptr, "IsJoyConAttachedOnAllRail"}, | ||
| 84 | {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, | ||
| 85 | {541, nullptr, "GetPlayReportControllerUsages"}, | ||
| 86 | {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, | ||
| 87 | {543, nullptr, "GetRegisteredDevicesOld"}, | ||
| 88 | {544, &IHidSystemServer::AcquireConnectionTriggerTimeoutEvent, "AcquireConnectionTriggerTimeoutEvent"}, | ||
| 89 | {545, nullptr, "SendConnectionTrigger"}, | ||
| 90 | {546, &IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport, "AcquireDeviceRegisteredEventForControllerSupport"}, | ||
| 91 | {547, nullptr, "GetAllowedBluetoothLinksCount"}, | ||
| 92 | {548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"}, | ||
| 93 | {549, nullptr, "GetConnectableRegisteredDevices"}, | ||
| 94 | {700, nullptr, "ActivateUniquePad"}, | ||
| 95 | {702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"}, | ||
| 96 | {703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"}, | ||
| 97 | {751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"}, | ||
| 98 | {800, nullptr, "ListSixAxisSensorHandles"}, | ||
| 99 | {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, | ||
| 100 | {802, nullptr, "ResetSixAxisSensorCalibrationValues"}, | ||
| 101 | {803, nullptr, "StartSixAxisSensorUserCalibration"}, | ||
| 102 | {804, nullptr, "CancelSixAxisSensorUserCalibration"}, | ||
| 103 | {805, nullptr, "GetUniquePadBluetoothAddress"}, | ||
| 104 | {806, nullptr, "DisconnectUniquePad"}, | ||
| 105 | {807, nullptr, "GetUniquePadType"}, | ||
| 106 | {808, nullptr, "GetUniquePadInterface"}, | ||
| 107 | {809, nullptr, "GetUniquePadSerialNumber"}, | ||
| 108 | {810, nullptr, "GetUniquePadControllerNumber"}, | ||
| 109 | {811, nullptr, "GetSixAxisSensorUserCalibrationStage"}, | ||
| 110 | {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"}, | ||
| 111 | {821, nullptr, "StartAnalogStickManualCalibration"}, | ||
| 112 | {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"}, | ||
| 113 | {823, nullptr, "CancelAnalogStickManualCalibration"}, | ||
| 114 | {824, nullptr, "ResetAnalogStickManualCalibration"}, | ||
| 115 | {825, nullptr, "GetAnalogStickState"}, | ||
| 116 | {826, nullptr, "GetAnalogStickManualCalibrationStage"}, | ||
| 117 | {827, nullptr, "IsAnalogStickButtonPressed"}, | ||
| 118 | {828, nullptr, "IsAnalogStickInReleasePosition"}, | ||
| 119 | {829, nullptr, "IsAnalogStickInCircumference"}, | ||
| 120 | {830, nullptr, "SetNotificationLedPattern"}, | ||
| 121 | {831, nullptr, "SetNotificationLedPatternWithTimeout"}, | ||
| 122 | {832, nullptr, "PrepareHidsForNotificationWake"}, | ||
| 123 | {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, | ||
| 124 | {851, nullptr, "EnableUsbFullKeyController"}, | ||
| 125 | {852, nullptr, "IsUsbConnected"}, | ||
| 126 | {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"}, | ||
| 127 | {900, nullptr, "ActivateInputDetector"}, | ||
| 128 | {901, nullptr, "NotifyInputDetector"}, | ||
| 129 | {1000, &IHidSystemServer::InitializeFirmwareUpdate, "InitializeFirmwareUpdate"}, | ||
| 130 | {1001, nullptr, "GetFirmwareVersion"}, | ||
| 131 | {1002, nullptr, "GetAvailableFirmwareVersion"}, | ||
| 132 | {1003, nullptr, "IsFirmwareUpdateAvailable"}, | ||
| 133 | {1004, nullptr, "CheckFirmwareUpdateRequired"}, | ||
| 134 | {1005, nullptr, "StartFirmwareUpdate"}, | ||
| 135 | {1006, nullptr, "AbortFirmwareUpdate"}, | ||
| 136 | {1007, nullptr, "GetFirmwareUpdateState"}, | ||
| 137 | {1008, nullptr, "ActivateAudioControl"}, | ||
| 138 | {1009, nullptr, "AcquireAudioControlEventHandle"}, | ||
| 139 | {1010, nullptr, "GetAudioControlStates"}, | ||
| 140 | {1011, nullptr, "DeactivateAudioControl"}, | ||
| 141 | {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"}, | ||
| 142 | {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"}, | ||
| 143 | {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, | ||
| 144 | {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, | ||
| 145 | {1100, nullptr, "GetHidbusSystemServiceObject"}, | ||
| 146 | {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, | ||
| 147 | {1130, nullptr, "InitializeUsbFirmwareUpdate"}, | ||
| 148 | {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, | ||
| 149 | {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, | ||
| 150 | {1133, nullptr, "StartUsbFirmwareUpdate"}, | ||
| 151 | {1134, nullptr, "GetUsbFirmwareUpdateState"}, | ||
| 152 | {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"}, | ||
| 153 | {1150, nullptr, "SetTouchScreenMagnification"}, | ||
| 154 | {1151, nullptr, "GetTouchScreenFirmwareVersion"}, | ||
| 155 | {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, | ||
| 156 | {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, | ||
| 157 | {1154, nullptr, "IsFirmwareAvailableForNotification"}, | ||
| 158 | {1155, nullptr, "SetForceHandheldStyleVibration"}, | ||
| 159 | {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, | ||
| 160 | {1157, nullptr, "CancelConnectionTrigger"}, | ||
| 161 | {1200, nullptr, "IsButtonConfigSupported"}, | ||
| 162 | {1201, nullptr, "IsButtonConfigEmbeddedSupported"}, | ||
| 163 | {1202, nullptr, "DeleteButtonConfig"}, | ||
| 164 | {1203, nullptr, "DeleteButtonConfigEmbedded"}, | ||
| 165 | {1204, nullptr, "SetButtonConfigEnabled"}, | ||
| 166 | {1205, nullptr, "SetButtonConfigEmbeddedEnabled"}, | ||
| 167 | {1206, nullptr, "IsButtonConfigEnabled"}, | ||
| 168 | {1207, nullptr, "IsButtonConfigEmbeddedEnabled"}, | ||
| 169 | {1208, nullptr, "SetButtonConfigEmbedded"}, | ||
| 170 | {1209, nullptr, "SetButtonConfigFull"}, | ||
| 171 | {1210, nullptr, "SetButtonConfigLeft"}, | ||
| 172 | {1211, nullptr, "SetButtonConfigRight"}, | ||
| 173 | {1212, nullptr, "GetButtonConfigEmbedded"}, | ||
| 174 | {1213, nullptr, "GetButtonConfigFull"}, | ||
| 175 | {1214, nullptr, "GetButtonConfigLeft"}, | ||
| 176 | {1215, nullptr, "GetButtonConfigRight"}, | ||
| 177 | {1250, nullptr, "IsCustomButtonConfigSupported"}, | ||
| 178 | {1251, nullptr, "IsDefaultButtonConfigEmbedded"}, | ||
| 179 | {1252, nullptr, "IsDefaultButtonConfigFull"}, | ||
| 180 | {1253, nullptr, "IsDefaultButtonConfigLeft"}, | ||
| 181 | {1254, nullptr, "IsDefaultButtonConfigRight"}, | ||
| 182 | {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"}, | ||
| 183 | {1256, nullptr, "IsButtonConfigStorageFullEmpty"}, | ||
| 184 | {1257, nullptr, "IsButtonConfigStorageLeftEmpty"}, | ||
| 185 | {1258, nullptr, "IsButtonConfigStorageRightEmpty"}, | ||
| 186 | {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"}, | ||
| 187 | {1260, nullptr, "GetButtonConfigStorageFullDeprecated"}, | ||
| 188 | {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"}, | ||
| 189 | {1262, nullptr, "GetButtonConfigStorageRightDeprecated"}, | ||
| 190 | {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"}, | ||
| 191 | {1264, nullptr, "SetButtonConfigStorageFullDeprecated"}, | ||
| 192 | {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"}, | ||
| 193 | {1266, nullptr, "SetButtonConfigStorageRightDeprecated"}, | ||
| 194 | {1267, nullptr, "DeleteButtonConfigStorageEmbedded"}, | ||
| 195 | {1268, nullptr, "DeleteButtonConfigStorageFull"}, | ||
| 196 | {1269, nullptr, "DeleteButtonConfigStorageLeft"}, | ||
| 197 | {1270, nullptr, "DeleteButtonConfigStorageRight"}, | ||
| 198 | {1271, nullptr, "IsUsingCustomButtonConfig"}, | ||
| 199 | {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, | ||
| 200 | {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, | ||
| 201 | {1274, nullptr, "SetDefaultButtonConfig"}, | ||
| 202 | {1275, nullptr, "SetAllDefaultButtonConfig"}, | ||
| 203 | {1276, nullptr, "SetHidButtonConfigEmbedded"}, | ||
| 204 | {1277, nullptr, "SetHidButtonConfigFull"}, | ||
| 205 | {1278, nullptr, "SetHidButtonConfigLeft"}, | ||
| 206 | {1279, nullptr, "SetHidButtonConfigRight"}, | ||
| 207 | {1280, nullptr, "GetHidButtonConfigEmbedded"}, | ||
| 208 | {1281, nullptr, "GetHidButtonConfigFull"}, | ||
| 209 | {1282, nullptr, "GetHidButtonConfigLeft"}, | ||
| 210 | {1283, nullptr, "GetHidButtonConfigRight"}, | ||
| 211 | {1284, nullptr, "GetButtonConfigStorageEmbedded"}, | ||
| 212 | {1285, nullptr, "GetButtonConfigStorageFull"}, | ||
| 213 | {1286, nullptr, "GetButtonConfigStorageLeft"}, | ||
| 214 | {1287, nullptr, "GetButtonConfigStorageRight"}, | ||
| 215 | {1288, nullptr, "SetButtonConfigStorageEmbedded"}, | ||
| 216 | {1289, nullptr, "SetButtonConfigStorageFull"}, | ||
| 217 | {1290, nullptr, "DeleteButtonConfigStorageRight"}, | ||
| 218 | {1291, nullptr, "DeleteButtonConfigStorageRight"}, | ||
| 219 | }; | ||
| 220 | // clang-format on | ||
| 221 | |||
| 222 | RegisterHandlers(functions); | ||
| 223 | |||
| 224 | joy_detach_event = service_context.CreateEvent("IHidSystemServer::JoyDetachEvent"); | ||
| 225 | acquire_device_registered_event = | ||
| 226 | service_context.CreateEvent("IHidSystemServer::AcquireDeviceRegisteredEvent"); | ||
| 227 | acquire_connection_trigger_timeout_event = | ||
| 228 | service_context.CreateEvent("IHidSystemServer::AcquireConnectionTriggerTimeoutEvent"); | ||
| 229 | unique_pad_connection_event = | ||
| 230 | service_context.CreateEvent("IHidSystemServer::AcquireUniquePadConnectionEventHandle"); | ||
| 231 | } | ||
| 232 | |||
| 233 | IHidSystemServer::~IHidSystemServer() { | ||
| 234 | service_context.CloseEvent(joy_detach_event); | ||
| 235 | service_context.CloseEvent(acquire_device_registered_event); | ||
| 236 | service_context.CloseEvent(acquire_connection_trigger_timeout_event); | ||
| 237 | service_context.CloseEvent(unique_pad_connection_event); | ||
| 238 | }; | ||
| 239 | |||
| 240 | void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { | ||
| 241 | LOG_WARNING(Service_HID, "called"); | ||
| 242 | |||
| 243 | GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(); | ||
| 244 | |||
| 245 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 246 | rb.Push(ResultSuccess); | ||
| 247 | } | ||
| 248 | |||
| 249 | void IHidSystemServer::EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx) { | ||
| 250 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 251 | |||
| 252 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 253 | rb.Push(ResultSuccess); | ||
| 254 | } | ||
| 255 | |||
| 256 | void IHidSystemServer::DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx) { | ||
| 257 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 258 | |||
| 259 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 260 | rb.Push(ResultSuccess); | ||
| 261 | } | ||
| 262 | |||
| 263 | void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) { | ||
| 264 | LOG_DEBUG(Service_HID, "(STUBBED) called"); // Spams a lot when controller applet is running | ||
| 265 | |||
| 266 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 267 | rb.Push(ResultSuccess); | ||
| 268 | rb.PushEnum(system.HIDCore().GetLastActiveController()); | ||
| 269 | } | ||
| 270 | |||
| 271 | void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) { | ||
| 272 | LOG_WARNING(Service_HID, "called"); | ||
| 273 | |||
| 274 | GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(); | ||
| 275 | |||
| 276 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 277 | rb.Push(ResultSuccess); | ||
| 278 | } | ||
| 279 | |||
| 280 | void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) { | ||
| 281 | IPC::RequestParser rp{ctx}; | ||
| 282 | const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 283 | |||
| 284 | LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", | ||
| 285 | npad_id_type); // Spams a lot when controller applet is running | ||
| 286 | |||
| 287 | Core::HID::NpadColor left_color{}; | ||
| 288 | Core::HID::NpadColor right_color{}; | ||
| 289 | // TODO: Get colors from Npad | ||
| 290 | |||
| 291 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 292 | rb.Push(ResultSuccess); | ||
| 293 | rb.PushRaw(left_color); | ||
| 294 | rb.PushRaw(right_color); | ||
| 295 | } | ||
| 296 | |||
| 297 | void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) { | ||
| 298 | IPC::RequestParser rp{ctx}; | ||
| 299 | |||
| 300 | LOG_INFO(Service_HID, "(STUBBED) called"); | ||
| 301 | |||
| 302 | Core::HID::NpadStyleSet supported_styleset = | ||
| 303 | GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw; | ||
| 304 | |||
| 305 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 306 | rb.Push(ResultSuccess); | ||
| 307 | rb.PushEnum(supported_styleset); | ||
| 308 | } | ||
| 309 | |||
| 310 | void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) { | ||
| 311 | IPC::RequestParser rp{ctx}; | ||
| 312 | |||
| 313 | LOG_INFO(Service_HID, "(STUBBED) called"); | ||
| 314 | |||
| 315 | Core::HID::NpadStyleSet supported_styleset = | ||
| 316 | GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw; | ||
| 317 | |||
| 318 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 319 | rb.Push(ResultSuccess); | ||
| 320 | rb.PushEnum(supported_styleset); | ||
| 321 | } | ||
| 322 | |||
| 323 | void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) { | ||
| 324 | IPC::RequestParser rp{ctx}; | ||
| 325 | const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 326 | |||
| 327 | LOG_DEBUG(Service_HID, "called, npad_id_type={}", | ||
| 328 | npad_id_type); // Spams a lot when controller applet is running | ||
| 329 | |||
| 330 | const NPad::AppletDetailedUiType detailed_ui_type = | ||
| 331 | GetResourceManager()->GetNpad()->GetAppletDetailedUiType(npad_id_type); | ||
| 332 | |||
| 333 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 334 | rb.Push(ResultSuccess); | ||
| 335 | rb.PushRaw(detailed_ui_type); | ||
| 336 | } | ||
| 337 | |||
| 338 | void IHidSystemServer::GetNpadInterfaceType(HLERequestContext& ctx) { | ||
| 339 | IPC::RequestParser rp{ctx}; | ||
| 340 | const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 341 | |||
| 342 | LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", | ||
| 343 | npad_id_type); // Spams a lot when controller applet is running | ||
| 344 | |||
| 345 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 346 | rb.Push(ResultSuccess); | ||
| 347 | rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth); | ||
| 348 | } | ||
| 349 | |||
| 350 | void IHidSystemServer::GetNpadLeftRightInterfaceType(HLERequestContext& ctx) { | ||
| 351 | IPC::RequestParser rp{ctx}; | ||
| 352 | const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 353 | |||
| 354 | LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", | ||
| 355 | npad_id_type); // Spams a lot when controller applet is running | ||
| 356 | |||
| 357 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 358 | rb.Push(ResultSuccess); | ||
| 359 | rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth); | ||
| 360 | rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth); | ||
| 361 | } | ||
| 362 | |||
| 363 | void IHidSystemServer::HasBattery(HLERequestContext& ctx) { | ||
| 364 | IPC::RequestParser rp{ctx}; | ||
| 365 | const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 366 | |||
| 367 | LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", | ||
| 368 | npad_id_type); // Spams a lot when controller applet is running | ||
| 369 | |||
| 370 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 371 | rb.Push(ResultSuccess); | ||
| 372 | rb.Push(false); | ||
| 373 | } | ||
| 374 | |||
| 375 | void IHidSystemServer::HasLeftRightBattery(HLERequestContext& ctx) { | ||
| 376 | IPC::RequestParser rp{ctx}; | ||
| 377 | const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 378 | |||
| 379 | LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", | ||
| 380 | npad_id_type); // Spams a lot when controller applet is running | ||
| 381 | |||
| 382 | struct LeftRightBattery { | ||
| 383 | bool left; | ||
| 384 | bool right; | ||
| 385 | }; | ||
| 386 | |||
| 387 | LeftRightBattery left_right_battery{ | ||
| 388 | .left = false, | ||
| 389 | .right = false, | ||
| 390 | }; | ||
| 391 | |||
| 392 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 393 | rb.Push(ResultSuccess); | ||
| 394 | rb.PushRaw(left_right_battery); | ||
| 395 | } | ||
| 396 | |||
| 397 | void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) { | ||
| 398 | IPC::RequestParser rp{ctx}; | ||
| 399 | const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; | ||
| 400 | |||
| 401 | LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", | ||
| 402 | npad_id_type); // Spams a lot when controller applet is running | ||
| 403 | |||
| 404 | const std::vector<Core::HID::UniquePadId> unique_pads{}; | ||
| 405 | |||
| 406 | if (!unique_pads.empty()) { | ||
| 407 | ctx.WriteBuffer(unique_pads); | ||
| 408 | } | ||
| 409 | |||
| 410 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 411 | rb.Push(ResultSuccess); | ||
| 412 | rb.Push(static_cast<u32>(unique_pads.size())); | ||
| 413 | } | ||
| 414 | |||
| 415 | void IHidSystemServer::GetIrSensorState(HLERequestContext& ctx) { | ||
| 416 | IPC::RequestParser rp{ctx}; | ||
| 417 | |||
| 418 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 419 | |||
| 420 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 421 | rb.Push(ResultSuccess); | ||
| 422 | } | ||
| 423 | |||
| 424 | void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) { | ||
| 425 | LOG_INFO(Service_AM, "(STUBBED) called"); | ||
| 426 | |||
| 427 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 428 | rb.Push(ResultSuccess); | ||
| 429 | rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent()); | ||
| 430 | } | ||
| 431 | |||
| 432 | void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) { | ||
| 433 | LOG_INFO(Service_HID, "(STUBBED) called"); | ||
| 434 | |||
| 435 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 436 | rb.Push(ResultSuccess); | ||
| 437 | rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent()); | ||
| 438 | } | ||
| 439 | |||
| 440 | void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) { | ||
| 441 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 442 | |||
| 443 | struct RegisterData { | ||
| 444 | std::array<u8, 0x68> data; | ||
| 445 | }; | ||
| 446 | static_assert(sizeof(RegisterData) == 0x68, "RegisterData is an invalid size"); | ||
| 447 | std::vector<RegisterData> registered_devices{}; | ||
| 448 | |||
| 449 | if (!registered_devices.empty()) { | ||
| 450 | ctx.WriteBuffer(registered_devices); | ||
| 451 | } | ||
| 452 | |||
| 453 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 454 | rb.Push(ResultSuccess); | ||
| 455 | rb.Push<u64>(registered_devices.size()); | ||
| 456 | } | ||
| 457 | |||
| 458 | void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx) { | ||
| 459 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 460 | |||
| 461 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 462 | rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent()); | ||
| 463 | rb.Push(ResultSuccess); | ||
| 464 | } | ||
| 465 | |||
| 466 | void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) { | ||
| 467 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 468 | |||
| 469 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 470 | rb.Push(ResultSuccess); | ||
| 471 | rb.Push<u64>(0); | ||
| 472 | } | ||
| 473 | |||
| 474 | void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) { | ||
| 475 | LOG_INFO(Service_AM, "called"); | ||
| 476 | |||
| 477 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 478 | rb.Push(ResultSuccess); | ||
| 479 | rb.PushCopyObjects(joy_detach_event->GetReadableEvent()); | ||
| 480 | } | ||
| 481 | |||
| 482 | void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { | ||
| 483 | const bool is_enabled = false; | ||
| 484 | |||
| 485 | LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled); | ||
| 486 | |||
| 487 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 488 | rb.Push(ResultSuccess); | ||
| 489 | rb.Push(is_enabled); | ||
| 490 | } | ||
| 491 | |||
| 492 | void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) { | ||
| 493 | const bool button_pressed = false; | ||
| 494 | |||
| 495 | LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}", | ||
| 496 | button_pressed); // Spams a lot when controller applet is open | ||
| 497 | |||
| 498 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 499 | rb.Push(ResultSuccess); | ||
| 500 | rb.Push(button_pressed); | ||
| 501 | } | ||
| 502 | |||
| 503 | void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) { | ||
| 504 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 505 | |||
| 506 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 507 | rb.Push(ResultSuccess); | ||
| 508 | } | ||
| 509 | |||
| 510 | void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) { | ||
| 511 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 512 | |||
| 513 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 514 | rb.Push(ResultSuccess); | ||
| 515 | } | ||
| 516 | |||
| 517 | void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { | ||
| 518 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 519 | |||
| 520 | Core::HID::TouchScreenConfigurationForNx touchscreen_config{ | ||
| 521 | .mode = Core::HID::TouchScreenModeForNx::Finger, | ||
| 522 | }; | ||
| 523 | |||
| 524 | if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && | ||
| 525 | touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { | ||
| 526 | touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; | ||
| 527 | } | ||
| 528 | |||
| 529 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 530 | rb.Push(ResultSuccess); | ||
| 531 | rb.PushRaw(touchscreen_config); | ||
| 532 | } | ||
| 533 | |||
| 534 | std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() { | ||
| 535 | resource_manager->Initialize(); | ||
| 536 | return resource_manager; | ||
| 537 | } | ||
| 538 | |||
| 539 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h new file mode 100644 index 000000000..822d5e5b9 --- /dev/null +++ b/src/core/hle/service/hid/hid_system_server.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/kernel_helpers.h" | ||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | class KEvent; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Service::HID { | ||
| 18 | class ResourceManager; | ||
| 19 | |||
| 20 | class IHidSystemServer final : public ServiceFramework<IHidSystemServer> { | ||
| 21 | public: | ||
| 22 | explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource); | ||
| 23 | ~IHidSystemServer() override; | ||
| 24 | |||
| 25 | private: | ||
| 26 | void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx); | ||
| 27 | void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx); | ||
| 28 | void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx); | ||
| 29 | void GetLastActiveNpad(HLERequestContext& ctx); | ||
| 30 | void ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx); | ||
| 31 | void GetNpadFullKeyGripColor(HLERequestContext& ctx); | ||
| 32 | void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx); | ||
| 33 | void SetSupportedNpadStyleSetAll(HLERequestContext& ctx); | ||
| 34 | void GetAppletDetailedUiType(HLERequestContext& ctx); | ||
| 35 | void GetNpadInterfaceType(HLERequestContext& ctx); | ||
| 36 | void GetNpadLeftRightInterfaceType(HLERequestContext& ctx); | ||
| 37 | void HasBattery(HLERequestContext& ctx); | ||
| 38 | void HasLeftRightBattery(HLERequestContext& ctx); | ||
| 39 | void GetUniquePadsFromNpad(HLERequestContext& ctx); | ||
| 40 | void GetIrSensorState(HLERequestContext& ctx); | ||
| 41 | void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx); | ||
| 42 | void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx); | ||
| 43 | void GetRegisteredDevices(HLERequestContext& ctx); | ||
| 44 | void AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx); | ||
| 45 | void GetUniquePadIds(HLERequestContext& ctx); | ||
| 46 | void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx); | ||
| 47 | void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); | ||
| 48 | void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx); | ||
| 49 | void InitializeFirmwareUpdate(HLERequestContext& ctx); | ||
| 50 | void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); | ||
| 51 | void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); | ||
| 52 | |||
| 53 | std::shared_ptr<ResourceManager> GetResourceManager(); | ||
| 54 | |||
| 55 | Kernel::KEvent* acquire_connection_trigger_timeout_event; | ||
| 56 | Kernel::KEvent* acquire_device_registered_event; | ||
| 57 | Kernel::KEvent* joy_detach_event; | ||
| 58 | Kernel::KEvent* unique_pad_connection_event; | ||
| 59 | KernelHelpers::ServiceContext service_context; | ||
| 60 | std::shared_ptr<ResourceManager> resource_manager; | ||
| 61 | }; | ||
| 62 | |||
| 63 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/hid_util.h b/src/core/hle/service/hid/hid_util.h new file mode 100644 index 000000000..b87cc10e3 --- /dev/null +++ b/src/core/hle/service/hid/hid_util.h | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hid/hid_types.h" | ||
| 7 | #include "core/hle/service/hid/errors.h" | ||
| 8 | |||
| 9 | namespace Service::HID { | ||
| 10 | |||
| 11 | constexpr bool IsNpadIdValid(const Core::HID::NpadIdType npad_id) { | ||
| 12 | switch (npad_id) { | ||
| 13 | case Core::HID::NpadIdType::Player1: | ||
| 14 | case Core::HID::NpadIdType::Player2: | ||
| 15 | case Core::HID::NpadIdType::Player3: | ||
| 16 | case Core::HID::NpadIdType::Player4: | ||
| 17 | case Core::HID::NpadIdType::Player5: | ||
| 18 | case Core::HID::NpadIdType::Player6: | ||
| 19 | case Core::HID::NpadIdType::Player7: | ||
| 20 | case Core::HID::NpadIdType::Player8: | ||
| 21 | case Core::HID::NpadIdType::Other: | ||
| 22 | case Core::HID::NpadIdType::Handheld: | ||
| 23 | return true; | ||
| 24 | default: | ||
| 25 | return false; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& handle) { | ||
| 30 | const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id)); | ||
| 31 | const bool device_index = handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; | ||
| 32 | |||
| 33 | if (!npad_id) { | ||
| 34 | return InvalidNpadId; | ||
| 35 | } | ||
| 36 | if (!device_index) { | ||
| 37 | return NpadDeviceIndexOutOfRange; | ||
| 38 | } | ||
| 39 | |||
| 40 | return ResultSuccess; | ||
| 41 | } | ||
| 42 | |||
| 43 | constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) { | ||
| 44 | switch (handle.npad_type) { | ||
| 45 | case Core::HID::NpadStyleIndex::ProController: | ||
| 46 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 47 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 48 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 49 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 50 | case Core::HID::NpadStyleIndex::GameCube: | ||
| 51 | case Core::HID::NpadStyleIndex::N64: | ||
| 52 | case Core::HID::NpadStyleIndex::SystemExt: | ||
| 53 | case Core::HID::NpadStyleIndex::System: | ||
| 54 | // These support vibration | ||
| 55 | break; | ||
| 56 | default: | ||
| 57 | return VibrationInvalidStyleIndex; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (!IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id))) { | ||
| 61 | return VibrationInvalidNpadId; | ||
| 62 | } | ||
| 63 | |||
| 64 | if (handle.device_index >= Core::HID::DeviceIndex::MaxDeviceIndex) { | ||
| 65 | return VibrationDeviceIndexOutOfRange; | ||
| 66 | } | ||
| 67 | |||
| 68 | return ResultSuccess; | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Converts a Core::HID::NpadIdType to an array index. | ||
| 72 | constexpr size_t NpadIdTypeToIndex(Core::HID::NpadIdType npad_id_type) { | ||
| 73 | switch (npad_id_type) { | ||
| 74 | case Core::HID::NpadIdType::Player1: | ||
| 75 | return 0; | ||
| 76 | case Core::HID::NpadIdType::Player2: | ||
| 77 | return 1; | ||
| 78 | case Core::HID::NpadIdType::Player3: | ||
| 79 | return 2; | ||
| 80 | case Core::HID::NpadIdType::Player4: | ||
| 81 | return 3; | ||
| 82 | case Core::HID::NpadIdType::Player5: | ||
| 83 | return 4; | ||
| 84 | case Core::HID::NpadIdType::Player6: | ||
| 85 | return 5; | ||
| 86 | case Core::HID::NpadIdType::Player7: | ||
| 87 | return 6; | ||
| 88 | case Core::HID::NpadIdType::Player8: | ||
| 89 | return 7; | ||
| 90 | case Core::HID::NpadIdType::Handheld: | ||
| 91 | return 8; | ||
| 92 | case Core::HID::NpadIdType::Other: | ||
| 93 | return 9; | ||
| 94 | default: | ||
| 95 | return 8; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | /// Converts an array index to a Core::HID::NpadIdType | ||
| 100 | constexpr Core::HID::NpadIdType IndexToNpadIdType(size_t index) { | ||
| 101 | switch (index) { | ||
| 102 | case 0: | ||
| 103 | return Core::HID::NpadIdType::Player1; | ||
| 104 | case 1: | ||
| 105 | return Core::HID::NpadIdType::Player2; | ||
| 106 | case 2: | ||
| 107 | return Core::HID::NpadIdType::Player3; | ||
| 108 | case 3: | ||
| 109 | return Core::HID::NpadIdType::Player4; | ||
| 110 | case 4: | ||
| 111 | return Core::HID::NpadIdType::Player5; | ||
| 112 | case 5: | ||
| 113 | return Core::HID::NpadIdType::Player6; | ||
| 114 | case 6: | ||
| 115 | return Core::HID::NpadIdType::Player7; | ||
| 116 | case 7: | ||
| 117 | return Core::HID::NpadIdType::Player8; | ||
| 118 | case 8: | ||
| 119 | return Core::HID::NpadIdType::Handheld; | ||
| 120 | case 9: | ||
| 121 | return Core::HID::NpadIdType::Other; | ||
| 122 | default: | ||
| 123 | return Core::HID::NpadIdType::Invalid; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | constexpr Core::HID::NpadStyleSet GetStylesetByIndex(std::size_t index) { | ||
| 128 | switch (index) { | ||
| 129 | case 0: | ||
| 130 | return Core::HID::NpadStyleSet::Fullkey; | ||
| 131 | case 1: | ||
| 132 | return Core::HID::NpadStyleSet::Handheld; | ||
| 133 | case 2: | ||
| 134 | return Core::HID::NpadStyleSet::JoyDual; | ||
| 135 | case 3: | ||
| 136 | return Core::HID::NpadStyleSet::JoyLeft; | ||
| 137 | case 4: | ||
| 138 | return Core::HID::NpadStyleSet::JoyRight; | ||
| 139 | case 5: | ||
| 140 | return Core::HID::NpadStyleSet::Palma; | ||
| 141 | default: | ||
| 142 | return Core::HID::NpadStyleSet::None; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 221c33b86..39b9a4474 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "core/hle/kernel/k_transfer_memory.h" | 12 | #include "core/hle/kernel/k_transfer_memory.h" |
| 13 | #include "core/hle/kernel/kernel.h" | 13 | #include "core/hle/kernel/kernel.h" |
| 14 | #include "core/hle/service/hid/errors.h" | 14 | #include "core/hle/service/hid/errors.h" |
| 15 | #include "core/hle/service/hid/hid_util.h" | ||
| 15 | #include "core/hle/service/hid/irs.h" | 16 | #include "core/hle/service/hid/irs.h" |
| 16 | #include "core/hle/service/hid/irsensor/clustering_processor.h" | 17 | #include "core/hle/service/hid/irsensor/clustering_processor.h" |
| 17 | #include "core/hle/service/hid/irsensor/image_transfer_processor.h" | 18 | #include "core/hle/service/hid/irsensor/image_transfer_processor.h" |
| @@ -138,7 +139,7 @@ void IRS::RunMomentProcessor(HLERequestContext& ctx) { | |||
| 138 | 139 | ||
| 139 | if (result.IsSuccess()) { | 140 | if (result.IsSuccess()) { |
| 140 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | 141 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); |
| 141 | MakeProcessor<MomentProcessor>(parameters.camera_handle, device); | 142 | MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device); |
| 142 | auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); | 143 | auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); |
| 143 | image_transfer_processor.SetConfig(parameters.processor_config); | 144 | image_transfer_processor.SetConfig(parameters.processor_config); |
| 144 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | 145 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| @@ -320,7 +321,7 @@ void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) { | |||
| 320 | } | 321 | } |
| 321 | 322 | ||
| 322 | Core::IrSensor::IrCameraHandle camera_handle{ | 323 | Core::IrSensor::IrCameraHandle camera_handle{ |
| 323 | .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)), | 324 | .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)), |
| 324 | .npad_type = Core::HID::NpadStyleIndex::None, | 325 | .npad_type = Core::HID::NpadStyleIndex::None, |
| 325 | }; | 326 | }; |
| 326 | 327 | ||
| @@ -545,7 +546,7 @@ void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) { | |||
| 545 | 546 | ||
| 546 | Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { | 547 | Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { |
| 547 | if (camera_handle.npad_id > | 548 | if (camera_handle.npad_id > |
| 548 | static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) { | 549 | static_cast<u8>(HID::NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) { |
| 549 | return InvalidIrCameraHandle; | 550 | return InvalidIrCameraHandle; |
| 550 | } | 551 | } |
| 551 | if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) { | 552 | if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) { |
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index a8fa19025..c8e6dab17 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h | |||
| @@ -3,15 +3,12 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/core.h" | ||
| 6 | #include "core/hid/hid_types.h" | 7 | #include "core/hid/hid_types.h" |
| 7 | #include "core/hid/irs_types.h" | 8 | #include "core/hid/irs_types.h" |
| 8 | #include "core/hle/service/hid/irsensor/processor_base.h" | 9 | #include "core/hle/service/hid/irsensor/processor_base.h" |
| 9 | #include "core/hle/service/service.h" | 10 | #include "core/hle/service/service.h" |
| 10 | 11 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Core::HID { | 12 | namespace Core::HID { |
| 16 | class EmulatedController; | 13 | class EmulatedController; |
| 17 | } // namespace Core::HID | 14 | } // namespace Core::HID |
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp index e2f4ae876..c559eb0d5 100644 --- a/src/core/hle/service/hid/irsensor/clustering_processor.cpp +++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp | |||
| @@ -3,16 +3,18 @@ | |||
| 3 | 3 | ||
| 4 | #include <queue> | 4 | #include <queue> |
| 5 | 5 | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/core_timing.h" | ||
| 6 | #include "core/hid/emulated_controller.h" | 8 | #include "core/hid/emulated_controller.h" |
| 7 | #include "core/hid/hid_core.h" | 9 | #include "core/hid/hid_core.h" |
| 8 | #include "core/hle/service/hid/irsensor/clustering_processor.h" | 10 | #include "core/hle/service/hid/irsensor/clustering_processor.h" |
| 9 | 11 | ||
| 10 | namespace Service::IRS { | 12 | namespace Service::IRS { |
| 11 | ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_, | 13 | ClusteringProcessor::ClusteringProcessor(Core::System& system_, |
| 12 | Core::IrSensor::DeviceFormat& device_format, | 14 | Core::IrSensor::DeviceFormat& device_format, |
| 13 | std::size_t npad_index) | 15 | std::size_t npad_index) |
| 14 | : device{device_format} { | 16 | : device{device_format}, system{system_} { |
| 15 | npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); | 17 | npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index); |
| 16 | 18 | ||
| 17 | device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; | 19 | device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; |
| 18 | device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; | 20 | device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
| @@ -48,7 +50,7 @@ void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType ty | |||
| 48 | } | 50 | } |
| 49 | 51 | ||
| 50 | next_state = {}; | 52 | next_state = {}; |
| 51 | const auto camera_data = npad_device->GetCamera(); | 53 | const auto& camera_data = npad_device->GetCamera(); |
| 52 | auto filtered_image = camera_data.data; | 54 | auto filtered_image = camera_data.data; |
| 53 | 55 | ||
| 54 | RemoveLowIntensityData(filtered_image); | 56 | RemoveLowIntensityData(filtered_image); |
| @@ -83,7 +85,7 @@ void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType ty | |||
| 83 | } | 85 | } |
| 84 | 86 | ||
| 85 | next_state.sampling_number = camera_data.sample; | 87 | next_state.sampling_number = camera_data.sample; |
| 86 | next_state.timestamp = next_state.timestamp + 131; | 88 | next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count(); |
| 87 | next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; | 89 | next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; |
| 88 | shared_memory->clustering_lifo.WriteNextEntry(next_state); | 90 | shared_memory->clustering_lifo.WriteNextEntry(next_state); |
| 89 | 91 | ||
| @@ -202,14 +204,14 @@ ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster( | |||
| 202 | } | 204 | } |
| 203 | 205 | ||
| 204 | u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { | 206 | u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { |
| 205 | if ((y * width) + x > data.size()) { | 207 | if ((y * width) + x >= data.size()) { |
| 206 | return 0; | 208 | return 0; |
| 207 | } | 209 | } |
| 208 | return data[(y * width) + x]; | 210 | return data[(y * width) + x]; |
| 209 | } | 211 | } |
| 210 | 212 | ||
| 211 | void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) { | 213 | void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) { |
| 212 | if ((y * width) + x > data.size()) { | 214 | if ((y * width) + x >= data.size()) { |
| 213 | return; | 215 | return; |
| 214 | } | 216 | } |
| 215 | data[(y * width) + x] = value; | 217 | data[(y * width) + x] = value; |
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h index dc01a8ea7..83f34734a 100644 --- a/src/core/hle/service/hid/irsensor/clustering_processor.h +++ b/src/core/hle/service/hid/irsensor/clustering_processor.h | |||
| @@ -8,6 +8,10 @@ | |||
| 8 | #include "core/hle/service/hid/irs_ring_lifo.h" | 8 | #include "core/hle/service/hid/irs_ring_lifo.h" |
| 9 | #include "core/hle/service/hid/irsensor/processor_base.h" | 9 | #include "core/hle/service/hid/irsensor/processor_base.h" |
| 10 | 10 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 11 | namespace Core::HID { | 15 | namespace Core::HID { |
| 12 | class EmulatedController; | 16 | class EmulatedController; |
| 13 | } // namespace Core::HID | 17 | } // namespace Core::HID |
| @@ -15,8 +19,7 @@ class EmulatedController; | |||
| 15 | namespace Service::IRS { | 19 | namespace Service::IRS { |
| 16 | class ClusteringProcessor final : public ProcessorBase { | 20 | class ClusteringProcessor final : public ProcessorBase { |
| 17 | public: | 21 | public: |
| 18 | explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_, | 22 | explicit ClusteringProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format, |
| 19 | Core::IrSensor::DeviceFormat& device_format, | ||
| 20 | std::size_t npad_index); | 23 | std::size_t npad_index); |
| 21 | ~ClusteringProcessor() override; | 24 | ~ClusteringProcessor() override; |
| 22 | 25 | ||
| @@ -106,5 +109,7 @@ private: | |||
| 106 | Core::IrSensor::DeviceFormat& device; | 109 | Core::IrSensor::DeviceFormat& device; |
| 107 | Core::HID::EmulatedController* npad_device; | 110 | Core::HID::EmulatedController* npad_device; |
| 108 | int callback_key{}; | 111 | int callback_key{}; |
| 112 | |||
| 113 | Core::System& system; | ||
| 109 | }; | 114 | }; |
| 110 | } // namespace Service::IRS | 115 | } // namespace Service::IRS |
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp index 803a6277c..22067a591 100644 --- a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp +++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp | |||
| @@ -49,7 +49,7 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType | |||
| 49 | return; | 49 | return; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | const auto camera_data = npad_device->GetCamera(); | 52 | const auto& camera_data = npad_device->GetCamera(); |
| 53 | 53 | ||
| 54 | // This indicates how much ambient light is present | 54 | // This indicates how much ambient light is present |
| 55 | processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; | 55 | processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; |
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp index dbaca420a..cf045bda7 100644 --- a/src/core/hle/service/hid/irsensor/moment_processor.cpp +++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp | |||
| @@ -1,24 +1,137 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hid/emulated_controller.h" | ||
| 7 | #include "core/hid/hid_core.h" | ||
| 4 | #include "core/hle/service/hid/irsensor/moment_processor.h" | 8 | #include "core/hle/service/hid/irsensor/moment_processor.h" |
| 5 | 9 | ||
| 6 | namespace Service::IRS { | 10 | namespace Service::IRS { |
| 7 | MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format) | 11 | static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size40x30; |
| 8 | : device(device_format) { | 12 | static constexpr std::size_t ImageWidth = 40; |
| 13 | static constexpr std::size_t ImageHeight = 30; | ||
| 14 | |||
| 15 | MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format, | ||
| 16 | std::size_t npad_index) | ||
| 17 | : device(device_format), system{system_} { | ||
| 18 | npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index); | ||
| 19 | |||
| 9 | device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; | 20 | device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; |
| 10 | device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; | 21 | device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
| 11 | device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; | 22 | device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
| 23 | |||
| 24 | shared_memory = std::construct_at( | ||
| 25 | reinterpret_cast<MomentSharedMemory*>(&device_format.state.processor_raw_data)); | ||
| 26 | |||
| 27 | Core::HID::ControllerUpdateCallback engine_callback{ | ||
| 28 | .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, | ||
| 29 | .is_npad_service = true, | ||
| 30 | }; | ||
| 31 | callback_key = npad_device->SetCallback(engine_callback); | ||
| 12 | } | 32 | } |
| 13 | 33 | ||
| 14 | MomentProcessor::~MomentProcessor() = default; | 34 | MomentProcessor::~MomentProcessor() { |
| 35 | npad_device->DeleteCallback(callback_key); | ||
| 36 | }; | ||
| 15 | 37 | ||
| 16 | void MomentProcessor::StartProcessor() {} | 38 | void MomentProcessor::StartProcessor() { |
| 39 | device.camera_status = Core::IrSensor::IrCameraStatus::Available; | ||
| 40 | device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; | ||
| 41 | } | ||
| 17 | 42 | ||
| 18 | void MomentProcessor::SuspendProcessor() {} | 43 | void MomentProcessor::SuspendProcessor() {} |
| 19 | 44 | ||
| 20 | void MomentProcessor::StopProcessor() {} | 45 | void MomentProcessor::StopProcessor() {} |
| 21 | 46 | ||
| 47 | void MomentProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { | ||
| 48 | if (type != Core::HID::ControllerTriggerType::IrSensor) { | ||
| 49 | return; | ||
| 50 | } | ||
| 51 | |||
| 52 | next_state = {}; | ||
| 53 | const auto& camera_data = npad_device->GetCamera(); | ||
| 54 | |||
| 55 | const auto window_width = static_cast<std::size_t>(current_config.window_of_interest.width); | ||
| 56 | const auto window_height = static_cast<std::size_t>(current_config.window_of_interest.height); | ||
| 57 | const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x); | ||
| 58 | const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y); | ||
| 59 | |||
| 60 | const std::size_t block_width = window_width / Columns; | ||
| 61 | const std::size_t block_height = window_height / Rows; | ||
| 62 | |||
| 63 | for (std::size_t row = 0; row < Rows; row++) { | ||
| 64 | for (std::size_t column = 0; column < Columns; column++) { | ||
| 65 | const size_t x_pos = (column * block_width) + window_start_x; | ||
| 66 | const size_t y_pos = (row * block_height) + window_start_y; | ||
| 67 | auto& statistic = next_state.statistic[column + (row * Columns)]; | ||
| 68 | statistic = GetStatistic(camera_data.data, x_pos, y_pos, block_width, block_height); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | next_state.sampling_number = camera_data.sample; | ||
| 73 | next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count(); | ||
| 74 | next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; | ||
| 75 | shared_memory->moment_lifo.WriteNextEntry(next_state); | ||
| 76 | |||
| 77 | if (!IsProcessorActive()) { | ||
| 78 | StartProcessor(); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { | ||
| 83 | if ((y * ImageWidth) + x >= data.size()) { | ||
| 84 | return 0; | ||
| 85 | } | ||
| 86 | return data[(y * ImageWidth) + x]; | ||
| 87 | } | ||
| 88 | |||
| 89 | MomentProcessor::MomentStatistic MomentProcessor::GetStatistic(const std::vector<u8>& data, | ||
| 90 | std::size_t start_x, | ||
| 91 | std::size_t start_y, | ||
| 92 | std::size_t width, | ||
| 93 | std::size_t height) const { | ||
| 94 | // The actual implementation is always 320x240 | ||
| 95 | static constexpr std::size_t RealWidth = 320; | ||
| 96 | static constexpr std::size_t RealHeight = 240; | ||
| 97 | static constexpr std::size_t Threshold = 30; | ||
| 98 | MomentStatistic statistic{}; | ||
| 99 | std::size_t active_points{}; | ||
| 100 | |||
| 101 | // Sum all data points on the block that meet with the threshold | ||
| 102 | for (std::size_t y = 0; y < width; y++) { | ||
| 103 | for (std::size_t x = 0; x < height; x++) { | ||
| 104 | const size_t x_pos = x + start_x; | ||
| 105 | const size_t y_pos = y + start_y; | ||
| 106 | const auto pixel = | ||
| 107 | GetPixel(data, x_pos * ImageWidth / RealWidth, y_pos * ImageHeight / RealHeight); | ||
| 108 | |||
| 109 | if (pixel < Threshold) { | ||
| 110 | continue; | ||
| 111 | } | ||
| 112 | |||
| 113 | statistic.average_intensity += pixel; | ||
| 114 | |||
| 115 | statistic.centroid.x += static_cast<float>(x_pos); | ||
| 116 | statistic.centroid.y += static_cast<float>(y_pos); | ||
| 117 | |||
| 118 | active_points++; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | // Return an empty field if no points were available | ||
| 123 | if (active_points == 0) { | ||
| 124 | return {}; | ||
| 125 | } | ||
| 126 | |||
| 127 | // Finally calculate the actual centroid and average intensity | ||
| 128 | statistic.centroid.x /= static_cast<float>(active_points); | ||
| 129 | statistic.centroid.y /= static_cast<float>(active_points); | ||
| 130 | statistic.average_intensity /= static_cast<f32>(width * height); | ||
| 131 | |||
| 132 | return statistic; | ||
| 133 | } | ||
| 134 | |||
| 22 | void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { | 135 | void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { |
| 23 | current_config.camera_config.exposure_time = config.camera_config.exposure_time; | 136 | current_config.camera_config.exposure_time = config.camera_config.exposure_time; |
| 24 | current_config.camera_config.gain = config.camera_config.gain; | 137 | current_config.camera_config.gain = config.camera_config.gain; |
| @@ -29,6 +142,8 @@ void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig conf | |||
| 29 | current_config.preprocess = | 142 | current_config.preprocess = |
| 30 | static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); | 143 | static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); |
| 31 | current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; | 144 | current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; |
| 145 | |||
| 146 | npad_device->SetCameraFormat(format); | ||
| 32 | } | 147 | } |
| 33 | 148 | ||
| 34 | } // namespace Service::IRS | 149 | } // namespace Service::IRS |
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h index d4bd22e0f..398cfbdc1 100644 --- a/src/core/hle/service/hid/irsensor/moment_processor.h +++ b/src/core/hle/service/hid/irsensor/moment_processor.h | |||
| @@ -6,12 +6,22 @@ | |||
| 6 | #include "common/bit_field.h" | 6 | #include "common/bit_field.h" |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/hid/irs_types.h" | 8 | #include "core/hid/irs_types.h" |
| 9 | #include "core/hle/service/hid/irs_ring_lifo.h" | ||
| 9 | #include "core/hle/service/hid/irsensor/processor_base.h" | 10 | #include "core/hle/service/hid/irsensor/processor_base.h" |
| 10 | 11 | ||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Core::HID { | ||
| 17 | class EmulatedController; | ||
| 18 | } // namespace Core::HID | ||
| 19 | |||
| 11 | namespace Service::IRS { | 20 | namespace Service::IRS { |
| 12 | class MomentProcessor final : public ProcessorBase { | 21 | class MomentProcessor final : public ProcessorBase { |
| 13 | public: | 22 | public: |
| 14 | explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format); | 23 | explicit MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format, |
| 24 | std::size_t npad_index); | ||
| 15 | ~MomentProcessor() override; | 25 | ~MomentProcessor() override; |
| 16 | 26 | ||
| 17 | // Called when the processor is initialized | 27 | // Called when the processor is initialized |
| @@ -27,6 +37,9 @@ public: | |||
| 27 | void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); | 37 | void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); |
| 28 | 38 | ||
| 29 | private: | 39 | private: |
| 40 | static constexpr std::size_t Columns = 8; | ||
| 41 | static constexpr std::size_t Rows = 6; | ||
| 42 | |||
| 30 | // This is nn::irsensor::MomentProcessorConfig | 43 | // This is nn::irsensor::MomentProcessorConfig |
| 31 | struct MomentProcessorConfig { | 44 | struct MomentProcessorConfig { |
| 32 | Core::IrSensor::CameraConfig camera_config; | 45 | Core::IrSensor::CameraConfig camera_config; |
| @@ -50,12 +63,29 @@ private: | |||
| 50 | u64 timestamp; | 63 | u64 timestamp; |
| 51 | Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; | 64 | Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; |
| 52 | INSERT_PADDING_BYTES(4); | 65 | INSERT_PADDING_BYTES(4); |
| 53 | std::array<MomentStatistic, 0x30> stadistic; | 66 | std::array<MomentStatistic, Columns * Rows> statistic; |
| 54 | }; | 67 | }; |
| 55 | static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); | 68 | static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); |
| 56 | 69 | ||
| 70 | struct MomentSharedMemory { | ||
| 71 | Service::IRS::Lifo<MomentProcessorState, 6> moment_lifo; | ||
| 72 | }; | ||
| 73 | static_assert(sizeof(MomentSharedMemory) == 0xE20, "MomentSharedMemory is an invalid size"); | ||
| 74 | |||
| 75 | void OnControllerUpdate(Core::HID::ControllerTriggerType type); | ||
| 76 | u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const; | ||
| 77 | MomentStatistic GetStatistic(const std::vector<u8>& data, std::size_t start_x, | ||
| 78 | std::size_t start_y, std::size_t width, std::size_t height) const; | ||
| 79 | |||
| 80 | MomentSharedMemory* shared_memory = nullptr; | ||
| 81 | MomentProcessorState next_state{}; | ||
| 82 | |||
| 57 | MomentProcessorConfig current_config{}; | 83 | MomentProcessorConfig current_config{}; |
| 58 | Core::IrSensor::DeviceFormat& device; | 84 | Core::IrSensor::DeviceFormat& device; |
| 85 | Core::HID::EmulatedController* npad_device; | ||
| 86 | int callback_key{}; | ||
| 87 | |||
| 88 | Core::System& system; | ||
| 59 | }; | 89 | }; |
| 60 | 90 | ||
| 61 | } // namespace Service::IRS | 91 | } // namespace Service::IRS |
diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp new file mode 100644 index 000000000..e76d4eea9 --- /dev/null +++ b/src/core/hle/service/hid/resource_manager.cpp | |||
| @@ -0,0 +1,241 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/core_timing.h" | ||
| 7 | #include "core/hid/hid_core.h" | ||
| 8 | #include "core/hle/kernel/k_shared_memory.h" | ||
| 9 | #include "core/hle/service/hid/resource_manager.h" | ||
| 10 | #include "core/hle/service/ipc_helpers.h" | ||
| 11 | |||
| 12 | #include "core/hle/service/hid/controllers/console_six_axis.h" | ||
| 13 | #include "core/hle/service/hid/controllers/debug_pad.h" | ||
| 14 | #include "core/hle/service/hid/controllers/gesture.h" | ||
| 15 | #include "core/hle/service/hid/controllers/keyboard.h" | ||
| 16 | #include "core/hle/service/hid/controllers/mouse.h" | ||
| 17 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 18 | #include "core/hle/service/hid/controllers/palma.h" | ||
| 19 | #include "core/hle/service/hid/controllers/seven_six_axis.h" | ||
| 20 | #include "core/hle/service/hid/controllers/six_axis.h" | ||
| 21 | #include "core/hle/service/hid/controllers/stubbed.h" | ||
| 22 | #include "core/hle/service/hid/controllers/touchscreen.h" | ||
| 23 | #include "core/hle/service/hid/controllers/xpad.h" | ||
| 24 | |||
| 25 | namespace Service::HID { | ||
| 26 | |||
| 27 | // Updating period for each HID device. | ||
| 28 | // Period time is obtained by measuring the number of samples in a second on HW using a homebrew | ||
| 29 | // Correct npad_update_ns is 4ms this is overclocked to lower input lag | ||
| 30 | constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) | ||
| 31 | constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz) | ||
| 32 | constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) | ||
| 33 | constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) | ||
| 34 | |||
| 35 | ResourceManager::ResourceManager(Core::System& system_) | ||
| 36 | : system{system_}, service_context{system_, "hid"} {} | ||
| 37 | |||
| 38 | ResourceManager::~ResourceManager() = default; | ||
| 39 | |||
| 40 | void ResourceManager::Initialize() { | ||
| 41 | if (is_initialized) { | ||
| 42 | return; | ||
| 43 | } | ||
| 44 | |||
| 45 | u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); | ||
| 46 | debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory); | ||
| 47 | mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory); | ||
| 48 | debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory); | ||
| 49 | keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory); | ||
| 50 | unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory); | ||
| 51 | npad = std::make_shared<NPad>(system.HIDCore(), shared_memory, service_context); | ||
| 52 | gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory); | ||
| 53 | touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory); | ||
| 54 | xpad = std::make_shared<XPad>(system.HIDCore(), shared_memory); | ||
| 55 | |||
| 56 | palma = std::make_shared<Palma>(system.HIDCore(), shared_memory, service_context); | ||
| 57 | |||
| 58 | home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory); | ||
| 59 | sleep_button = std::make_shared<SleepButton>(system.HIDCore(), shared_memory); | ||
| 60 | capture_button = std::make_shared<CaptureButton>(system.HIDCore(), shared_memory); | ||
| 61 | |||
| 62 | six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad); | ||
| 63 | console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory); | ||
| 64 | seven_six_axis = std::make_shared<SevenSixAxis>(system); | ||
| 65 | |||
| 66 | home_button->SetCommonHeaderOffset(0x4C00); | ||
| 67 | sleep_button->SetCommonHeaderOffset(0x4E00); | ||
| 68 | capture_button->SetCommonHeaderOffset(0x5000); | ||
| 69 | unique_pad->SetCommonHeaderOffset(0x5A00); | ||
| 70 | debug_mouse->SetCommonHeaderOffset(0x3DC00); | ||
| 71 | |||
| 72 | // Homebrew doesn't try to activate some controllers, so we activate them by default | ||
| 73 | npad->Activate(); | ||
| 74 | six_axis->Activate(); | ||
| 75 | touch_screen->Activate(); | ||
| 76 | |||
| 77 | system.HIDCore().ReloadInputDevices(); | ||
| 78 | is_initialized = true; | ||
| 79 | } | ||
| 80 | std::shared_ptr<CaptureButton> ResourceManager::GetCaptureButton() const { | ||
| 81 | return capture_button; | ||
| 82 | } | ||
| 83 | |||
| 84 | std::shared_ptr<ConsoleSixAxis> ResourceManager::GetConsoleSixAxis() const { | ||
| 85 | return console_six_axis; | ||
| 86 | } | ||
| 87 | |||
| 88 | std::shared_ptr<DebugMouse> ResourceManager::GetDebugMouse() const { | ||
| 89 | return debug_mouse; | ||
| 90 | } | ||
| 91 | |||
| 92 | std::shared_ptr<DebugPad> ResourceManager::GetDebugPad() const { | ||
| 93 | return debug_pad; | ||
| 94 | } | ||
| 95 | |||
| 96 | std::shared_ptr<Gesture> ResourceManager::GetGesture() const { | ||
| 97 | return gesture; | ||
| 98 | } | ||
| 99 | |||
| 100 | std::shared_ptr<HomeButton> ResourceManager::GetHomeButton() const { | ||
| 101 | return home_button; | ||
| 102 | } | ||
| 103 | |||
| 104 | std::shared_ptr<Keyboard> ResourceManager::GetKeyboard() const { | ||
| 105 | return keyboard; | ||
| 106 | } | ||
| 107 | |||
| 108 | std::shared_ptr<Mouse> ResourceManager::GetMouse() const { | ||
| 109 | return mouse; | ||
| 110 | } | ||
| 111 | |||
| 112 | std::shared_ptr<NPad> ResourceManager::GetNpad() const { | ||
| 113 | return npad; | ||
| 114 | } | ||
| 115 | |||
| 116 | std::shared_ptr<Palma> ResourceManager::GetPalma() const { | ||
| 117 | return palma; | ||
| 118 | } | ||
| 119 | |||
| 120 | std::shared_ptr<SevenSixAxis> ResourceManager::GetSevenSixAxis() const { | ||
| 121 | return seven_six_axis; | ||
| 122 | } | ||
| 123 | |||
| 124 | std::shared_ptr<SixAxis> ResourceManager::GetSixAxis() const { | ||
| 125 | return six_axis; | ||
| 126 | } | ||
| 127 | |||
| 128 | std::shared_ptr<SleepButton> ResourceManager::GetSleepButton() const { | ||
| 129 | return sleep_button; | ||
| 130 | } | ||
| 131 | |||
| 132 | std::shared_ptr<TouchScreen> ResourceManager::GetTouchScreen() const { | ||
| 133 | return touch_screen; | ||
| 134 | } | ||
| 135 | |||
| 136 | std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const { | ||
| 137 | return unique_pad; | ||
| 138 | } | ||
| 139 | |||
| 140 | void ResourceManager::UpdateControllers(std::uintptr_t user_data, | ||
| 141 | std::chrono::nanoseconds ns_late) { | ||
| 142 | auto& core_timing = system.CoreTiming(); | ||
| 143 | debug_pad->OnUpdate(core_timing); | ||
| 144 | unique_pad->OnUpdate(core_timing); | ||
| 145 | gesture->OnUpdate(core_timing); | ||
| 146 | touch_screen->OnUpdate(core_timing); | ||
| 147 | palma->OnUpdate(core_timing); | ||
| 148 | home_button->OnUpdate(core_timing); | ||
| 149 | sleep_button->OnUpdate(core_timing); | ||
| 150 | capture_button->OnUpdate(core_timing); | ||
| 151 | xpad->OnUpdate(core_timing); | ||
| 152 | } | ||
| 153 | |||
| 154 | void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | ||
| 155 | auto& core_timing = system.CoreTiming(); | ||
| 156 | npad->OnUpdate(core_timing); | ||
| 157 | } | ||
| 158 | |||
| 159 | void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data, | ||
| 160 | std::chrono::nanoseconds ns_late) { | ||
| 161 | auto& core_timing = system.CoreTiming(); | ||
| 162 | mouse->OnUpdate(core_timing); | ||
| 163 | debug_mouse->OnUpdate(core_timing); | ||
| 164 | keyboard->OnUpdate(core_timing); | ||
| 165 | } | ||
| 166 | |||
| 167 | void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | ||
| 168 | auto& core_timing = system.CoreTiming(); | ||
| 169 | six_axis->OnUpdate(core_timing); | ||
| 170 | seven_six_axis->OnUpdate(core_timing); | ||
| 171 | console_six_axis->OnUpdate(core_timing); | ||
| 172 | } | ||
| 173 | |||
| 174 | IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource) | ||
| 175 | : ServiceFramework{system_, "IAppletResource"} { | ||
| 176 | static const FunctionInfo functions[] = { | ||
| 177 | {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, | ||
| 178 | }; | ||
| 179 | RegisterHandlers(functions); | ||
| 180 | |||
| 181 | resource->Initialize(); | ||
| 182 | |||
| 183 | // Register update callbacks | ||
| 184 | npad_update_event = Core::Timing::CreateEvent( | ||
| 185 | "HID::UpdatePadCallback", | ||
| 186 | [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) | ||
| 187 | -> std::optional<std::chrono::nanoseconds> { | ||
| 188 | const auto guard = LockService(); | ||
| 189 | resource->UpdateNpad(user_data, ns_late); | ||
| 190 | return std::nullopt; | ||
| 191 | }); | ||
| 192 | default_update_event = Core::Timing::CreateEvent( | ||
| 193 | "HID::UpdateDefaultCallback", | ||
| 194 | [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) | ||
| 195 | -> std::optional<std::chrono::nanoseconds> { | ||
| 196 | const auto guard = LockService(); | ||
| 197 | resource->UpdateControllers(user_data, ns_late); | ||
| 198 | return std::nullopt; | ||
| 199 | }); | ||
| 200 | mouse_keyboard_update_event = Core::Timing::CreateEvent( | ||
| 201 | "HID::UpdateMouseKeyboardCallback", | ||
| 202 | [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) | ||
| 203 | -> std::optional<std::chrono::nanoseconds> { | ||
| 204 | const auto guard = LockService(); | ||
| 205 | resource->UpdateMouseKeyboard(user_data, ns_late); | ||
| 206 | return std::nullopt; | ||
| 207 | }); | ||
| 208 | motion_update_event = Core::Timing::CreateEvent( | ||
| 209 | "HID::UpdateMotionCallback", | ||
| 210 | [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) | ||
| 211 | -> std::optional<std::chrono::nanoseconds> { | ||
| 212 | const auto guard = LockService(); | ||
| 213 | resource->UpdateMotion(user_data, ns_late); | ||
| 214 | return std::nullopt; | ||
| 215 | }); | ||
| 216 | |||
| 217 | system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); | ||
| 218 | system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, | ||
| 219 | default_update_event); | ||
| 220 | system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, | ||
| 221 | mouse_keyboard_update_event); | ||
| 222 | system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, | ||
| 223 | motion_update_event); | ||
| 224 | } | ||
| 225 | |||
| 226 | IAppletResource::~IAppletResource() { | ||
| 227 | system.CoreTiming().UnscheduleEvent(npad_update_event, 0); | ||
| 228 | system.CoreTiming().UnscheduleEvent(default_update_event, 0); | ||
| 229 | system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); | ||
| 230 | system.CoreTiming().UnscheduleEvent(motion_update_event, 0); | ||
| 231 | } | ||
| 232 | |||
| 233 | void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) { | ||
| 234 | LOG_DEBUG(Service_HID, "called"); | ||
| 235 | |||
| 236 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 237 | rb.Push(ResultSuccess); | ||
| 238 | rb.PushCopyObjects(&system.Kernel().GetHidSharedMem()); | ||
| 239 | } | ||
| 240 | |||
| 241 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h new file mode 100644 index 000000000..2b6a9b5e6 --- /dev/null +++ b/src/core/hle/service/hid/resource_manager.h | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/service/kernel_helpers.h" | ||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Core::Timing { | ||
| 10 | struct EventType; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::HID { | ||
| 14 | class Controller_Stubbed; | ||
| 15 | class ConsoleSixAxis; | ||
| 16 | class DebugPad; | ||
| 17 | class Gesture; | ||
| 18 | class Keyboard; | ||
| 19 | class Mouse; | ||
| 20 | class NPad; | ||
| 21 | class Palma; | ||
| 22 | class SevenSixAxis; | ||
| 23 | class SixAxis; | ||
| 24 | class TouchScreen; | ||
| 25 | class XPad; | ||
| 26 | |||
| 27 | using CaptureButton = Controller_Stubbed; | ||
| 28 | using DebugMouse = Controller_Stubbed; | ||
| 29 | using HomeButton = Controller_Stubbed; | ||
| 30 | using SleepButton = Controller_Stubbed; | ||
| 31 | using UniquePad = Controller_Stubbed; | ||
| 32 | |||
| 33 | class ResourceManager { | ||
| 34 | |||
| 35 | public: | ||
| 36 | explicit ResourceManager(Core::System& system_); | ||
| 37 | ~ResourceManager(); | ||
| 38 | |||
| 39 | void Initialize(); | ||
| 40 | |||
| 41 | std::shared_ptr<CaptureButton> GetCaptureButton() const; | ||
| 42 | std::shared_ptr<ConsoleSixAxis> GetConsoleSixAxis() const; | ||
| 43 | std::shared_ptr<DebugMouse> GetDebugMouse() const; | ||
| 44 | std::shared_ptr<DebugPad> GetDebugPad() const; | ||
| 45 | std::shared_ptr<Gesture> GetGesture() const; | ||
| 46 | std::shared_ptr<HomeButton> GetHomeButton() const; | ||
| 47 | std::shared_ptr<Keyboard> GetKeyboard() const; | ||
| 48 | std::shared_ptr<Mouse> GetMouse() const; | ||
| 49 | std::shared_ptr<NPad> GetNpad() const; | ||
| 50 | std::shared_ptr<Palma> GetPalma() const; | ||
| 51 | std::shared_ptr<SevenSixAxis> GetSevenSixAxis() const; | ||
| 52 | std::shared_ptr<SixAxis> GetSixAxis() const; | ||
| 53 | std::shared_ptr<SleepButton> GetSleepButton() const; | ||
| 54 | std::shared_ptr<TouchScreen> GetTouchScreen() const; | ||
| 55 | std::shared_ptr<UniquePad> GetUniquePad() const; | ||
| 56 | |||
| 57 | void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 58 | void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 59 | void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 60 | void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 61 | |||
| 62 | private: | ||
| 63 | bool is_initialized{false}; | ||
| 64 | |||
| 65 | std::shared_ptr<CaptureButton> capture_button = nullptr; | ||
| 66 | std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr; | ||
| 67 | std::shared_ptr<DebugMouse> debug_mouse = nullptr; | ||
| 68 | std::shared_ptr<DebugPad> debug_pad = nullptr; | ||
| 69 | std::shared_ptr<Gesture> gesture = nullptr; | ||
| 70 | std::shared_ptr<HomeButton> home_button = nullptr; | ||
| 71 | std::shared_ptr<Keyboard> keyboard = nullptr; | ||
| 72 | std::shared_ptr<Mouse> mouse = nullptr; | ||
| 73 | std::shared_ptr<NPad> npad = nullptr; | ||
| 74 | std::shared_ptr<Palma> palma = nullptr; | ||
| 75 | std::shared_ptr<SevenSixAxis> seven_six_axis = nullptr; | ||
| 76 | std::shared_ptr<SixAxis> six_axis = nullptr; | ||
| 77 | std::shared_ptr<SleepButton> sleep_button = nullptr; | ||
| 78 | std::shared_ptr<TouchScreen> touch_screen = nullptr; | ||
| 79 | std::shared_ptr<UniquePad> unique_pad = nullptr; | ||
| 80 | std::shared_ptr<XPad> xpad = nullptr; | ||
| 81 | |||
| 82 | // TODO: Create these resources | ||
| 83 | // std::shared_ptr<AudioControl> audio_control = nullptr; | ||
| 84 | // std::shared_ptr<ButtonConfig> button_config = nullptr; | ||
| 85 | // std::shared_ptr<Config> config = nullptr; | ||
| 86 | // std::shared_ptr<Connection> connection = nullptr; | ||
| 87 | // std::shared_ptr<CustomConfig> custom_config = nullptr; | ||
| 88 | // std::shared_ptr<Digitizer> digitizer = nullptr; | ||
| 89 | // std::shared_ptr<Hdls> hdls = nullptr; | ||
| 90 | // std::shared_ptr<PlayReport> play_report = nullptr; | ||
| 91 | // std::shared_ptr<Rail> rail = nullptr; | ||
| 92 | |||
| 93 | Core::System& system; | ||
| 94 | KernelHelpers::ServiceContext service_context; | ||
| 95 | }; | ||
| 96 | |||
| 97 | class IAppletResource final : public ServiceFramework<IAppletResource> { | ||
| 98 | public: | ||
| 99 | explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource); | ||
| 100 | ~IAppletResource() override; | ||
| 101 | |||
| 102 | private: | ||
| 103 | void GetSharedMemoryHandle(HLERequestContext& ctx); | ||
| 104 | |||
| 105 | std::shared_ptr<Core::Timing::EventType> npad_update_event; | ||
| 106 | std::shared_ptr<Core::Timing::EventType> default_update_event; | ||
| 107 | std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; | ||
| 108 | std::shared_ptr<Core::Timing::EventType> motion_update_event; | ||
| 109 | }; | ||
| 110 | |||
| 111 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h index 65eb7ea02..0816784e0 100644 --- a/src/core/hle/service/hid/ring_lifo.h +++ b/src/core/hle/service/hid/ring_lifo.h | |||
| @@ -32,15 +32,15 @@ struct Lifo { | |||
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | std::size_t GetPreviousEntryIndex() const { | 34 | std::size_t GetPreviousEntryIndex() const { |
| 35 | return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count); | 35 | return static_cast<size_t>((buffer_tail + max_buffer_size - 1) % max_buffer_size); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | std::size_t GetNextEntryIndex() const { | 38 | std::size_t GetNextEntryIndex() const { |
| 39 | return static_cast<size_t>((buffer_tail + 1) % total_buffer_count); | 39 | return static_cast<size_t>((buffer_tail + 1) % max_buffer_size); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | void WriteNextEntry(const State& new_state) { | 42 | void WriteNextEntry(const State& new_state) { |
| 43 | if (buffer_count < total_buffer_count - 1) { | 43 | if (buffer_count < static_cast<s64>(max_buffer_size) - 1) { |
| 44 | buffer_count++; | 44 | buffer_count++; |
| 45 | } | 45 | } |
| 46 | buffer_tail = GetNextEntryIndex(); | 46 | buffer_tail = GetNextEntryIndex(); |
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 7927f8264..961f89a14 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp | |||
| @@ -115,12 +115,20 @@ public: | |||
| 115 | {400, nullptr, "InitializeSystem"}, | 115 | {400, nullptr, "InitializeSystem"}, |
| 116 | {401, nullptr, "FinalizeSystem"}, | 116 | {401, nullptr, "FinalizeSystem"}, |
| 117 | {402, nullptr, "SetOperationMode"}, | 117 | {402, nullptr, "SetOperationMode"}, |
| 118 | {403, nullptr, "InitializeSystem2"}, | 118 | {403, &ISystemLocalCommunicationService::InitializeSystem2, "InitializeSystem2"}, |
| 119 | }; | 119 | }; |
| 120 | // clang-format on | 120 | // clang-format on |
| 121 | 121 | ||
| 122 | RegisterHandlers(functions); | 122 | RegisterHandlers(functions); |
| 123 | } | 123 | } |
| 124 | |||
| 125 | private: | ||
| 126 | void InitializeSystem2(HLERequestContext& ctx) { | ||
| 127 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 128 | |||
| 129 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 130 | rb.Push(ResultSuccess); | ||
| 131 | } | ||
| 124 | }; | 132 | }; |
| 125 | 133 | ||
| 126 | class IUserLocalCommunicationService final | 134 | class IUserLocalCommunicationService final |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index c73035c77..97b6a9385 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -286,9 +286,14 @@ public: | |||
| 286 | rb.Push(ResultSuccess); | 286 | rb.Push(ResultSuccess); |
| 287 | } | 287 | } |
| 288 | 288 | ||
| 289 | bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const { | 289 | bool ValidateRegionForMap(Kernel::KProcessPageTable& page_table, VAddr start, |
| 290 | std::size_t size) const { | ||
| 290 | const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize}; | 291 | const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize}; |
| 291 | const auto start_info{page_table.QueryInfo(start - 1)}; | 292 | |
| 293 | Kernel::KMemoryInfo start_info; | ||
| 294 | Kernel::Svc::PageInfo page_info; | ||
| 295 | R_ASSERT( | ||
| 296 | page_table.QueryInfo(std::addressof(start_info), std::addressof(page_info), start - 1)); | ||
| 292 | 297 | ||
| 293 | if (start_info.GetState() != Kernel::KMemoryState::Free) { | 298 | if (start_info.GetState() != Kernel::KMemoryState::Free) { |
| 294 | return {}; | 299 | return {}; |
| @@ -298,7 +303,9 @@ public: | |||
| 298 | return {}; | 303 | return {}; |
| 299 | } | 304 | } |
| 300 | 305 | ||
| 301 | const auto end_info{page_table.QueryInfo(start + size)}; | 306 | Kernel::KMemoryInfo end_info; |
| 307 | R_ASSERT(page_table.QueryInfo(std::addressof(end_info), std::addressof(page_info), | ||
| 308 | start + size)); | ||
| 302 | 309 | ||
| 303 | if (end_info.GetState() != Kernel::KMemoryState::Free) { | 310 | if (end_info.GetState() != Kernel::KMemoryState::Free) { |
| 304 | return {}; | 311 | return {}; |
| @@ -307,7 +314,7 @@ public: | |||
| 307 | return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); | 314 | return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); |
| 308 | } | 315 | } |
| 309 | 316 | ||
| 310 | Result GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) { | 317 | Result GetAvailableMapRegion(Kernel::KProcessPageTable& page_table, u64 size, VAddr& out_addr) { |
| 311 | size = Common::AlignUp(size, Kernel::PageSize); | 318 | size = Common::AlignUp(size, Kernel::PageSize); |
| 312 | size += page_table.GetNumGuardPages() * Kernel::PageSize * 4; | 319 | size += page_table.GetNumGuardPages() * Kernel::PageSize * 4; |
| 313 | 320 | ||
| @@ -391,12 +398,8 @@ public: | |||
| 391 | 398 | ||
| 392 | if (bss_size) { | 399 | if (bss_size) { |
| 393 | auto block_guard = detail::ScopeExit([&] { | 400 | auto block_guard = detail::ScopeExit([&] { |
| 394 | page_table.UnmapCodeMemory( | 401 | page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size); |
| 395 | addr + nro_size, bss_addr, bss_size, | 402 | page_table.UnmapCodeMemory(addr, nro_addr, nro_size); |
| 396 | Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange); | ||
| 397 | page_table.UnmapCodeMemory( | ||
| 398 | addr, nro_addr, nro_size, | ||
| 399 | Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange); | ||
| 400 | }); | 403 | }); |
| 401 | 404 | ||
| 402 | const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)}; | 405 | const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)}; |
| @@ -578,21 +581,17 @@ public: | |||
| 578 | auto& page_table{system.ApplicationProcess()->GetPageTable()}; | 581 | auto& page_table{system.ApplicationProcess()->GetPageTable()}; |
| 579 | 582 | ||
| 580 | if (info.bss_size != 0) { | 583 | if (info.bss_size != 0) { |
| 581 | R_TRY(page_table.UnmapCodeMemory( | 584 | R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size + |
| 582 | info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address, | 585 | info.data_size, |
| 583 | info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); | 586 | info.bss_address, info.bss_size)); |
| 584 | } | 587 | } |
| 585 | 588 | ||
| 586 | R_TRY(page_table.UnmapCodeMemory( | 589 | R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size, |
| 587 | info.nro_address + info.text_size + info.ro_size, | 590 | info.src_addr + info.text_size + info.ro_size, |
| 588 | info.src_addr + info.text_size + info.ro_size, info.data_size, | 591 | info.data_size)); |
| 589 | Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); | 592 | R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size, |
| 590 | R_TRY(page_table.UnmapCodeMemory( | 593 | info.src_addr + info.text_size, info.ro_size)); |
| 591 | info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size, | 594 | R_TRY(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size)); |
| 592 | Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); | ||
| 593 | R_TRY(page_table.UnmapCodeMemory( | ||
| 594 | info.nro_address, info.src_addr, info.text_size, | ||
| 595 | Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); | ||
| 596 | return ResultSuccess; | 595 | return ResultSuccess; |
| 597 | } | 596 | } |
| 598 | 597 | ||
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index a71d26157..ad534177d 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/hid/hid_types.h" | 8 | #include "core/hid/hid_types.h" |
| 9 | #include "core/hle/kernel/k_event.h" | 9 | #include "core/hle/kernel/k_event.h" |
| 10 | #include "core/hle/service/hid/hid_util.h" | ||
| 10 | #include "core/hle/service/ipc_helpers.h" | 11 | #include "core/hle/service/ipc_helpers.h" |
| 11 | #include "core/hle/service/nfc/common/device.h" | 12 | #include "core/hle/service/nfc/common/device.h" |
| 12 | #include "core/hle/service/nfc/common/device_manager.h" | 13 | #include "core/hle/service/nfc/common/device_manager.h" |
| @@ -24,7 +25,7 @@ DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContex | |||
| 24 | 25 | ||
| 25 | for (u32 device_index = 0; device_index < devices.size(); device_index++) { | 26 | for (u32 device_index = 0; device_index < devices.size(); device_index++) { |
| 26 | devices[device_index] = | 27 | devices[device_index] = |
| 27 | std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system, | 28 | std::make_shared<NfcDevice>(HID::IndexToNpadIdType(device_index), system, |
| 28 | service_context, availability_change_event); | 29 | service_context, availability_change_event); |
| 29 | } | 30 | } |
| 30 | 31 | ||
diff --git a/src/core/hle/service/nvdrv/devices/ioctl_serialization.h b/src/core/hle/service/nvdrv/devices/ioctl_serialization.h new file mode 100644 index 000000000..b12bcd138 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/ioctl_serialization.h | |||
| @@ -0,0 +1,159 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <span> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "common/concepts.h" | ||
| 10 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | ||
| 11 | |||
| 12 | namespace Service::Nvidia::Devices { | ||
| 13 | |||
| 14 | struct IoctlOneArgTraits { | ||
| 15 | template <typename T, typename R, typename A, typename... B> | ||
| 16 | static A GetFirstArgImpl(R (T::*)(A, B...)); | ||
| 17 | }; | ||
| 18 | |||
| 19 | struct IoctlTwoArgTraits { | ||
| 20 | template <typename T, typename R, typename A, typename B, typename... C> | ||
| 21 | static A GetFirstArgImpl(R (T::*)(A, B, C...)); | ||
| 22 | |||
| 23 | template <typename T, typename R, typename A, typename B, typename... C> | ||
| 24 | static B GetSecondArgImpl(R (T::*)(A, B, C...)); | ||
| 25 | }; | ||
| 26 | |||
| 27 | struct Null {}; | ||
| 28 | |||
| 29 | // clang-format off | ||
| 30 | |||
| 31 | template <typename FixedArg, typename VarArg, typename InlInVarArg, typename InlOutVarArg, typename F> | ||
| 32 | NvResult WrapGeneric(F&& callable, std::span<const u8> input, std::span<const u8> inline_input, std::span<u8> output, std::span<u8> inline_output) { | ||
| 33 | constexpr bool HasFixedArg = !std::is_same_v<FixedArg, Null>; | ||
| 34 | constexpr bool HasVarArg = !std::is_same_v<VarArg, Null>; | ||
| 35 | constexpr bool HasInlInVarArg = !std::is_same_v<InlInVarArg, Null>; | ||
| 36 | constexpr bool HasInlOutVarArg = !std::is_same_v<InlOutVarArg, Null>; | ||
| 37 | |||
| 38 | // Declare the fixed-size input value. | ||
| 39 | FixedArg fixed{}; | ||
| 40 | size_t var_offset = 0; | ||
| 41 | |||
| 42 | if constexpr (HasFixedArg) { | ||
| 43 | // Read the fixed-size input value. | ||
| 44 | var_offset = std::min(sizeof(FixedArg), input.size()); | ||
| 45 | if (var_offset > 0) { | ||
| 46 | std::memcpy(&fixed, input.data(), var_offset); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | // Read the variable-sized inputs. | ||
| 51 | const size_t num_var_args = HasVarArg ? ((input.size() - var_offset) / sizeof(VarArg)) : 0; | ||
| 52 | std::vector<VarArg> var_args(num_var_args); | ||
| 53 | if constexpr (HasVarArg) { | ||
| 54 | if (num_var_args > 0) { | ||
| 55 | std::memcpy(var_args.data(), input.data() + var_offset, num_var_args * sizeof(VarArg)); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | const size_t num_inl_in_var_args = HasInlInVarArg ? (inline_input.size() / sizeof(InlInVarArg)) : 0; | ||
| 60 | std::vector<InlInVarArg> inl_in_var_args(num_inl_in_var_args); | ||
| 61 | if constexpr (HasInlInVarArg) { | ||
| 62 | if (num_inl_in_var_args > 0) { | ||
| 63 | std::memcpy(inl_in_var_args.data(), inline_input.data(), num_inl_in_var_args * sizeof(InlInVarArg)); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | // Construct inline output data. | ||
| 68 | const size_t num_inl_out_var_args = HasInlOutVarArg ? (inline_output.size() / sizeof(InlOutVarArg)) : 0; | ||
| 69 | std::vector<InlOutVarArg> inl_out_var_args(num_inl_out_var_args); | ||
| 70 | |||
| 71 | // Perform the call. | ||
| 72 | NvResult result = callable(fixed, var_args, inl_in_var_args, inl_out_var_args); | ||
| 73 | |||
| 74 | // Copy outputs. | ||
| 75 | if constexpr (HasFixedArg) { | ||
| 76 | if (output.size() > 0) { | ||
| 77 | std::memcpy(output.data(), &fixed, std::min(output.size(), sizeof(FixedArg))); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | if constexpr (HasVarArg) { | ||
| 82 | if (num_var_args > 0 && output.size() > var_offset) { | ||
| 83 | const size_t max_var_size = output.size() - var_offset; | ||
| 84 | std::memcpy(output.data() + var_offset, var_args.data(), std::min(max_var_size, num_var_args * sizeof(VarArg))); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | // Copy inline outputs. | ||
| 89 | if constexpr (HasInlOutVarArg) { | ||
| 90 | if (num_inl_out_var_args > 0) { | ||
| 91 | std::memcpy(inline_output.data(), inl_out_var_args.data(), num_inl_out_var_args * sizeof(InlOutVarArg)); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | // We're done. | ||
| 96 | return result; | ||
| 97 | } | ||
| 98 | |||
| 99 | template <typename Self, typename F, typename... Rest> | ||
| 100 | NvResult WrapFixed(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) { | ||
| 101 | using FixedArg = typename std::remove_reference_t<decltype(IoctlOneArgTraits::GetFirstArgImpl(callable))>; | ||
| 102 | |||
| 103 | const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult { | ||
| 104 | return (self->*callable)(fixed, std::forward<Rest>(rest)...); | ||
| 105 | }; | ||
| 106 | |||
| 107 | return WrapGeneric<FixedArg, Null, Null, Null>(std::move(Callable), input, {}, output, {}); | ||
| 108 | } | ||
| 109 | |||
| 110 | template <typename Self, typename F, typename... Rest> | ||
| 111 | NvResult WrapFixedInlOut(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output, Rest&&... rest) { | ||
| 112 | using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>; | ||
| 113 | using InlOutVarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type; | ||
| 114 | |||
| 115 | const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult { | ||
| 116 | return (self->*callable)(fixed, inl_out, std::forward<Rest>(rest)...); | ||
| 117 | }; | ||
| 118 | |||
| 119 | return WrapGeneric<FixedArg, Null, Null, InlOutVarArg>(std::move(Callable), input, {}, output, inline_output); | ||
| 120 | } | ||
| 121 | |||
| 122 | template <typename Self, typename F, typename... Rest> | ||
| 123 | NvResult WrapVariable(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) { | ||
| 124 | using VarArg = typename std::remove_reference_t<decltype(IoctlOneArgTraits::GetFirstArgImpl(callable))>::value_type; | ||
| 125 | |||
| 126 | const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult { | ||
| 127 | return (self->*callable)(var, std::forward<Rest>(rest)...); | ||
| 128 | }; | ||
| 129 | |||
| 130 | return WrapGeneric<Null, VarArg, Null, Null>(std::move(Callable), input, {}, output, {}); | ||
| 131 | } | ||
| 132 | |||
| 133 | template <typename Self, typename F, typename... Rest> | ||
| 134 | NvResult WrapFixedVariable(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) { | ||
| 135 | using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>; | ||
| 136 | using VarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type; | ||
| 137 | |||
| 138 | const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult { | ||
| 139 | return (self->*callable)(fixed, var, std::forward<Rest>(rest)...); | ||
| 140 | }; | ||
| 141 | |||
| 142 | return WrapGeneric<FixedArg, VarArg, Null, Null>(std::move(Callable), input, {}, output, {}); | ||
| 143 | } | ||
| 144 | |||
| 145 | template <typename Self, typename F, typename... Rest> | ||
| 146 | NvResult WrapFixedInlIn(Self* self, F&& callable, std::span<const u8> input, std::span<const u8> inline_input, std::span<u8> output, Rest&&... rest) { | ||
| 147 | using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>; | ||
| 148 | using InlInVarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type; | ||
| 149 | |||
| 150 | const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult { | ||
| 151 | return (self->*callable)(fixed, inl_in, std::forward<Rest>(rest)...); | ||
| 152 | }; | ||
| 153 | |||
| 154 | return WrapGeneric<FixedArg, Null, InlInVarArg, Null>(std::move(Callable), input, inline_input, output, {}); | ||
| 155 | } | ||
| 156 | |||
| 157 | // clang-format on | ||
| 158 | |||
| 159 | } // namespace Service::Nvidia::Devices | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 7d7bb8687..6b3639008 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/hle/service/nvdrv/core/container.h" | 12 | #include "core/hle/service/nvdrv/core/container.h" |
| 13 | #include "core/hle/service/nvdrv/core/nvmap.h" | 13 | #include "core/hle/service/nvdrv/core/nvmap.h" |
| 14 | #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" | ||
| 14 | #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" | 15 | #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" |
| 15 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" | 16 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" |
| 16 | #include "core/hle/service/nvdrv/nvdrv.h" | 17 | #include "core/hle/service/nvdrv/nvdrv.h" |
| @@ -33,21 +34,21 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> i | |||
| 33 | case 'A': | 34 | case 'A': |
| 34 | switch (command.cmd) { | 35 | switch (command.cmd) { |
| 35 | case 0x1: | 36 | case 0x1: |
| 36 | return BindChannel(input, output); | 37 | return WrapFixed(this, &nvhost_as_gpu::BindChannel, input, output); |
| 37 | case 0x2: | 38 | case 0x2: |
| 38 | return AllocateSpace(input, output); | 39 | return WrapFixed(this, &nvhost_as_gpu::AllocateSpace, input, output); |
| 39 | case 0x3: | 40 | case 0x3: |
| 40 | return FreeSpace(input, output); | 41 | return WrapFixed(this, &nvhost_as_gpu::FreeSpace, input, output); |
| 41 | case 0x5: | 42 | case 0x5: |
| 42 | return UnmapBuffer(input, output); | 43 | return WrapFixed(this, &nvhost_as_gpu::UnmapBuffer, input, output); |
| 43 | case 0x6: | 44 | case 0x6: |
| 44 | return MapBufferEx(input, output); | 45 | return WrapFixed(this, &nvhost_as_gpu::MapBufferEx, input, output); |
| 45 | case 0x8: | 46 | case 0x8: |
| 46 | return GetVARegions(input, output); | 47 | return WrapFixed(this, &nvhost_as_gpu::GetVARegions1, input, output); |
| 47 | case 0x9: | 48 | case 0x9: |
| 48 | return AllocAsEx(input, output); | 49 | return WrapFixed(this, &nvhost_as_gpu::AllocAsEx, input, output); |
| 49 | case 0x14: | 50 | case 0x14: |
| 50 | return Remap(input, output); | 51 | return WrapVariable(this, &nvhost_as_gpu::Remap, input, output); |
| 51 | default: | 52 | default: |
| 52 | break; | 53 | break; |
| 53 | } | 54 | } |
| @@ -72,7 +73,8 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i | |||
| 72 | case 'A': | 73 | case 'A': |
| 73 | switch (command.cmd) { | 74 | switch (command.cmd) { |
| 74 | case 0x8: | 75 | case 0x8: |
| 75 | return GetVARegions(input, output, inline_output); | 76 | return WrapFixedInlOut(this, &nvhost_as_gpu::GetVARegions3, input, output, |
| 77 | inline_output); | ||
| 76 | default: | 78 | default: |
| 77 | break; | 79 | break; |
| 78 | } | 80 | } |
| @@ -87,10 +89,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i | |||
| 87 | void nvhost_as_gpu::OnOpen(DeviceFD fd) {} | 89 | void nvhost_as_gpu::OnOpen(DeviceFD fd) {} |
| 88 | void nvhost_as_gpu::OnClose(DeviceFD fd) {} | 90 | void nvhost_as_gpu::OnClose(DeviceFD fd) {} |
| 89 | 91 | ||
| 90 | NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> output) { | 92 | NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { |
| 91 | IoctlAllocAsEx params{}; | ||
| 92 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 93 | |||
| 94 | LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size); | 93 | LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size); |
| 95 | 94 | ||
| 96 | std::scoped_lock lock(mutex); | 95 | std::scoped_lock lock(mutex); |
| @@ -141,10 +140,7 @@ NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> outpu | |||
| 141 | return NvResult::Success; | 140 | return NvResult::Success; |
| 142 | } | 141 | } |
| 143 | 142 | ||
| 144 | NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> output) { | 143 | NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) { |
| 145 | IoctlAllocSpace params{}; | ||
| 146 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 147 | |||
| 148 | LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, | 144 | LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, |
| 149 | params.page_size, params.flags); | 145 | params.page_size, params.flags); |
| 150 | 146 | ||
| @@ -194,7 +190,6 @@ NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> o | |||
| 194 | .big_pages = params.page_size != VM::YUZU_PAGESIZE, | 190 | .big_pages = params.page_size != VM::YUZU_PAGESIZE, |
| 195 | }; | 191 | }; |
| 196 | 192 | ||
| 197 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 198 | return NvResult::Success; | 193 | return NvResult::Success; |
| 199 | } | 194 | } |
| 200 | 195 | ||
| @@ -222,10 +217,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) { | |||
| 222 | mapping_map.erase(offset); | 217 | mapping_map.erase(offset); |
| 223 | } | 218 | } |
| 224 | 219 | ||
| 225 | NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> output) { | 220 | NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) { |
| 226 | IoctlFreeSpace params{}; | ||
| 227 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 228 | |||
| 229 | LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, | 221 | LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, |
| 230 | params.pages, params.page_size); | 222 | params.pages, params.page_size); |
| 231 | 223 | ||
| @@ -264,18 +256,11 @@ NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> outpu | |||
| 264 | return NvResult::BadValue; | 256 | return NvResult::BadValue; |
| 265 | } | 257 | } |
| 266 | 258 | ||
| 267 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 268 | return NvResult::Success; | 259 | return NvResult::Success; |
| 269 | } | 260 | } |
| 270 | 261 | ||
| 271 | NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) { | 262 | NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) { |
| 272 | const auto num_entries = input.size() / sizeof(IoctlRemapEntry); | 263 | LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", entries.size()); |
| 273 | |||
| 274 | LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); | ||
| 275 | |||
| 276 | std::scoped_lock lock(mutex); | ||
| 277 | entries.resize_destructive(num_entries); | ||
| 278 | std::memcpy(entries.data(), input.data(), input.size()); | ||
| 279 | 264 | ||
| 280 | if (!vm.initialised) { | 265 | if (!vm.initialised) { |
| 281 | return NvResult::BadValue; | 266 | return NvResult::BadValue; |
| @@ -317,14 +302,10 @@ NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) { | |||
| 317 | } | 302 | } |
| 318 | } | 303 | } |
| 319 | 304 | ||
| 320 | std::memcpy(output.data(), entries.data(), output.size()); | ||
| 321 | return NvResult::Success; | 305 | return NvResult::Success; |
| 322 | } | 306 | } |
| 323 | 307 | ||
| 324 | NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> output) { | 308 | NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { |
| 325 | IoctlMapBufferEx params{}; | ||
| 326 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 327 | |||
| 328 | LOG_DEBUG(Service_NVDRV, | 309 | LOG_DEBUG(Service_NVDRV, |
| 329 | "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" | 310 | "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" |
| 330 | ", offset={}", | 311 | ", offset={}", |
| @@ -421,14 +402,10 @@ NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> out | |||
| 421 | mapping_map[params.offset] = mapping; | 402 | mapping_map[params.offset] = mapping; |
| 422 | } | 403 | } |
| 423 | 404 | ||
| 424 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 425 | return NvResult::Success; | 405 | return NvResult::Success; |
| 426 | } | 406 | } |
| 427 | 407 | ||
| 428 | NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> output) { | 408 | NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) { |
| 429 | IoctlUnmapBuffer params{}; | ||
| 430 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 431 | |||
| 432 | LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); | 409 | LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); |
| 433 | 410 | ||
| 434 | std::scoped_lock lock(mutex); | 411 | std::scoped_lock lock(mutex); |
| @@ -464,9 +441,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> out | |||
| 464 | return NvResult::Success; | 441 | return NvResult::Success; |
| 465 | } | 442 | } |
| 466 | 443 | ||
| 467 | NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::span<u8> output) { | 444 | NvResult nvhost_as_gpu::BindChannel(IoctlBindChannel& params) { |
| 468 | IoctlBindChannel params{}; | ||
| 469 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 470 | LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); | 445 | LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); |
| 471 | 446 | ||
| 472 | auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd); | 447 | auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd); |
| @@ -493,10 +468,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) { | |||
| 493 | }; | 468 | }; |
| 494 | } | 469 | } |
| 495 | 470 | ||
| 496 | NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output) { | 471 | NvResult nvhost_as_gpu::GetVARegions1(IoctlGetVaRegions& params) { |
| 497 | IoctlGetVaRegions params{}; | ||
| 498 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 499 | |||
| 500 | LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, | 472 | LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, |
| 501 | params.buf_size); | 473 | params.buf_size); |
| 502 | 474 | ||
| @@ -508,15 +480,10 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> ou | |||
| 508 | 480 | ||
| 509 | GetVARegionsImpl(params); | 481 | GetVARegionsImpl(params); |
| 510 | 482 | ||
| 511 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 512 | return NvResult::Success; | 483 | return NvResult::Success; |
| 513 | } | 484 | } |
| 514 | 485 | ||
| 515 | NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output, | 486 | NvResult nvhost_as_gpu::GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions) { |
| 516 | std::span<u8> inline_output) { | ||
| 517 | IoctlGetVaRegions params{}; | ||
| 518 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 519 | |||
| 520 | LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, | 487 | LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, |
| 521 | params.buf_size); | 488 | params.buf_size); |
| 522 | 489 | ||
| @@ -528,9 +495,10 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> ou | |||
| 528 | 495 | ||
| 529 | GetVARegionsImpl(params); | 496 | GetVARegionsImpl(params); |
| 530 | 497 | ||
| 531 | std::memcpy(output.data(), ¶ms, output.size()); | 498 | const size_t num_regions = std::min(params.regions.size(), regions.size()); |
| 532 | std::memcpy(inline_output.data(), ¶ms.regions[0], sizeof(VaRegion)); | 499 | for (size_t i = 0; i < num_regions; i++) { |
| 533 | std::memcpy(inline_output.data() + sizeof(VaRegion), ¶ms.regions[1], sizeof(VaRegion)); | 500 | regions[i] = params.regions[i]; |
| 501 | } | ||
| 534 | 502 | ||
| 535 | return NvResult::Success; | 503 | return NvResult::Success; |
| 536 | } | 504 | } |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 2af3e1260..932997e75 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h | |||
| @@ -139,18 +139,17 @@ private: | |||
| 139 | static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, | 139 | static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, |
| 140 | "IoctlGetVaRegions is incorrect size"); | 140 | "IoctlGetVaRegions is incorrect size"); |
| 141 | 141 | ||
| 142 | NvResult AllocAsEx(std::span<const u8> input, std::span<u8> output); | 142 | NvResult AllocAsEx(IoctlAllocAsEx& params); |
| 143 | NvResult AllocateSpace(std::span<const u8> input, std::span<u8> output); | 143 | NvResult AllocateSpace(IoctlAllocSpace& params); |
| 144 | NvResult Remap(std::span<const u8> input, std::span<u8> output); | 144 | NvResult Remap(std::span<IoctlRemapEntry> params); |
| 145 | NvResult MapBufferEx(std::span<const u8> input, std::span<u8> output); | 145 | NvResult MapBufferEx(IoctlMapBufferEx& params); |
| 146 | NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); | 146 | NvResult UnmapBuffer(IoctlUnmapBuffer& params); |
| 147 | NvResult FreeSpace(std::span<const u8> input, std::span<u8> output); | 147 | NvResult FreeSpace(IoctlFreeSpace& params); |
| 148 | NvResult BindChannel(std::span<const u8> input, std::span<u8> output); | 148 | NvResult BindChannel(IoctlBindChannel& params); |
| 149 | 149 | ||
| 150 | void GetVARegionsImpl(IoctlGetVaRegions& params); | 150 | void GetVARegionsImpl(IoctlGetVaRegions& params); |
| 151 | NvResult GetVARegions(std::span<const u8> input, std::span<u8> output); | 151 | NvResult GetVARegions1(IoctlGetVaRegions& params); |
| 152 | NvResult GetVARegions(std::span<const u8> input, std::span<u8> output, | 152 | NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions); |
| 153 | std::span<u8> inline_output); | ||
| 154 | 153 | ||
| 155 | void FreeMappingLocked(u64 offset); | 154 | void FreeMappingLocked(u64 offset); |
| 156 | 155 | ||
| @@ -213,7 +212,6 @@ private: | |||
| 213 | bool initialised{}; | 212 | bool initialised{}; |
| 214 | } vm; | 213 | } vm; |
| 215 | std::shared_ptr<Tegra::MemoryManager> gmmu; | 214 | std::shared_ptr<Tegra::MemoryManager> gmmu; |
| 216 | Common::ScratchBuffer<IoctlRemapEntry> entries; | ||
| 217 | 215 | ||
| 218 | // s32 channel{}; | 216 | // s32 channel{}; |
| 219 | // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; | 217 | // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 4d55554b4..b8dd34e24 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/hle/kernel/k_event.h" | 14 | #include "core/hle/kernel/k_event.h" |
| 15 | #include "core/hle/service/nvdrv/core/container.h" | 15 | #include "core/hle/service/nvdrv/core/container.h" |
| 16 | #include "core/hle/service/nvdrv/core/syncpoint_manager.h" | 16 | #include "core/hle/service/nvdrv/core/syncpoint_manager.h" |
| 17 | #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" | ||
| 17 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" | 18 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" |
| 18 | #include "video_core/gpu.h" | 19 | #include "video_core/gpu.h" |
| 19 | #include "video_core/host1x/host1x.h" | 20 | #include "video_core/host1x/host1x.h" |
| @@ -40,19 +41,19 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inp | |||
| 40 | case 0x0: | 41 | case 0x0: |
| 41 | switch (command.cmd) { | 42 | switch (command.cmd) { |
| 42 | case 0x1b: | 43 | case 0x1b: |
| 43 | return NvOsGetConfigU32(input, output); | 44 | return WrapFixed(this, &nvhost_ctrl::NvOsGetConfigU32, input, output); |
| 44 | case 0x1c: | 45 | case 0x1c: |
| 45 | return IocCtrlClearEventWait(input, output); | 46 | return WrapFixed(this, &nvhost_ctrl::IocCtrlClearEventWait, input, output); |
| 46 | case 0x1d: | 47 | case 0x1d: |
| 47 | return IocCtrlEventWait(input, output, true); | 48 | return WrapFixed(this, &nvhost_ctrl::IocCtrlEventWait, input, output, true); |
| 48 | case 0x1e: | 49 | case 0x1e: |
| 49 | return IocCtrlEventWait(input, output, false); | 50 | return WrapFixed(this, &nvhost_ctrl::IocCtrlEventWait, input, output, false); |
| 50 | case 0x1f: | 51 | case 0x1f: |
| 51 | return IocCtrlEventRegister(input, output); | 52 | return WrapFixed(this, &nvhost_ctrl::IocCtrlEventRegister, input, output); |
| 52 | case 0x20: | 53 | case 0x20: |
| 53 | return IocCtrlEventUnregister(input, output); | 54 | return WrapFixed(this, &nvhost_ctrl::IocCtrlEventUnregister, input, output); |
| 54 | case 0x21: | 55 | case 0x21: |
| 55 | return IocCtrlEventUnregisterBatch(input, output); | 56 | return WrapFixed(this, &nvhost_ctrl::IocCtrlEventUnregisterBatch, input, output); |
| 56 | } | 57 | } |
| 57 | break; | 58 | break; |
| 58 | default: | 59 | default: |
| @@ -79,25 +80,19 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {} | |||
| 79 | 80 | ||
| 80 | void nvhost_ctrl::OnClose(DeviceFD fd) {} | 81 | void nvhost_ctrl::OnClose(DeviceFD fd) {} |
| 81 | 82 | ||
| 82 | NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output) { | 83 | NvResult nvhost_ctrl::NvOsGetConfigU32(IocGetConfigParams& params) { |
| 83 | IocGetConfigParams params{}; | ||
| 84 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 85 | LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), | 84 | LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), |
| 86 | params.param_str.data()); | 85 | params.param_str.data()); |
| 87 | return NvResult::ConfigVarNotFound; // Returns error on production mode | 86 | return NvResult::ConfigVarNotFound; // Returns error on production mode |
| 88 | } | 87 | } |
| 89 | 88 | ||
| 90 | NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, | 89 | NvResult nvhost_ctrl::IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_allocation) { |
| 91 | bool is_allocation) { | ||
| 92 | IocCtrlEventWaitParams params{}; | ||
| 93 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 94 | LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}", | 90 | LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}", |
| 95 | params.fence.id, params.fence.value, params.timeout, is_allocation); | 91 | params.fence.id, params.fence.value, params.timeout, is_allocation); |
| 96 | 92 | ||
| 97 | bool must_unmark_fail = !is_allocation; | 93 | bool must_unmark_fail = !is_allocation; |
| 98 | const u32 event_id = params.value.raw; | 94 | const u32 event_id = params.value.raw; |
| 99 | SCOPE_EXIT({ | 95 | SCOPE_EXIT({ |
| 100 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 101 | if (must_unmark_fail) { | 96 | if (must_unmark_fail) { |
| 102 | events[event_id].fails = 0; | 97 | events[event_id].fails = 0; |
| 103 | } | 98 | } |
| @@ -231,9 +226,7 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) { | |||
| 231 | return NvResult::Success; | 226 | return NvResult::Success; |
| 232 | } | 227 | } |
| 233 | 228 | ||
| 234 | NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output) { | 229 | NvResult nvhost_ctrl::IocCtrlEventRegister(IocCtrlEventRegisterParams& params) { |
| 235 | IocCtrlEventRegisterParams params{}; | ||
| 236 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 237 | const u32 event_id = params.user_event_id; | 230 | const u32 event_id = params.user_event_id; |
| 238 | LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); | 231 | LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); |
| 239 | if (event_id >= MaxNvEvents) { | 232 | if (event_id >= MaxNvEvents) { |
| @@ -252,9 +245,7 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span< | |||
| 252 | return NvResult::Success; | 245 | return NvResult::Success; |
| 253 | } | 246 | } |
| 254 | 247 | ||
| 255 | NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output) { | 248 | NvResult nvhost_ctrl::IocCtrlEventUnregister(IocCtrlEventUnregisterParams& params) { |
| 256 | IocCtrlEventUnregisterParams params{}; | ||
| 257 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 258 | const u32 event_id = params.user_event_id & 0x00FF; | 249 | const u32 event_id = params.user_event_id & 0x00FF; |
| 259 | LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); | 250 | LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); |
| 260 | 251 | ||
| @@ -262,9 +253,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::spa | |||
| 262 | return FreeEvent(event_id); | 253 | return FreeEvent(event_id); |
| 263 | } | 254 | } |
| 264 | 255 | ||
| 265 | NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output) { | 256 | NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(IocCtrlEventUnregisterBatchParams& params) { |
| 266 | IocCtrlEventUnregisterBatchParams params{}; | ||
| 267 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 268 | u64 event_mask = params.user_events; | 257 | u64 event_mask = params.user_events; |
| 269 | LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask); | 258 | LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask); |
| 270 | 259 | ||
| @@ -280,10 +269,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std | |||
| 280 | return NvResult::Success; | 269 | return NvResult::Success; |
| 281 | } | 270 | } |
| 282 | 271 | ||
| 283 | NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output) { | 272 | NvResult nvhost_ctrl::IocCtrlClearEventWait(IocCtrlEventClearParams& params) { |
| 284 | IocCtrlEventClearParams params{}; | ||
| 285 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 286 | |||
| 287 | u32 event_id = params.event_id.slot; | 273 | u32 event_id = params.event_id.slot; |
| 288 | LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id); | 274 | LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id); |
| 289 | 275 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 2efed4862..992124b60 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h | |||
| @@ -186,12 +186,12 @@ private: | |||
| 186 | static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, | 186 | static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, |
| 187 | "IocCtrlEventKill is incorrect size"); | 187 | "IocCtrlEventKill is incorrect size"); |
| 188 | 188 | ||
| 189 | NvResult NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output); | 189 | NvResult NvOsGetConfigU32(IocGetConfigParams& params); |
| 190 | NvResult IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, bool is_allocation); | 190 | NvResult IocCtrlEventRegister(IocCtrlEventRegisterParams& params); |
| 191 | NvResult IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output); | 191 | NvResult IocCtrlEventUnregister(IocCtrlEventUnregisterParams& params); |
| 192 | NvResult IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output); | 192 | NvResult IocCtrlEventUnregisterBatch(IocCtrlEventUnregisterBatchParams& params); |
| 193 | NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output); | 193 | NvResult IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_allocation); |
| 194 | NvResult IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output); | 194 | NvResult IocCtrlClearEventWait(IocCtrlEventClearParams& params); |
| 195 | 195 | ||
| 196 | NvResult FreeEvent(u32 slot); | 196 | NvResult FreeEvent(u32 slot); |
| 197 | 197 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 6081d92e9..61a2df121 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 9 | #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" | ||
| 9 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" | 10 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" |
| 10 | #include "core/hle/service/nvdrv/nvdrv.h" | 11 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 11 | 12 | ||
| @@ -27,23 +28,23 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> | |||
| 27 | case 'G': | 28 | case 'G': |
| 28 | switch (command.cmd) { | 29 | switch (command.cmd) { |
| 29 | case 0x1: | 30 | case 0x1: |
| 30 | return ZCullGetCtxSize(input, output); | 31 | return WrapFixed(this, &nvhost_ctrl_gpu::ZCullGetCtxSize, input, output); |
| 31 | case 0x2: | 32 | case 0x2: |
| 32 | return ZCullGetInfo(input, output); | 33 | return WrapFixed(this, &nvhost_ctrl_gpu::ZCullGetInfo, input, output); |
| 33 | case 0x3: | 34 | case 0x3: |
| 34 | return ZBCSetTable(input, output); | 35 | return WrapFixed(this, &nvhost_ctrl_gpu::ZBCSetTable, input, output); |
| 35 | case 0x4: | 36 | case 0x4: |
| 36 | return ZBCQueryTable(input, output); | 37 | return WrapFixed(this, &nvhost_ctrl_gpu::ZBCQueryTable, input, output); |
| 37 | case 0x5: | 38 | case 0x5: |
| 38 | return GetCharacteristics(input, output); | 39 | return WrapFixed(this, &nvhost_ctrl_gpu::GetCharacteristics1, input, output); |
| 39 | case 0x6: | 40 | case 0x6: |
| 40 | return GetTPCMasks(input, output); | 41 | return WrapFixed(this, &nvhost_ctrl_gpu::GetTPCMasks1, input, output); |
| 41 | case 0x7: | 42 | case 0x7: |
| 42 | return FlushL2(input, output); | 43 | return WrapFixed(this, &nvhost_ctrl_gpu::FlushL2, input, output); |
| 43 | case 0x14: | 44 | case 0x14: |
| 44 | return GetActiveSlotMask(input, output); | 45 | return WrapFixed(this, &nvhost_ctrl_gpu::GetActiveSlotMask, input, output); |
| 45 | case 0x1c: | 46 | case 0x1c: |
| 46 | return GetGpuTime(input, output); | 47 | return WrapFixed(this, &nvhost_ctrl_gpu::GetGpuTime, input, output); |
| 47 | default: | 48 | default: |
| 48 | break; | 49 | break; |
| 49 | } | 50 | } |
| @@ -65,9 +66,11 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> | |||
| 65 | case 'G': | 66 | case 'G': |
| 66 | switch (command.cmd) { | 67 | switch (command.cmd) { |
| 67 | case 0x5: | 68 | case 0x5: |
| 68 | return GetCharacteristics(input, output, inline_output); | 69 | return WrapFixedInlOut(this, &nvhost_ctrl_gpu::GetCharacteristics3, input, output, |
| 70 | inline_output); | ||
| 69 | case 0x6: | 71 | case 0x6: |
| 70 | return GetTPCMasks(input, output, inline_output); | 72 | return WrapFixedInlOut(this, &nvhost_ctrl_gpu::GetTPCMasks3, input, output, |
| 73 | inline_output); | ||
| 71 | default: | 74 | default: |
| 72 | break; | 75 | break; |
| 73 | } | 76 | } |
| @@ -82,10 +85,8 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> | |||
| 82 | void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} | 85 | void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} |
| 83 | void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} | 86 | void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} |
| 84 | 87 | ||
| 85 | NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output) { | 88 | NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) { |
| 86 | LOG_DEBUG(Service_NVDRV, "called"); | 89 | LOG_DEBUG(Service_NVDRV, "called"); |
| 87 | IoctlCharacteristics params{}; | ||
| 88 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 89 | params.gc.arch = 0x120; | 90 | params.gc.arch = 0x120; |
| 90 | params.gc.impl = 0xb; | 91 | params.gc.impl = 0xb; |
| 91 | params.gc.rev = 0xa1; | 92 | params.gc.rev = 0xa1; |
| @@ -123,15 +124,13 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::spa | |||
| 123 | params.gc.gr_compbit_store_base_hw = 0x0; | 124 | params.gc.gr_compbit_store_base_hw = 0x0; |
| 124 | params.gpu_characteristics_buf_size = 0xA0; | 125 | params.gpu_characteristics_buf_size = 0xA0; |
| 125 | params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) | 126 | params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) |
| 126 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 127 | return NvResult::Success; | 127 | return NvResult::Success; |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output, | 130 | NvResult nvhost_ctrl_gpu::GetCharacteristics3( |
| 131 | std::span<u8> inline_output) { | 131 | IoctlCharacteristics& params, std::span<IoctlGpuCharacteristics> gpu_characteristics) { |
| 132 | LOG_DEBUG(Service_NVDRV, "called"); | 132 | LOG_DEBUG(Service_NVDRV, "called"); |
| 133 | IoctlCharacteristics params{}; | 133 | |
| 134 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 135 | params.gc.arch = 0x120; | 134 | params.gc.arch = 0x120; |
| 136 | params.gc.impl = 0xb; | 135 | params.gc.impl = 0xb; |
| 137 | params.gc.rev = 0xa1; | 136 | params.gc.rev = 0xa1; |
| @@ -169,70 +168,47 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::spa | |||
| 169 | params.gc.gr_compbit_store_base_hw = 0x0; | 168 | params.gc.gr_compbit_store_base_hw = 0x0; |
| 170 | params.gpu_characteristics_buf_size = 0xA0; | 169 | params.gpu_characteristics_buf_size = 0xA0; |
| 171 | params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) | 170 | params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) |
| 172 | 171 | if (!gpu_characteristics.empty()) { | |
| 173 | std::memcpy(output.data(), ¶ms, output.size()); | 172 | gpu_characteristics.front() = params.gc; |
| 174 | std::memcpy(inline_output.data(), ¶ms.gc, inline_output.size()); | 173 | } |
| 175 | return NvResult::Success; | 174 | return NvResult::Success; |
| 176 | } | 175 | } |
| 177 | 176 | ||
| 178 | NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output) { | 177 | NvResult nvhost_ctrl_gpu::GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params) { |
| 179 | IoctlGpuGetTpcMasksArgs params{}; | ||
| 180 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 181 | LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); | 178 | LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); |
| 182 | if (params.mask_buffer_size != 0) { | 179 | if (params.mask_buffer_size != 0) { |
| 183 | params.tcp_mask = 3; | 180 | params.tcp_mask = 3; |
| 184 | } | 181 | } |
| 185 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 186 | return NvResult::Success; | 182 | return NvResult::Success; |
| 187 | } | 183 | } |
| 188 | 184 | ||
| 189 | NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output, | 185 | NvResult nvhost_ctrl_gpu::GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask) { |
| 190 | std::span<u8> inline_output) { | ||
| 191 | IoctlGpuGetTpcMasksArgs params{}; | ||
| 192 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 193 | LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); | 186 | LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); |
| 194 | if (params.mask_buffer_size != 0) { | 187 | if (params.mask_buffer_size != 0) { |
| 195 | params.tcp_mask = 3; | 188 | params.tcp_mask = 3; |
| 196 | } | 189 | } |
| 197 | std::memcpy(output.data(), ¶ms, output.size()); | 190 | if (!tpc_mask.empty()) { |
| 198 | std::memcpy(inline_output.data(), ¶ms.tcp_mask, inline_output.size()); | 191 | tpc_mask.front() = params.tcp_mask; |
| 192 | } | ||
| 199 | return NvResult::Success; | 193 | return NvResult::Success; |
| 200 | } | 194 | } |
| 201 | 195 | ||
| 202 | NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::span<u8> output) { | 196 | NvResult nvhost_ctrl_gpu::GetActiveSlotMask(IoctlActiveSlotMask& params) { |
| 203 | LOG_DEBUG(Service_NVDRV, "called"); | 197 | LOG_DEBUG(Service_NVDRV, "called"); |
| 204 | 198 | ||
| 205 | IoctlActiveSlotMask params{}; | ||
| 206 | if (input.size() > 0) { | ||
| 207 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 208 | } | ||
| 209 | params.slot = 0x07; | 199 | params.slot = 0x07; |
| 210 | params.mask = 0x01; | 200 | params.mask = 0x01; |
| 211 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 212 | return NvResult::Success; | 201 | return NvResult::Success; |
| 213 | } | 202 | } |
| 214 | 203 | ||
| 215 | NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output) { | 204 | NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(IoctlZcullGetCtxSize& params) { |
| 216 | LOG_DEBUG(Service_NVDRV, "called"); | 205 | LOG_DEBUG(Service_NVDRV, "called"); |
| 217 | |||
| 218 | IoctlZcullGetCtxSize params{}; | ||
| 219 | if (input.size() > 0) { | ||
| 220 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 221 | } | ||
| 222 | params.size = 0x1; | 206 | params.size = 0x1; |
| 223 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 224 | return NvResult::Success; | 207 | return NvResult::Success; |
| 225 | } | 208 | } |
| 226 | 209 | ||
| 227 | NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8> output) { | 210 | NvResult nvhost_ctrl_gpu::ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params) { |
| 228 | LOG_DEBUG(Service_NVDRV, "called"); | 211 | LOG_DEBUG(Service_NVDRV, "called"); |
| 229 | |||
| 230 | IoctlNvgpuGpuZcullGetInfoArgs params{}; | ||
| 231 | |||
| 232 | if (input.size() > 0) { | ||
| 233 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 234 | } | ||
| 235 | |||
| 236 | params.width_align_pixels = 0x20; | 212 | params.width_align_pixels = 0x20; |
| 237 | params.height_align_pixels = 0x20; | 213 | params.height_align_pixels = 0x20; |
| 238 | params.pixel_squares_by_aliquots = 0x400; | 214 | params.pixel_squares_by_aliquots = 0x400; |
| @@ -243,53 +219,28 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8> | |||
| 243 | params.subregion_width_align_pixels = 0x20; | 219 | params.subregion_width_align_pixels = 0x20; |
| 244 | params.subregion_height_align_pixels = 0x40; | 220 | params.subregion_height_align_pixels = 0x40; |
| 245 | params.subregion_count = 0x10; | 221 | params.subregion_count = 0x10; |
| 246 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 247 | return NvResult::Success; | 222 | return NvResult::Success; |
| 248 | } | 223 | } |
| 249 | 224 | ||
| 250 | NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::span<u8> output) { | 225 | NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) { |
| 251 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | 226 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); |
| 252 | |||
| 253 | IoctlZbcSetTable params{}; | ||
| 254 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 255 | // TODO(ogniK): What does this even actually do? | 227 | // TODO(ogniK): What does this even actually do? |
| 256 | |||
| 257 | // Prevent null pointer being passed as arg 1 | ||
| 258 | if (output.empty()) { | ||
| 259 | LOG_WARNING(Service_NVDRV, "Avoiding passing null pointer to memcpy"); | ||
| 260 | } else { | ||
| 261 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 262 | } | ||
| 263 | return NvResult::Success; | 228 | return NvResult::Success; |
| 264 | } | 229 | } |
| 265 | 230 | ||
| 266 | NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::span<u8> output) { | 231 | NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) { |
| 267 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | 232 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); |
| 268 | |||
| 269 | IoctlZbcQueryTable params{}; | ||
| 270 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 271 | // TODO : To implement properly | ||
| 272 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 273 | return NvResult::Success; | 233 | return NvResult::Success; |
| 274 | } | 234 | } |
| 275 | 235 | ||
| 276 | NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::span<u8> output) { | 236 | NvResult nvhost_ctrl_gpu::FlushL2(IoctlFlushL2& params) { |
| 277 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | 237 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); |
| 278 | |||
| 279 | IoctlFlushL2 params{}; | ||
| 280 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 281 | // TODO : To implement properly | ||
| 282 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 283 | return NvResult::Success; | 238 | return NvResult::Success; |
| 284 | } | 239 | } |
| 285 | 240 | ||
| 286 | NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::span<u8> output) { | 241 | NvResult nvhost_ctrl_gpu::GetGpuTime(IoctlGetGpuTime& params) { |
| 287 | LOG_DEBUG(Service_NVDRV, "called"); | 242 | LOG_DEBUG(Service_NVDRV, "called"); |
| 288 | |||
| 289 | IoctlGetGpuTime params{}; | ||
| 290 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 291 | params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); | 243 | params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); |
| 292 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 293 | return NvResult::Success; | 244 | return NvResult::Success; |
| 294 | } | 245 | } |
| 295 | 246 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 97995551c..d170299bd 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h | |||
| @@ -151,21 +151,20 @@ private: | |||
| 151 | }; | 151 | }; |
| 152 | static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); | 152 | static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); |
| 153 | 153 | ||
| 154 | NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output); | 154 | NvResult GetCharacteristics1(IoctlCharacteristics& params); |
| 155 | NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output, | 155 | NvResult GetCharacteristics3(IoctlCharacteristics& params, |
| 156 | std::span<u8> inline_output); | 156 | std::span<IoctlGpuCharacteristics> gpu_characteristics); |
| 157 | 157 | ||
| 158 | NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output); | 158 | NvResult GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params); |
| 159 | NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output, | 159 | NvResult GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask); |
| 160 | std::span<u8> inline_output); | 160 | |
| 161 | 161 | NvResult GetActiveSlotMask(IoctlActiveSlotMask& params); | |
| 162 | NvResult GetActiveSlotMask(std::span<const u8> input, std::span<u8> output); | 162 | NvResult ZCullGetCtxSize(IoctlZcullGetCtxSize& params); |
| 163 | NvResult ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output); | 163 | NvResult ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params); |
| 164 | NvResult ZCullGetInfo(std::span<const u8> input, std::span<u8> output); | 164 | NvResult ZBCSetTable(IoctlZbcSetTable& params); |
| 165 | NvResult ZBCSetTable(std::span<const u8> input, std::span<u8> output); | 165 | NvResult ZBCQueryTable(IoctlZbcQueryTable& params); |
| 166 | NvResult ZBCQueryTable(std::span<const u8> input, std::span<u8> output); | 166 | NvResult FlushL2(IoctlFlushL2& params); |
| 167 | NvResult FlushL2(std::span<const u8> input, std::span<u8> output); | 167 | NvResult GetGpuTime(IoctlGetGpuTime& params); |
| 168 | NvResult GetGpuTime(std::span<const u8> input, std::span<u8> output); | ||
| 169 | 168 | ||
| 170 | EventInterface& events_interface; | 169 | EventInterface& events_interface; |
| 171 | 170 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 46a25fcab..b0395c2f0 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "core/hle/service/nvdrv/core/container.h" | 8 | #include "core/hle/service/nvdrv/core/container.h" |
| 9 | #include "core/hle/service/nvdrv/core/nvmap.h" | 9 | #include "core/hle/service/nvdrv/core/nvmap.h" |
| 10 | #include "core/hle/service/nvdrv/core/syncpoint_manager.h" | 10 | #include "core/hle/service/nvdrv/core/syncpoint_manager.h" |
| 11 | #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" | ||
| 11 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" | 12 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" |
| 12 | #include "core/hle/service/nvdrv/nvdrv.h" | 13 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 13 | #include "core/memory.h" | 14 | #include "core/memory.h" |
| @@ -52,7 +53,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu | |||
| 52 | case 0x0: | 53 | case 0x0: |
| 53 | switch (command.cmd) { | 54 | switch (command.cmd) { |
| 54 | case 0x3: | 55 | case 0x3: |
| 55 | return GetWaitbase(input, output); | 56 | return WrapFixed(this, &nvhost_gpu::GetWaitbase, input, output); |
| 56 | default: | 57 | default: |
| 57 | break; | 58 | break; |
| 58 | } | 59 | } |
| @@ -60,25 +61,25 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu | |||
| 60 | case 'H': | 61 | case 'H': |
| 61 | switch (command.cmd) { | 62 | switch (command.cmd) { |
| 62 | case 0x1: | 63 | case 0x1: |
| 63 | return SetNVMAPfd(input, output); | 64 | return WrapFixed(this, &nvhost_gpu::SetNVMAPfd, input, output); |
| 64 | case 0x3: | 65 | case 0x3: |
| 65 | return ChannelSetTimeout(input, output); | 66 | return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output); |
| 66 | case 0x8: | 67 | case 0x8: |
| 67 | return SubmitGPFIFOBase(input, output, false); | 68 | return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, false); |
| 68 | case 0x9: | 69 | case 0x9: |
| 69 | return AllocateObjectContext(input, output); | 70 | return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output); |
| 70 | case 0xb: | 71 | case 0xb: |
| 71 | return ZCullBind(input, output); | 72 | return WrapFixed(this, &nvhost_gpu::ZCullBind, input, output); |
| 72 | case 0xc: | 73 | case 0xc: |
| 73 | return SetErrorNotifier(input, output); | 74 | return WrapFixed(this, &nvhost_gpu::SetErrorNotifier, input, output); |
| 74 | case 0xd: | 75 | case 0xd: |
| 75 | return SetChannelPriority(input, output); | 76 | return WrapFixed(this, &nvhost_gpu::SetChannelPriority, input, output); |
| 76 | case 0x1a: | 77 | case 0x1a: |
| 77 | return AllocGPFIFOEx2(input, output); | 78 | return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output); |
| 78 | case 0x1b: | 79 | case 0x1b: |
| 79 | return SubmitGPFIFOBase(input, output, true); | 80 | return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, true); |
| 80 | case 0x1d: | 81 | case 0x1d: |
| 81 | return ChannelSetTimeslice(input, output); | 82 | return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output); |
| 82 | default: | 83 | default: |
| 83 | break; | 84 | break; |
| 84 | } | 85 | } |
| @@ -86,9 +87,9 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu | |||
| 86 | case 'G': | 87 | case 'G': |
| 87 | switch (command.cmd) { | 88 | switch (command.cmd) { |
| 88 | case 0x14: | 89 | case 0x14: |
| 89 | return SetClientData(input, output); | 90 | return WrapFixed(this, &nvhost_gpu::SetClientData, input, output); |
| 90 | case 0x15: | 91 | case 0x15: |
| 91 | return GetClientData(input, output); | 92 | return WrapFixed(this, &nvhost_gpu::GetClientData, input, output); |
| 92 | default: | 93 | default: |
| 93 | break; | 94 | break; |
| 94 | } | 95 | } |
| @@ -104,7 +105,8 @@ NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> inpu | |||
| 104 | case 'H': | 105 | case 'H': |
| 105 | switch (command.cmd) { | 106 | switch (command.cmd) { |
| 106 | case 0x1b: | 107 | case 0x1b: |
| 107 | return SubmitGPFIFOBase(input, inline_input, output); | 108 | return WrapFixedInlIn(this, &nvhost_gpu::SubmitGPFIFOBase2, input, inline_input, |
| 109 | output); | ||
| 108 | } | 110 | } |
| 109 | break; | 111 | break; |
| 110 | } | 112 | } |
| @@ -121,63 +123,45 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu | |||
| 121 | void nvhost_gpu::OnOpen(DeviceFD fd) {} | 123 | void nvhost_gpu::OnOpen(DeviceFD fd) {} |
| 122 | void nvhost_gpu::OnClose(DeviceFD fd) {} | 124 | void nvhost_gpu::OnClose(DeviceFD fd) {} |
| 123 | 125 | ||
| 124 | NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) { | 126 | NvResult nvhost_gpu::SetNVMAPfd(IoctlSetNvmapFD& params) { |
| 125 | IoctlSetNvmapFD params{}; | ||
| 126 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 127 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); | 127 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); |
| 128 | 128 | ||
| 129 | nvmap_fd = params.nvmap_fd; | 129 | nvmap_fd = params.nvmap_fd; |
| 130 | return NvResult::Success; | 130 | return NvResult::Success; |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::span<u8> output) { | 133 | NvResult nvhost_gpu::SetClientData(IoctlClientData& params) { |
| 134 | LOG_DEBUG(Service_NVDRV, "called"); | 134 | LOG_DEBUG(Service_NVDRV, "called"); |
| 135 | |||
| 136 | IoctlClientData params{}; | ||
| 137 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 138 | user_data = params.data; | 135 | user_data = params.data; |
| 139 | return NvResult::Success; | 136 | return NvResult::Success; |
| 140 | } | 137 | } |
| 141 | 138 | ||
| 142 | NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::span<u8> output) { | 139 | NvResult nvhost_gpu::GetClientData(IoctlClientData& params) { |
| 143 | LOG_DEBUG(Service_NVDRV, "called"); | 140 | LOG_DEBUG(Service_NVDRV, "called"); |
| 144 | |||
| 145 | IoctlClientData params{}; | ||
| 146 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 147 | params.data = user_data; | 141 | params.data = user_data; |
| 148 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 149 | return NvResult::Success; | 142 | return NvResult::Success; |
| 150 | } | 143 | } |
| 151 | 144 | ||
| 152 | NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::span<u8> output) { | 145 | NvResult nvhost_gpu::ZCullBind(IoctlZCullBind& params) { |
| 153 | std::memcpy(&zcull_params, input.data(), input.size()); | 146 | zcull_params = params; |
| 154 | LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, | 147 | LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, |
| 155 | zcull_params.mode); | 148 | zcull_params.mode); |
| 156 | |||
| 157 | std::memcpy(output.data(), &zcull_params, output.size()); | ||
| 158 | return NvResult::Success; | 149 | return NvResult::Success; |
| 159 | } | 150 | } |
| 160 | 151 | ||
| 161 | NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::span<u8> output) { | 152 | NvResult nvhost_gpu::SetErrorNotifier(IoctlSetErrorNotifier& params) { |
| 162 | IoctlSetErrorNotifier params{}; | ||
| 163 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 164 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, | 153 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, |
| 165 | params.size, params.mem); | 154 | params.size, params.mem); |
| 166 | |||
| 167 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 168 | return NvResult::Success; | 155 | return NvResult::Success; |
| 169 | } | 156 | } |
| 170 | 157 | ||
| 171 | NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::span<u8> output) { | 158 | NvResult nvhost_gpu::SetChannelPriority(IoctlChannelSetPriority& params) { |
| 172 | std::memcpy(&channel_priority, input.data(), input.size()); | 159 | channel_priority = params.priority; |
| 173 | LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); | 160 | LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); |
| 174 | |||
| 175 | return NvResult::Success; | 161 | return NvResult::Success; |
| 176 | } | 162 | } |
| 177 | 163 | ||
| 178 | NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output) { | 164 | NvResult nvhost_gpu::AllocGPFIFOEx2(IoctlAllocGpfifoEx2& params) { |
| 179 | IoctlAllocGpfifoEx2 params{}; | ||
| 180 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 181 | LOG_WARNING(Service_NVDRV, | 165 | LOG_WARNING(Service_NVDRV, |
| 182 | "(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, " | 166 | "(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, " |
| 183 | "unk1={:X}, unk2={:X}, unk3={:X}", | 167 | "unk1={:X}, unk2={:X}, unk3={:X}", |
| @@ -193,18 +177,14 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> out | |||
| 193 | 177 | ||
| 194 | params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); | 178 | params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); |
| 195 | 179 | ||
| 196 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 197 | return NvResult::Success; | 180 | return NvResult::Success; |
| 198 | } | 181 | } |
| 199 | 182 | ||
| 200 | NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::span<u8> output) { | 183 | NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) { |
| 201 | IoctlAllocObjCtx params{}; | ||
| 202 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 203 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, | 184 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, |
| 204 | params.flags); | 185 | params.flags); |
| 205 | 186 | ||
| 206 | params.obj_id = 0x0; | 187 | params.obj_id = 0x0; |
| 207 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 208 | return NvResult::Success; | 188 | return NvResult::Success; |
| 209 | } | 189 | } |
| 210 | 190 | ||
| @@ -248,8 +228,7 @@ static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementW | |||
| 248 | return result; | 228 | return result; |
| 249 | } | 229 | } |
| 250 | 230 | ||
| 251 | NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output, | 231 | NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries) { |
| 252 | Tegra::CommandList&& entries) { | ||
| 253 | LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, | 232 | LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, |
| 254 | params.num_entries, params.flags.raw); | 233 | params.num_entries, params.flags.raw); |
| 255 | 234 | ||
| @@ -290,65 +269,55 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> o | |||
| 290 | 269 | ||
| 291 | flags.raw = 0; | 270 | flags.raw = 0; |
| 292 | 271 | ||
| 293 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); | ||
| 294 | return NvResult::Success; | 272 | return NvResult::Success; |
| 295 | } | 273 | } |
| 296 | 274 | ||
| 297 | NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output, | 275 | NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, |
| 298 | bool kickoff) { | 276 | std::span<Tegra::CommandListHeader> commands, bool kickoff) { |
| 299 | if (input.size() < sizeof(IoctlSubmitGpfifo)) { | 277 | if (params.num_entries > commands.size()) { |
| 300 | UNIMPLEMENTED(); | 278 | UNIMPLEMENTED(); |
| 301 | return NvResult::InvalidSize; | 279 | return NvResult::InvalidSize; |
| 302 | } | 280 | } |
| 303 | IoctlSubmitGpfifo params{}; | ||
| 304 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); | ||
| 305 | Tegra::CommandList entries(params.num_entries); | ||
| 306 | 281 | ||
| 282 | Tegra::CommandList entries(params.num_entries); | ||
| 307 | if (kickoff) { | 283 | if (kickoff) { |
| 308 | system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(), | 284 | system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(), |
| 309 | params.num_entries * sizeof(Tegra::CommandListHeader)); | 285 | params.num_entries * sizeof(Tegra::CommandListHeader)); |
| 310 | } else { | 286 | } else { |
| 311 | std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)], | 287 | std::memcpy(entries.command_lists.data(), commands.data(), |
| 312 | params.num_entries * sizeof(Tegra::CommandListHeader)); | 288 | params.num_entries * sizeof(Tegra::CommandListHeader)); |
| 313 | } | 289 | } |
| 314 | 290 | ||
| 315 | return SubmitGPFIFOImpl(params, output, std::move(entries)); | 291 | return SubmitGPFIFOImpl(params, std::move(entries)); |
| 316 | } | 292 | } |
| 317 | 293 | ||
| 318 | NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, | 294 | NvResult nvhost_gpu::SubmitGPFIFOBase2(IoctlSubmitGpfifo& params, |
| 319 | std::span<u8> output) { | 295 | std::span<const Tegra::CommandListHeader> commands) { |
| 320 | if (input.size() < sizeof(IoctlSubmitGpfifo)) { | 296 | if (params.num_entries > commands.size()) { |
| 321 | UNIMPLEMENTED(); | 297 | UNIMPLEMENTED(); |
| 322 | return NvResult::InvalidSize; | 298 | return NvResult::InvalidSize; |
| 323 | } | 299 | } |
| 324 | IoctlSubmitGpfifo params{}; | 300 | |
| 325 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); | ||
| 326 | Tegra::CommandList entries(params.num_entries); | 301 | Tegra::CommandList entries(params.num_entries); |
| 327 | std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size()); | 302 | std::memcpy(entries.command_lists.data(), commands.data(), |
| 328 | return SubmitGPFIFOImpl(params, output, std::move(entries)); | 303 | params.num_entries * sizeof(Tegra::CommandListHeader)); |
| 304 | return SubmitGPFIFOImpl(params, std::move(entries)); | ||
| 329 | } | 305 | } |
| 330 | 306 | ||
| 331 | NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::span<u8> output) { | 307 | NvResult nvhost_gpu::GetWaitbase(IoctlGetWaitbase& params) { |
| 332 | IoctlGetWaitbase params{}; | ||
| 333 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); | ||
| 334 | LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); | 308 | LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); |
| 335 | 309 | ||
| 336 | params.value = 0; // Seems to be hard coded at 0 | 310 | params.value = 0; // Seems to be hard coded at 0 |
| 337 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 338 | return NvResult::Success; | 311 | return NvResult::Success; |
| 339 | } | 312 | } |
| 340 | 313 | ||
| 341 | NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::span<u8> output) { | 314 | NvResult nvhost_gpu::ChannelSetTimeout(IoctlChannelSetTimeout& params) { |
| 342 | IoctlChannelSetTimeout params{}; | ||
| 343 | std::memcpy(¶ms, input.data(), sizeof(IoctlChannelSetTimeout)); | ||
| 344 | LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); | 315 | LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); |
| 345 | 316 | ||
| 346 | return NvResult::Success; | 317 | return NvResult::Success; |
| 347 | } | 318 | } |
| 348 | 319 | ||
| 349 | NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output) { | 320 | NvResult nvhost_gpu::ChannelSetTimeslice(IoctlSetTimeslice& params) { |
| 350 | IoctlSetTimeslice params{}; | ||
| 351 | std::memcpy(¶ms, input.data(), sizeof(IoctlSetTimeslice)); | ||
| 352 | LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); | 321 | LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); |
| 353 | 322 | ||
| 354 | channel_timeslice = params.timeslice; | 323 | channel_timeslice = params.timeslice; |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 529c20526..88fd228ff 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h | |||
| @@ -186,23 +186,24 @@ private: | |||
| 186 | u32_le channel_priority{}; | 186 | u32_le channel_priority{}; |
| 187 | u32_le channel_timeslice{}; | 187 | u32_le channel_timeslice{}; |
| 188 | 188 | ||
| 189 | NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output); | 189 | NvResult SetNVMAPfd(IoctlSetNvmapFD& params); |
| 190 | NvResult SetClientData(std::span<const u8> input, std::span<u8> output); | 190 | NvResult SetClientData(IoctlClientData& params); |
| 191 | NvResult GetClientData(std::span<const u8> input, std::span<u8> output); | 191 | NvResult GetClientData(IoctlClientData& params); |
| 192 | NvResult ZCullBind(std::span<const u8> input, std::span<u8> output); | 192 | NvResult ZCullBind(IoctlZCullBind& params); |
| 193 | NvResult SetErrorNotifier(std::span<const u8> input, std::span<u8> output); | 193 | NvResult SetErrorNotifier(IoctlSetErrorNotifier& params); |
| 194 | NvResult SetChannelPriority(std::span<const u8> input, std::span<u8> output); | 194 | NvResult SetChannelPriority(IoctlChannelSetPriority& params); |
| 195 | NvResult AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output); | 195 | NvResult AllocGPFIFOEx2(IoctlAllocGpfifoEx2& params); |
| 196 | NvResult AllocateObjectContext(std::span<const u8> input, std::span<u8> output); | 196 | NvResult AllocateObjectContext(IoctlAllocObjCtx& params); |
| 197 | NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output, | 197 | |
| 198 | Tegra::CommandList&& entries); | 198 | NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries); |
| 199 | NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output, | 199 | NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, |
| 200 | bool kickoff = false); | 200 | std::span<Tegra::CommandListHeader> commands, bool kickoff = false); |
| 201 | NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, | 201 | NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params, |
| 202 | std::span<u8> output); | 202 | std::span<const Tegra::CommandListHeader> commands); |
| 203 | NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output); | 203 | |
| 204 | NvResult ChannelSetTimeout(std::span<const u8> input, std::span<u8> output); | 204 | NvResult GetWaitbase(IoctlGetWaitbase& params); |
| 205 | NvResult ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output); | 205 | NvResult ChannelSetTimeout(IoctlChannelSetTimeout& params); |
| 206 | NvResult ChannelSetTimeslice(IoctlSetTimeslice& params); | ||
| 206 | 207 | ||
| 207 | EventInterface& events_interface; | 208 | EventInterface& events_interface; |
| 208 | NvCore::Container& core; | 209 | NvCore::Container& core; |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index a174442a6..f43914e1b 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/hle/service/nvdrv/core/container.h" | 8 | #include "core/hle/service/nvdrv/core/container.h" |
| 9 | #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" | ||
| 9 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" | 10 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" |
| 10 | #include "video_core/renderer_base.h" | 11 | #include "video_core/renderer_base.h" |
| 11 | 12 | ||
| @@ -25,18 +26,18 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in | |||
| 25 | if (!host1x_file.fd_to_id.contains(fd)) { | 26 | if (!host1x_file.fd_to_id.contains(fd)) { |
| 26 | host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++; | 27 | host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++; |
| 27 | } | 28 | } |
| 28 | return Submit(fd, input, output); | 29 | return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd); |
| 29 | } | 30 | } |
| 30 | case 0x2: | 31 | case 0x2: |
| 31 | return GetSyncpoint(input, output); | 32 | return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output); |
| 32 | case 0x3: | 33 | case 0x3: |
| 33 | return GetWaitbase(input, output); | 34 | return WrapFixed(this, &nvhost_nvdec::GetWaitbase, input, output); |
| 34 | case 0x7: | 35 | case 0x7: |
| 35 | return SetSubmitTimeout(input, output); | 36 | return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output); |
| 36 | case 0x9: | 37 | case 0x9: |
| 37 | return MapBuffer(input, output); | 38 | return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output); |
| 38 | case 0xa: | 39 | case 0xa: |
| 39 | return UnmapBuffer(input, output); | 40 | return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output); |
| 40 | default: | 41 | default: |
| 41 | break; | 42 | break; |
| 42 | } | 43 | } |
| @@ -44,7 +45,7 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in | |||
| 44 | case 'H': | 45 | case 'H': |
| 45 | switch (command.cmd) { | 46 | switch (command.cmd) { |
| 46 | case 0x1: | 47 | case 0x1: |
| 47 | return SetNVMAPfd(input); | 48 | return WrapFixed(this, &nvhost_nvdec::SetNVMAPfd, input, output); |
| 48 | default: | 49 | default: |
| 49 | break; | 50 | break; |
| 50 | } | 51 | } |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 61649aa4a..74c701b95 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp | |||
| @@ -29,6 +29,9 @@ std::size_t SliceVectors(std::span<const u8> input, std::vector<T>& dst, std::si | |||
| 29 | return 0; | 29 | return 0; |
| 30 | } | 30 | } |
| 31 | const size_t bytes_copied = count * sizeof(T); | 31 | const size_t bytes_copied = count * sizeof(T); |
| 32 | if (input.size() < offset + bytes_copied) { | ||
| 33 | return 0; | ||
| 34 | } | ||
| 32 | std::memcpy(dst.data(), input.data() + offset, bytes_copied); | 35 | std::memcpy(dst.data(), input.data() + offset, bytes_copied); |
| 33 | return bytes_copied; | 36 | return bytes_copied; |
| 34 | } | 37 | } |
| @@ -41,6 +44,9 @@ std::size_t WriteVectors(std::span<u8> dst, const std::vector<T>& src, std::size | |||
| 41 | return 0; | 44 | return 0; |
| 42 | } | 45 | } |
| 43 | const size_t bytes_copied = src.size() * sizeof(T); | 46 | const size_t bytes_copied = src.size() * sizeof(T); |
| 47 | if (dst.size() < offset + bytes_copied) { | ||
| 48 | return 0; | ||
| 49 | } | ||
| 44 | std::memcpy(dst.data() + offset, src.data(), bytes_copied); | 50 | std::memcpy(dst.data() + offset, src.data(), bytes_copied); |
| 45 | return bytes_copied; | 51 | return bytes_copied; |
| 46 | } | 52 | } |
| @@ -63,18 +69,14 @@ nvhost_nvdec_common::~nvhost_nvdec_common() { | |||
| 63 | core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); | 69 | core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); |
| 64 | } | 70 | } |
| 65 | 71 | ||
| 66 | NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) { | 72 | NvResult nvhost_nvdec_common::SetNVMAPfd(IoctlSetNvmapFD& params) { |
| 67 | IoctlSetNvmapFD params{}; | ||
| 68 | std::memcpy(¶ms, input.data(), sizeof(IoctlSetNvmapFD)); | ||
| 69 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); | 73 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); |
| 70 | 74 | ||
| 71 | nvmap_fd = params.nvmap_fd; | 75 | nvmap_fd = params.nvmap_fd; |
| 72 | return NvResult::Success; | 76 | return NvResult::Success; |
| 73 | } | 77 | } |
| 74 | 78 | ||
| 75 | NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output) { | 79 | NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, DeviceFD fd) { |
| 76 | IoctlSubmit params{}; | ||
| 77 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit)); | ||
| 78 | LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); | 80 | LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); |
| 79 | 81 | ||
| 80 | // Instantiate param buffers | 82 | // Instantiate param buffers |
| @@ -85,12 +87,12 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std | |||
| 85 | std::vector<u32> fence_thresholds(params.fence_count); | 87 | std::vector<u32> fence_thresholds(params.fence_count); |
| 86 | 88 | ||
| 87 | // Slice input into their respective buffers | 89 | // Slice input into their respective buffers |
| 88 | std::size_t offset = sizeof(IoctlSubmit); | 90 | std::size_t offset = 0; |
| 89 | offset += SliceVectors(input, command_buffers, params.cmd_buffer_count, offset); | 91 | offset += SliceVectors(data, command_buffers, params.cmd_buffer_count, offset); |
| 90 | offset += SliceVectors(input, relocs, params.relocation_count, offset); | 92 | offset += SliceVectors(data, relocs, params.relocation_count, offset); |
| 91 | offset += SliceVectors(input, reloc_shifts, params.relocation_count, offset); | 93 | offset += SliceVectors(data, reloc_shifts, params.relocation_count, offset); |
| 92 | offset += SliceVectors(input, syncpt_increments, params.syncpoint_count, offset); | 94 | offset += SliceVectors(data, syncpt_increments, params.syncpoint_count, offset); |
| 93 | offset += SliceVectors(input, fence_thresholds, params.fence_count, offset); | 95 | offset += SliceVectors(data, fence_thresholds, params.fence_count, offset); |
| 94 | 96 | ||
| 95 | auto& gpu = system.GPU(); | 97 | auto& gpu = system.GPU(); |
| 96 | if (gpu.UseNvdec()) { | 98 | if (gpu.UseNvdec()) { |
| @@ -108,72 +110,51 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std | |||
| 108 | cmdlist.size() * sizeof(u32)); | 110 | cmdlist.size() * sizeof(u32)); |
| 109 | gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); | 111 | gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); |
| 110 | } | 112 | } |
| 111 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); | ||
| 112 | // Some games expect command_buffers to be written back | 113 | // Some games expect command_buffers to be written back |
| 113 | offset = sizeof(IoctlSubmit); | 114 | offset = 0; |
| 114 | offset += WriteVectors(output, command_buffers, offset); | 115 | offset += WriteVectors(data, command_buffers, offset); |
| 115 | offset += WriteVectors(output, relocs, offset); | 116 | offset += WriteVectors(data, relocs, offset); |
| 116 | offset += WriteVectors(output, reloc_shifts, offset); | 117 | offset += WriteVectors(data, reloc_shifts, offset); |
| 117 | offset += WriteVectors(output, syncpt_increments, offset); | 118 | offset += WriteVectors(data, syncpt_increments, offset); |
| 118 | offset += WriteVectors(output, fence_thresholds, offset); | 119 | offset += WriteVectors(data, fence_thresholds, offset); |
| 119 | 120 | ||
| 120 | return NvResult::Success; | 121 | return NvResult::Success; |
| 121 | } | 122 | } |
| 122 | 123 | ||
| 123 | NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::span<u8> output) { | 124 | NvResult nvhost_nvdec_common::GetSyncpoint(IoctlGetSyncpoint& params) { |
| 124 | IoctlGetSyncpoint params{}; | ||
| 125 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); | ||
| 126 | LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); | 125 | LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); |
| 127 | |||
| 128 | // const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast<u32>(channel_type)]}; | ||
| 129 | params.value = channel_syncpoint; | 126 | params.value = channel_syncpoint; |
| 130 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); | ||
| 131 | |||
| 132 | return NvResult::Success; | 127 | return NvResult::Success; |
| 133 | } | 128 | } |
| 134 | 129 | ||
| 135 | NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::span<u8> output) { | 130 | NvResult nvhost_nvdec_common::GetWaitbase(IoctlGetWaitbase& params) { |
| 136 | IoctlGetWaitbase params{}; | ||
| 137 | LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); | 131 | LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); |
| 138 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); | ||
| 139 | params.value = 0; // Seems to be hard coded at 0 | 132 | params.value = 0; // Seems to be hard coded at 0 |
| 140 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); | ||
| 141 | return NvResult::Success; | 133 | return NvResult::Success; |
| 142 | } | 134 | } |
| 143 | 135 | ||
| 144 | NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::span<u8> output) { | 136 | NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries) { |
| 145 | IoctlMapBuffer params{}; | 137 | const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size())); |
| 146 | std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); | 138 | for (size_t i = 0; i < num_entries; i++) { |
| 147 | std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); | 139 | entries[i].map_address = nvmap.PinHandle(entries[i].map_handle); |
| 148 | |||
| 149 | SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); | ||
| 150 | |||
| 151 | for (auto& cmd_buffer : cmd_buffer_handles) { | ||
| 152 | cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle); | ||
| 153 | } | 140 | } |
| 154 | std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); | ||
| 155 | std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), | ||
| 156 | cmd_buffer_handles.size() * sizeof(MapBufferEntry)); | ||
| 157 | 141 | ||
| 158 | return NvResult::Success; | 142 | return NvResult::Success; |
| 159 | } | 143 | } |
| 160 | 144 | ||
| 161 | NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::span<u8> output) { | 145 | NvResult nvhost_nvdec_common::UnmapBuffer(IoctlMapBuffer& params, |
| 162 | IoctlMapBuffer params{}; | 146 | std::span<MapBufferEntry> entries) { |
| 163 | std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); | 147 | const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size())); |
| 164 | std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); | 148 | for (size_t i = 0; i < num_entries; i++) { |
| 165 | 149 | nvmap.UnpinHandle(entries[i].map_handle); | |
| 166 | SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); | 150 | entries[i] = {}; |
| 167 | for (auto& cmd_buffer : cmd_buffer_handles) { | ||
| 168 | nvmap.UnpinHandle(cmd_buffer.map_handle); | ||
| 169 | } | 151 | } |
| 170 | 152 | ||
| 171 | std::memset(output.data(), 0, output.size()); | 153 | params = {}; |
| 172 | return NvResult::Success; | 154 | return NvResult::Success; |
| 173 | } | 155 | } |
| 174 | 156 | ||
| 175 | NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::span<u8> output) { | 157 | NvResult nvhost_nvdec_common::SetSubmitTimeout(u32 timeout) { |
| 176 | std::memcpy(&submit_timeout, input.data(), input.size()); | ||
| 177 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | 158 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); |
| 178 | return NvResult::Success; | 159 | return NvResult::Success; |
| 179 | } | 160 | } |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 9bb573bfe..7ce748e18 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h | |||
| @@ -107,13 +107,13 @@ protected: | |||
| 107 | static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); | 107 | static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); |
| 108 | 108 | ||
| 109 | /// Ioctl command implementations | 109 | /// Ioctl command implementations |
| 110 | NvResult SetNVMAPfd(std::span<const u8> input); | 110 | NvResult SetNVMAPfd(IoctlSetNvmapFD&); |
| 111 | NvResult Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output); | 111 | NvResult Submit(IoctlSubmit& params, std::span<u8> input, DeviceFD fd); |
| 112 | NvResult GetSyncpoint(std::span<const u8> input, std::span<u8> output); | 112 | NvResult GetSyncpoint(IoctlGetSyncpoint& params); |
| 113 | NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output); | 113 | NvResult GetWaitbase(IoctlGetWaitbase& params); |
| 114 | NvResult MapBuffer(std::span<const u8> input, std::span<u8> output); | 114 | NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries); |
| 115 | NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); | 115 | NvResult UnmapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries); |
| 116 | NvResult SetSubmitTimeout(std::span<const u8> input, std::span<u8> output); | 116 | NvResult SetSubmitTimeout(u32 timeout); |
| 117 | 117 | ||
| 118 | Kernel::KEvent* QueryEvent(u32 event_id) override; | 118 | Kernel::KEvent* QueryEvent(u32 event_id) override; |
| 119 | 119 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index a05c8cdae..9e6b86458 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" | ||
| 8 | #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" | 9 | #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" |
| 9 | 10 | ||
| 10 | namespace Service::Nvidia::Devices { | 11 | namespace Service::Nvidia::Devices { |
| @@ -18,7 +19,7 @@ NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in | |||
| 18 | case 'H': | 19 | case 'H': |
| 19 | switch (command.cmd) { | 20 | switch (command.cmd) { |
| 20 | case 0x1: | 21 | case 0x1: |
| 21 | return SetNVMAPfd(input, output); | 22 | return WrapFixed(this, &nvhost_nvjpg::SetNVMAPfd, input, output); |
| 22 | default: | 23 | default: |
| 23 | break; | 24 | break; |
| 24 | } | 25 | } |
| @@ -46,9 +47,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in | |||
| 46 | void nvhost_nvjpg::OnOpen(DeviceFD fd) {} | 47 | void nvhost_nvjpg::OnOpen(DeviceFD fd) {} |
| 47 | void nvhost_nvjpg::OnClose(DeviceFD fd) {} | 48 | void nvhost_nvjpg::OnClose(DeviceFD fd) {} |
| 48 | 49 | ||
| 49 | NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) { | 50 | NvResult nvhost_nvjpg::SetNVMAPfd(IoctlSetNvmapFD& params) { |
| 50 | IoctlSetNvmapFD params{}; | ||
| 51 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 52 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); | 51 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); |
| 53 | 52 | ||
| 54 | nvmap_fd = params.nvmap_fd; | 53 | nvmap_fd = params.nvmap_fd; |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 5623e0d47..790c97f6a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h | |||
| @@ -33,7 +33,7 @@ private: | |||
| 33 | 33 | ||
| 34 | s32_le nvmap_fd{}; | 34 | s32_le nvmap_fd{}; |
| 35 | 35 | ||
| 36 | NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output); | 36 | NvResult SetNVMAPfd(IoctlSetNvmapFD& params); |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | } // namespace Service::Nvidia::Devices | 39 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index c0b8684c3..87f8d7c22 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "core/hle/service/nvdrv/core/container.h" | 7 | #include "core/hle/service/nvdrv/core/container.h" |
| 8 | #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" | ||
| 8 | #include "core/hle/service/nvdrv/devices/nvhost_vic.h" | 9 | #include "core/hle/service/nvdrv/devices/nvhost_vic.h" |
| 9 | #include "video_core/renderer_base.h" | 10 | #include "video_core/renderer_base.h" |
| 10 | 11 | ||
| @@ -25,16 +26,16 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu | |||
| 25 | if (!host1x_file.fd_to_id.contains(fd)) { | 26 | if (!host1x_file.fd_to_id.contains(fd)) { |
| 26 | host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++; | 27 | host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++; |
| 27 | } | 28 | } |
| 28 | return Submit(fd, input, output); | 29 | return WrapFixedVariable(this, &nvhost_vic::Submit, input, output, fd); |
| 29 | } | 30 | } |
| 30 | case 0x2: | 31 | case 0x2: |
| 31 | return GetSyncpoint(input, output); | 32 | return WrapFixed(this, &nvhost_vic::GetSyncpoint, input, output); |
| 32 | case 0x3: | 33 | case 0x3: |
| 33 | return GetWaitbase(input, output); | 34 | return WrapFixed(this, &nvhost_vic::GetWaitbase, input, output); |
| 34 | case 0x9: | 35 | case 0x9: |
| 35 | return MapBuffer(input, output); | 36 | return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output); |
| 36 | case 0xa: | 37 | case 0xa: |
| 37 | return UnmapBuffer(input, output); | 38 | return WrapFixedVariable(this, &nvhost_vic::UnmapBuffer, input, output); |
| 38 | default: | 39 | default: |
| 39 | break; | 40 | break; |
| 40 | } | 41 | } |
| @@ -42,7 +43,7 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu | |||
| 42 | case 'H': | 43 | case 'H': |
| 43 | switch (command.cmd) { | 44 | switch (command.cmd) { |
| 44 | case 0x1: | 45 | case 0x1: |
| 45 | return SetNVMAPfd(input); | 46 | return WrapFixed(this, &nvhost_vic::SetNVMAPfd, input, output); |
| 46 | default: | 47 | default: |
| 47 | break; | 48 | break; |
| 48 | } | 49 | } |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 968eaa175..71b2e62ec 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "core/hle/kernel/k_process.h" | 13 | #include "core/hle/kernel/k_process.h" |
| 14 | #include "core/hle/service/nvdrv/core/container.h" | 14 | #include "core/hle/service/nvdrv/core/container.h" |
| 15 | #include "core/hle/service/nvdrv/core/nvmap.h" | 15 | #include "core/hle/service/nvdrv/core/nvmap.h" |
| 16 | #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" | ||
| 16 | #include "core/hle/service/nvdrv/devices/nvmap.h" | 17 | #include "core/hle/service/nvdrv/devices/nvmap.h" |
| 17 | #include "core/memory.h" | 18 | #include "core/memory.h" |
| 18 | 19 | ||
| @@ -31,17 +32,17 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, | |||
| 31 | case 0x1: | 32 | case 0x1: |
| 32 | switch (command.cmd) { | 33 | switch (command.cmd) { |
| 33 | case 0x1: | 34 | case 0x1: |
| 34 | return IocCreate(input, output); | 35 | return WrapFixed(this, &nvmap::IocCreate, input, output); |
| 35 | case 0x3: | 36 | case 0x3: |
| 36 | return IocFromId(input, output); | 37 | return WrapFixed(this, &nvmap::IocFromId, input, output); |
| 37 | case 0x4: | 38 | case 0x4: |
| 38 | return IocAlloc(input, output); | 39 | return WrapFixed(this, &nvmap::IocAlloc, input, output); |
| 39 | case 0x5: | 40 | case 0x5: |
| 40 | return IocFree(input, output); | 41 | return WrapFixed(this, &nvmap::IocFree, input, output); |
| 41 | case 0x9: | 42 | case 0x9: |
| 42 | return IocParam(input, output); | 43 | return WrapFixed(this, &nvmap::IocParam, input, output); |
| 43 | case 0xe: | 44 | case 0xe: |
| 44 | return IocGetId(input, output); | 45 | return WrapFixed(this, &nvmap::IocGetId, input, output); |
| 45 | default: | 46 | default: |
| 46 | break; | 47 | break; |
| 47 | } | 48 | } |
| @@ -69,9 +70,7 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, st | |||
| 69 | void nvmap::OnOpen(DeviceFD fd) {} | 70 | void nvmap::OnOpen(DeviceFD fd) {} |
| 70 | void nvmap::OnClose(DeviceFD fd) {} | 71 | void nvmap::OnClose(DeviceFD fd) {} |
| 71 | 72 | ||
| 72 | NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) { | 73 | NvResult nvmap::IocCreate(IocCreateParams& params) { |
| 73 | IocCreateParams params; | ||
| 74 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 75 | LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); | 74 | LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); |
| 76 | 75 | ||
| 77 | std::shared_ptr<NvCore::NvMap::Handle> handle_description{}; | 76 | std::shared_ptr<NvCore::NvMap::Handle> handle_description{}; |
| @@ -85,13 +84,10 @@ NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) { | |||
| 85 | params.handle = handle_description->id; | 84 | params.handle = handle_description->id; |
| 86 | LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size); | 85 | LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size); |
| 87 | 86 | ||
| 88 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 89 | return NvResult::Success; | 87 | return NvResult::Success; |
| 90 | } | 88 | } |
| 91 | 89 | ||
| 92 | NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) { | 90 | NvResult nvmap::IocAlloc(IocAllocParams& params) { |
| 93 | IocAllocParams params; | ||
| 94 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 95 | LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); | 91 | LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); |
| 96 | 92 | ||
| 97 | if (!params.handle) { | 93 | if (!params.handle) { |
| @@ -133,14 +129,10 @@ NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) { | |||
| 133 | handle_description->size, | 129 | handle_description->size, |
| 134 | Kernel::KMemoryPermission::None, true, false) | 130 | Kernel::KMemoryPermission::None, true, false) |
| 135 | .IsSuccess()); | 131 | .IsSuccess()); |
| 136 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 137 | return result; | 132 | return result; |
| 138 | } | 133 | } |
| 139 | 134 | ||
| 140 | NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) { | 135 | NvResult nvmap::IocGetId(IocGetIdParams& params) { |
| 141 | IocGetIdParams params; | ||
| 142 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 143 | |||
| 144 | LOG_DEBUG(Service_NVDRV, "called"); | 136 | LOG_DEBUG(Service_NVDRV, "called"); |
| 145 | 137 | ||
| 146 | // See the comment in FromId for extra info on this function | 138 | // See the comment in FromId for extra info on this function |
| @@ -157,14 +149,10 @@ NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) { | |||
| 157 | } | 149 | } |
| 158 | 150 | ||
| 159 | params.id = handle_description->id; | 151 | params.id = handle_description->id; |
| 160 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 161 | return NvResult::Success; | 152 | return NvResult::Success; |
| 162 | } | 153 | } |
| 163 | 154 | ||
| 164 | NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) { | 155 | NvResult nvmap::IocFromId(IocFromIdParams& params) { |
| 165 | IocFromIdParams params; | ||
| 166 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 167 | |||
| 168 | LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id); | 156 | LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id); |
| 169 | 157 | ||
| 170 | // Handles and IDs are always the same value in nvmap however IDs can be used globally given the | 158 | // Handles and IDs are always the same value in nvmap however IDs can be used globally given the |
| @@ -188,16 +176,12 @@ NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) { | |||
| 188 | return result; | 176 | return result; |
| 189 | } | 177 | } |
| 190 | params.handle = handle_description->id; | 178 | params.handle = handle_description->id; |
| 191 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 192 | return NvResult::Success; | 179 | return NvResult::Success; |
| 193 | } | 180 | } |
| 194 | 181 | ||
| 195 | NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) { | 182 | NvResult nvmap::IocParam(IocParamParams& params) { |
| 196 | enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; | 183 | enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; |
| 197 | 184 | ||
| 198 | IocParamParams params; | ||
| 199 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 200 | |||
| 201 | LOG_DEBUG(Service_NVDRV, "called type={}", params.param); | 185 | LOG_DEBUG(Service_NVDRV, "called type={}", params.param); |
| 202 | 186 | ||
| 203 | if (!params.handle) { | 187 | if (!params.handle) { |
| @@ -237,14 +221,10 @@ NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) { | |||
| 237 | return NvResult::BadValue; | 221 | return NvResult::BadValue; |
| 238 | } | 222 | } |
| 239 | 223 | ||
| 240 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 241 | return NvResult::Success; | 224 | return NvResult::Success; |
| 242 | } | 225 | } |
| 243 | 226 | ||
| 244 | NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) { | 227 | NvResult nvmap::IocFree(IocFreeParams& params) { |
| 245 | IocFreeParams params; | ||
| 246 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 247 | |||
| 248 | LOG_DEBUG(Service_NVDRV, "called"); | 228 | LOG_DEBUG(Service_NVDRV, "called"); |
| 249 | 229 | ||
| 250 | if (!params.handle) { | 230 | if (!params.handle) { |
| @@ -267,7 +247,6 @@ NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) { | |||
| 267 | // This is possible when there's internal dups or other duplicates. | 247 | // This is possible when there's internal dups or other duplicates. |
| 268 | } | 248 | } |
| 269 | 249 | ||
| 270 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 271 | return NvResult::Success; | 250 | return NvResult::Success; |
| 272 | } | 251 | } |
| 273 | 252 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 4c0cc71cd..049c11028 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h | |||
| @@ -99,12 +99,12 @@ public: | |||
| 99 | }; | 99 | }; |
| 100 | static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); | 100 | static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); |
| 101 | 101 | ||
| 102 | NvResult IocCreate(std::span<const u8> input, std::span<u8> output); | 102 | NvResult IocCreate(IocCreateParams& params); |
| 103 | NvResult IocAlloc(std::span<const u8> input, std::span<u8> output); | 103 | NvResult IocAlloc(IocAllocParams& params); |
| 104 | NvResult IocGetId(std::span<const u8> input, std::span<u8> output); | 104 | NvResult IocGetId(IocGetIdParams& params); |
| 105 | NvResult IocFromId(std::span<const u8> input, std::span<u8> output); | 105 | NvResult IocFromId(IocFromIdParams& params); |
| 106 | NvResult IocParam(std::span<const u8> input, std::span<u8> output); | 106 | NvResult IocParam(IocParamParams& params); |
| 107 | NvResult IocFree(std::span<const u8> input, std::span<u8> output); | 107 | NvResult IocFree(IocFreeParams& params); |
| 108 | 108 | ||
| 109 | private: | 109 | private: |
| 110 | /// Id to use for the next handle that is created. | 110 | /// Id to use for the next handle that is created. |
diff --git a/src/core/hle/service/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h index 3da8cc3aa..7fd808f54 100644 --- a/src/core/hle/service/nvnflinger/buffer_item.h +++ b/src/core/hle/service/nvnflinger/buffer_item.h | |||
| @@ -15,7 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | namespace Service::android { | 16 | namespace Service::android { |
| 17 | 17 | ||
| 18 | struct GraphicBuffer; | 18 | class GraphicBuffer; |
| 19 | 19 | ||
| 20 | class BufferItem final { | 20 | class BufferItem final { |
| 21 | public: | 21 | public: |
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp index 51291539d..d91886bed 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | // https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp | 5 | // https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp |
| 6 | 6 | ||
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 9 | #include "core/hle/service/nvnflinger/buffer_item.h" | 8 | #include "core/hle/service/nvnflinger/buffer_item.h" |
| 10 | #include "core/hle/service/nvnflinger/buffer_queue_consumer.h" | 9 | #include "core/hle/service/nvnflinger/buffer_queue_consumer.h" |
| 11 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" | 10 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" |
| @@ -14,9 +13,8 @@ | |||
| 14 | 13 | ||
| 15 | namespace Service::android { | 14 | namespace Service::android { |
| 16 | 15 | ||
| 17 | BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, | 16 | BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_) |
| 18 | Service::Nvidia::NvCore::NvMap& nvmap_) | 17 | : core{std::move(core_)}, slots{core->slots} {} |
| 19 | : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {} | ||
| 20 | 18 | ||
| 21 | BufferQueueConsumer::~BufferQueueConsumer() = default; | 19 | BufferQueueConsumer::~BufferQueueConsumer() = default; |
| 22 | 20 | ||
| @@ -136,8 +134,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc | |||
| 136 | 134 | ||
| 137 | slots[slot].buffer_state = BufferState::Free; | 135 | slots[slot].buffer_state = BufferState::Free; |
| 138 | 136 | ||
| 139 | nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true); | ||
| 140 | |||
| 141 | listener = core->connected_producer_listener; | 137 | listener = core->connected_producer_listener; |
| 142 | 138 | ||
| 143 | LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot); | 139 | LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot); |
| @@ -175,6 +171,25 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_ | |||
| 175 | return Status::NoError; | 171 | return Status::NoError; |
| 176 | } | 172 | } |
| 177 | 173 | ||
| 174 | Status BufferQueueConsumer::Disconnect() { | ||
| 175 | LOG_DEBUG(Service_Nvnflinger, "called"); | ||
| 176 | |||
| 177 | std::scoped_lock lock{core->mutex}; | ||
| 178 | |||
| 179 | if (core->consumer_listener == nullptr) { | ||
| 180 | LOG_ERROR(Service_Nvnflinger, "no consumer is connected"); | ||
| 181 | return Status::BadValue; | ||
| 182 | } | ||
| 183 | |||
| 184 | core->is_abandoned = true; | ||
| 185 | core->consumer_listener = nullptr; | ||
| 186 | core->queue.clear(); | ||
| 187 | core->FreeAllBuffersLocked(); | ||
| 188 | core->SignalDequeueCondition(); | ||
| 189 | |||
| 190 | return Status::NoError; | ||
| 191 | } | ||
| 192 | |||
| 178 | Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { | 193 | Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { |
| 179 | if (out_slot_mask == nullptr) { | 194 | if (out_slot_mask == nullptr) { |
| 180 | LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr"); | 195 | LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr"); |
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h index 50ed0bb5f..0a61e8dbd 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h | |||
| @@ -13,10 +13,6 @@ | |||
| 13 | #include "core/hle/service/nvnflinger/buffer_queue_defs.h" | 13 | #include "core/hle/service/nvnflinger/buffer_queue_defs.h" |
| 14 | #include "core/hle/service/nvnflinger/status.h" | 14 | #include "core/hle/service/nvnflinger/status.h" |
| 15 | 15 | ||
| 16 | namespace Service::Nvidia::NvCore { | ||
| 17 | class NvMap; | ||
| 18 | } // namespace Service::Nvidia::NvCore | ||
| 19 | |||
| 20 | namespace Service::android { | 16 | namespace Service::android { |
| 21 | 17 | ||
| 22 | class BufferItem; | 18 | class BufferItem; |
| @@ -25,19 +21,18 @@ class IConsumerListener; | |||
| 25 | 21 | ||
| 26 | class BufferQueueConsumer final { | 22 | class BufferQueueConsumer final { |
| 27 | public: | 23 | public: |
| 28 | explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, | 24 | explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_); |
| 29 | Service::Nvidia::NvCore::NvMap& nvmap_); | ||
| 30 | ~BufferQueueConsumer(); | 25 | ~BufferQueueConsumer(); |
| 31 | 26 | ||
| 32 | Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); | 27 | Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); |
| 33 | Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); | 28 | Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); |
| 34 | Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); | 29 | Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); |
| 30 | Status Disconnect(); | ||
| 35 | Status GetReleasedBuffers(u64* out_slot_mask); | 31 | Status GetReleasedBuffers(u64* out_slot_mask); |
| 36 | 32 | ||
| 37 | private: | 33 | private: |
| 38 | std::shared_ptr<BufferQueueCore> core; | 34 | std::shared_ptr<BufferQueueCore> core; |
| 39 | BufferQueueDefs::SlotsType& slots; | 35 | BufferQueueDefs::SlotsType& slots; |
| 40 | Service::Nvidia::NvCore::NvMap& nvmap; | ||
| 41 | }; | 36 | }; |
| 42 | 37 | ||
| 43 | } // namespace Service::android | 38 | } // namespace Service::android |
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp index ed66f6f5b..4ed5e5978 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp | |||
| @@ -14,24 +14,12 @@ BufferQueueCore::BufferQueueCore() = default; | |||
| 14 | 14 | ||
| 15 | BufferQueueCore::~BufferQueueCore() = default; | 15 | BufferQueueCore::~BufferQueueCore() = default; |
| 16 | 16 | ||
| 17 | void BufferQueueCore::NotifyShutdown() { | ||
| 18 | std::scoped_lock lock{mutex}; | ||
| 19 | |||
| 20 | is_shutting_down = true; | ||
| 21 | |||
| 22 | SignalDequeueCondition(); | ||
| 23 | } | ||
| 24 | |||
| 25 | void BufferQueueCore::SignalDequeueCondition() { | 17 | void BufferQueueCore::SignalDequeueCondition() { |
| 26 | dequeue_possible.store(true); | 18 | dequeue_possible.store(true); |
| 27 | dequeue_condition.notify_all(); | 19 | dequeue_condition.notify_all(); |
| 28 | } | 20 | } |
| 29 | 21 | ||
| 30 | bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) { | 22 | bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) { |
| 31 | if (is_shutting_down) { | ||
| 32 | return false; | ||
| 33 | } | ||
| 34 | |||
| 35 | dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); | 23 | dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); |
| 36 | dequeue_possible.store(false); | 24 | dequeue_possible.store(false); |
| 37 | 25 | ||
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h index 9164f08a0..e513d183b 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h | |||
| @@ -34,8 +34,6 @@ public: | |||
| 34 | BufferQueueCore(); | 34 | BufferQueueCore(); |
| 35 | ~BufferQueueCore(); | 35 | ~BufferQueueCore(); |
| 36 | 36 | ||
| 37 | void NotifyShutdown(); | ||
| 38 | |||
| 39 | private: | 37 | private: |
| 40 | void SignalDequeueCondition(); | 38 | void SignalDequeueCondition(); |
| 41 | bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk); | 39 | bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk); |
| @@ -74,7 +72,6 @@ private: | |||
| 74 | u32 transform_hint{}; | 72 | u32 transform_hint{}; |
| 75 | bool is_allocating{}; | 73 | bool is_allocating{}; |
| 76 | mutable std::condition_variable_any is_allocating_condition; | 74 | mutable std::condition_variable_any is_allocating_condition; |
| 77 | bool is_shutting_down{}; | ||
| 78 | }; | 75 | }; |
| 79 | 76 | ||
| 80 | } // namespace Service::android | 77 | } // namespace Service::android |
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index 6e7a49658..5d8762d25 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp | |||
| @@ -13,7 +13,6 @@ | |||
| 13 | #include "core/hle/kernel/kernel.h" | 13 | #include "core/hle/kernel/kernel.h" |
| 14 | #include "core/hle/service/hle_ipc.h" | 14 | #include "core/hle/service/hle_ipc.h" |
| 15 | #include "core/hle/service/kernel_helpers.h" | 15 | #include "core/hle/service/kernel_helpers.h" |
| 16 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 17 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" | 16 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" |
| 18 | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" | 17 | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" |
| 19 | #include "core/hle/service/nvnflinger/consumer_listener.h" | 18 | #include "core/hle/service/nvnflinger/consumer_listener.h" |
| @@ -533,8 +532,6 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, | |||
| 533 | item.is_droppable = core->dequeue_buffer_cannot_block || async; | 532 | item.is_droppable = core->dequeue_buffer_cannot_block || async; |
| 534 | item.swap_interval = swap_interval; | 533 | item.swap_interval = swap_interval; |
| 535 | 534 | ||
| 536 | nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true); | ||
| 537 | |||
| 538 | sticky_transform = sticky_transform_; | 535 | sticky_transform = sticky_transform_; |
| 539 | 536 | ||
| 540 | if (core->queue.empty()) { | 537 | if (core->queue.empty()) { |
| @@ -744,19 +741,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { | |||
| 744 | return Status::NoError; | 741 | return Status::NoError; |
| 745 | } | 742 | } |
| 746 | 743 | ||
| 747 | // HACK: We are not Android. Remove handle for items in queue, and clear queue. | ||
| 748 | // Allows synchronous destruction of nvmap handles. | ||
| 749 | for (auto& item : core->queue) { | ||
| 750 | nvmap.FreeHandle(item.graphic_buffer->BufferId(), true); | ||
| 751 | } | ||
| 752 | core->queue.clear(); | ||
| 753 | |||
| 754 | switch (api) { | 744 | switch (api) { |
| 755 | case NativeWindowApi::Egl: | 745 | case NativeWindowApi::Egl: |
| 756 | case NativeWindowApi::Cpu: | 746 | case NativeWindowApi::Cpu: |
| 757 | case NativeWindowApi::Media: | 747 | case NativeWindowApi::Media: |
| 758 | case NativeWindowApi::Camera: | 748 | case NativeWindowApi::Camera: |
| 759 | if (core->connected_api == api) { | 749 | if (core->connected_api == api) { |
| 750 | core->queue.clear(); | ||
| 760 | core->FreeAllBuffersLocked(); | 751 | core->FreeAllBuffersLocked(); |
| 761 | core->connected_producer_listener = nullptr; | 752 | core->connected_producer_listener = nullptr; |
| 762 | core->connected_api = NativeWindowApi::NoConnectedApi; | 753 | core->connected_api = NativeWindowApi::NoConnectedApi; |
| @@ -785,7 +776,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { | |||
| 785 | } | 776 | } |
| 786 | 777 | ||
| 787 | Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, | 778 | Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, |
| 788 | const std::shared_ptr<GraphicBuffer>& buffer) { | 779 | const std::shared_ptr<NvGraphicBuffer>& buffer) { |
| 789 | LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); | 780 | LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); |
| 790 | 781 | ||
| 791 | if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { | 782 | if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { |
| @@ -796,7 +787,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, | |||
| 796 | 787 | ||
| 797 | slots[slot] = {}; | 788 | slots[slot] = {}; |
| 798 | slots[slot].fence = Fence::NoFence(); | 789 | slots[slot].fence = Fence::NoFence(); |
| 799 | slots[slot].graphic_buffer = buffer; | 790 | slots[slot].graphic_buffer = std::make_shared<GraphicBuffer>(nvmap, buffer); |
| 800 | slots[slot].frame_number = 0; | 791 | slots[slot].frame_number = 0; |
| 801 | 792 | ||
| 802 | // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for | 793 | // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for |
| @@ -839,7 +830,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u | |||
| 839 | } | 830 | } |
| 840 | case TransactionId::SetPreallocatedBuffer: { | 831 | case TransactionId::SetPreallocatedBuffer: { |
| 841 | const auto slot = parcel_in.Read<s32>(); | 832 | const auto slot = parcel_in.Read<s32>(); |
| 842 | const auto buffer = parcel_in.ReadObject<GraphicBuffer>(); | 833 | const auto buffer = parcel_in.ReadObject<NvGraphicBuffer>(); |
| 843 | 834 | ||
| 844 | status = SetPreallocatedBuffer(slot, buffer); | 835 | status = SetPreallocatedBuffer(slot, buffer); |
| 845 | break; | 836 | break; |
| @@ -867,7 +858,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u | |||
| 867 | 858 | ||
| 868 | status = RequestBuffer(slot, &buf); | 859 | status = RequestBuffer(slot, &buf); |
| 869 | 860 | ||
| 870 | parcel_out.WriteFlattenedObject(buf); | 861 | parcel_out.WriteFlattenedObject<NvGraphicBuffer>(buf.get()); |
| 871 | break; | 862 | break; |
| 872 | } | 863 | } |
| 873 | case TransactionId::QueueBuffer: { | 864 | case TransactionId::QueueBuffer: { |
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h index d4201c104..64c17d56c 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h | |||
| @@ -38,6 +38,7 @@ namespace Service::android { | |||
| 38 | 38 | ||
| 39 | class BufferQueueCore; | 39 | class BufferQueueCore; |
| 40 | class IProducerListener; | 40 | class IProducerListener; |
| 41 | struct NvGraphicBuffer; | ||
| 41 | 42 | ||
| 42 | class BufferQueueProducer final : public IBinder { | 43 | class BufferQueueProducer final : public IBinder { |
| 43 | public: | 44 | public: |
| @@ -65,7 +66,7 @@ public: | |||
| 65 | bool producer_controlled_by_app, QueueBufferOutput* output); | 66 | bool producer_controlled_by_app, QueueBufferOutput* output); |
| 66 | 67 | ||
| 67 | Status Disconnect(NativeWindowApi api); | 68 | Status Disconnect(NativeWindowApi api); |
| 68 | Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer); | 69 | Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<NvGraphicBuffer>& buffer); |
| 69 | 70 | ||
| 70 | private: | 71 | private: |
| 71 | BufferQueueProducer(const BufferQueueProducer&) = delete; | 72 | BufferQueueProducer(const BufferQueueProducer&) = delete; |
diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h index d8c9dec3b..d25bca049 100644 --- a/src/core/hle/service/nvnflinger/buffer_slot.h +++ b/src/core/hle/service/nvnflinger/buffer_slot.h | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | namespace Service::android { | 14 | namespace Service::android { |
| 15 | 15 | ||
| 16 | struct GraphicBuffer; | 16 | class GraphicBuffer; |
| 17 | 17 | ||
| 18 | enum class BufferState : u32 { | 18 | enum class BufferState : u32 { |
| 19 | Free = 0, | 19 | Free = 0, |
diff --git a/src/core/hle/service/nvnflinger/buffer_transform_flags.h b/src/core/hle/service/nvnflinger/buffer_transform_flags.h index 67aa5dad6..ffe579718 100644 --- a/src/core/hle/service/nvnflinger/buffer_transform_flags.h +++ b/src/core/hle/service/nvnflinger/buffer_transform_flags.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "common/common_funcs.h" | ||
| 6 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 7 | 8 | ||
| 8 | namespace Service::android { | 9 | namespace Service::android { |
| @@ -21,5 +22,6 @@ enum class BufferTransformFlags : u32 { | |||
| 21 | /// Rotate source image 270 degrees clockwise | 22 | /// Rotate source image 270 degrees clockwise |
| 22 | Rotate270 = 0x07, | 23 | Rotate270 = 0x07, |
| 23 | }; | 24 | }; |
| 25 | DECLARE_ENUM_FLAG_OPERATORS(BufferTransformFlags); | ||
| 24 | 26 | ||
| 25 | } // namespace Service::android | 27 | } // namespace Service::android |
diff --git a/src/core/hle/service/nvnflinger/consumer_base.cpp b/src/core/hle/service/nvnflinger/consumer_base.cpp index 4dcda8dac..1059e72bf 100644 --- a/src/core/hle/service/nvnflinger/consumer_base.cpp +++ b/src/core/hle/service/nvnflinger/consumer_base.cpp | |||
| @@ -27,6 +27,26 @@ void ConsumerBase::Connect(bool controlled_by_app) { | |||
| 27 | consumer->Connect(shared_from_this(), controlled_by_app); | 27 | consumer->Connect(shared_from_this(), controlled_by_app); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | void ConsumerBase::Abandon() { | ||
| 31 | LOG_DEBUG(Service_Nvnflinger, "called"); | ||
| 32 | |||
| 33 | std::scoped_lock lock{mutex}; | ||
| 34 | |||
| 35 | if (!is_abandoned) { | ||
| 36 | this->AbandonLocked(); | ||
| 37 | is_abandoned = true; | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | void ConsumerBase::AbandonLocked() { | ||
| 42 | for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { | ||
| 43 | this->FreeBufferLocked(i); | ||
| 44 | } | ||
| 45 | // disconnect from the BufferQueue | ||
| 46 | consumer->Disconnect(); | ||
| 47 | consumer = nullptr; | ||
| 48 | } | ||
| 49 | |||
| 30 | void ConsumerBase::FreeBufferLocked(s32 slot_index) { | 50 | void ConsumerBase::FreeBufferLocked(s32 slot_index) { |
| 31 | LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index); | 51 | LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index); |
| 32 | 52 | ||
diff --git a/src/core/hle/service/nvnflinger/consumer_base.h b/src/core/hle/service/nvnflinger/consumer_base.h index 264829414..ea3e9e97a 100644 --- a/src/core/hle/service/nvnflinger/consumer_base.h +++ b/src/core/hle/service/nvnflinger/consumer_base.h | |||
| @@ -24,6 +24,7 @@ class BufferQueueConsumer; | |||
| 24 | class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> { | 24 | class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> { |
| 25 | public: | 25 | public: |
| 26 | void Connect(bool controlled_by_app); | 26 | void Connect(bool controlled_by_app); |
| 27 | void Abandon(); | ||
| 27 | 28 | ||
| 28 | protected: | 29 | protected: |
| 29 | explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); | 30 | explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); |
| @@ -34,6 +35,7 @@ protected: | |||
| 34 | void OnBuffersReleased() override; | 35 | void OnBuffersReleased() override; |
| 35 | void OnSidebandStreamChanged() override; | 36 | void OnSidebandStreamChanged() override; |
| 36 | 37 | ||
| 38 | void AbandonLocked(); | ||
| 37 | void FreeBufferLocked(s32 slot_index); | 39 | void FreeBufferLocked(s32 slot_index); |
| 38 | Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); | 40 | Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); |
| 39 | Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer); | 41 | Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer); |
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp index 2e29bc848..d7db24f42 100644 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp | |||
| @@ -71,24 +71,17 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address, | |||
| 71 | R_SUCCEED(); | 71 | R_SUCCEED(); |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | template <typename T> | ||
| 75 | std::span<u8> SerializeIoc(T& params) { | ||
| 76 | return std::span(reinterpret_cast<u8*>(std::addressof(params)), sizeof(T)); | ||
| 77 | } | ||
| 78 | |||
| 79 | Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) { | 74 | Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) { |
| 80 | // Create a handle. | 75 | // Create a handle. |
| 81 | Nvidia::Devices::nvmap::IocCreateParams create_in_params{ | 76 | Nvidia::Devices::nvmap::IocCreateParams create_params{ |
| 82 | .size = size, | 77 | .size = size, |
| 83 | .handle = 0, | 78 | .handle = 0, |
| 84 | }; | 79 | }; |
| 85 | Nvidia::Devices::nvmap::IocCreateParams create_out_params{}; | 80 | R_UNLESS(nvmap.IocCreate(create_params) == Nvidia::NvResult::Success, |
| 86 | R_UNLESS(nvmap.IocCreate(SerializeIoc(create_in_params), SerializeIoc(create_out_params)) == | ||
| 87 | Nvidia::NvResult::Success, | ||
| 88 | VI::ResultOperationFailed); | 81 | VI::ResultOperationFailed); |
| 89 | 82 | ||
| 90 | // Assign the output handle. | 83 | // Assign the output handle. |
| 91 | *out_nv_map_handle = create_out_params.handle; | 84 | *out_nv_map_handle = create_params.handle; |
| 92 | 85 | ||
| 93 | // We succeeded. | 86 | // We succeeded. |
| 94 | R_SUCCEED(); | 87 | R_SUCCEED(); |
| @@ -96,13 +89,10 @@ Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, | |||
| 96 | 89 | ||
| 97 | Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) { | 90 | Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) { |
| 98 | // Free the handle. | 91 | // Free the handle. |
| 99 | Nvidia::Devices::nvmap::IocFreeParams free_in_params{ | 92 | Nvidia::Devices::nvmap::IocFreeParams free_params{ |
| 100 | .handle = handle, | 93 | .handle = handle, |
| 101 | }; | 94 | }; |
| 102 | Nvidia::Devices::nvmap::IocFreeParams free_out_params{}; | 95 | R_UNLESS(nvmap.IocFree(free_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed); |
| 103 | R_UNLESS(nvmap.IocFree(SerializeIoc(free_in_params), SerializeIoc(free_out_params)) == | ||
| 104 | Nvidia::NvResult::Success, | ||
| 105 | VI::ResultOperationFailed); | ||
| 106 | 96 | ||
| 107 | // We succeeded. | 97 | // We succeeded. |
| 108 | R_SUCCEED(); | 98 | R_SUCCEED(); |
| @@ -111,7 +101,7 @@ Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) { | |||
| 111 | Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer, | 101 | Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer, |
| 112 | u32 size) { | 102 | u32 size) { |
| 113 | // Assign the allocated memory to the handle. | 103 | // Assign the allocated memory to the handle. |
| 114 | Nvidia::Devices::nvmap::IocAllocParams alloc_in_params{ | 104 | Nvidia::Devices::nvmap::IocAllocParams alloc_params{ |
| 115 | .handle = handle, | 105 | .handle = handle, |
| 116 | .heap_mask = 0, | 106 | .heap_mask = 0, |
| 117 | .flags = {}, | 107 | .flags = {}, |
| @@ -119,10 +109,7 @@ Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::Proce | |||
| 119 | .kind = 0, | 109 | .kind = 0, |
| 120 | .address = GetInteger(buffer), | 110 | .address = GetInteger(buffer), |
| 121 | }; | 111 | }; |
| 122 | Nvidia::Devices::nvmap::IocAllocParams alloc_out_params{}; | 112 | R_UNLESS(nvmap.IocAlloc(alloc_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed); |
| 123 | R_UNLESS(nvmap.IocAlloc(SerializeIoc(alloc_in_params), SerializeIoc(alloc_out_params)) == | ||
| 124 | Nvidia::NvResult::Success, | ||
| 125 | VI::ResultOperationFailed); | ||
| 126 | 113 | ||
| 127 | // We succeeded. | 114 | // We succeeded. |
| 128 | R_SUCCEED(); | 115 | R_SUCCEED(); |
| @@ -179,7 +166,7 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] { | |||
| 179 | }(); | 166 | }(); |
| 180 | 167 | ||
| 181 | void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { | 168 | void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { |
| 182 | auto buffer = std::make_shared<android::GraphicBuffer>(); | 169 | auto buffer = std::make_shared<android::NvGraphicBuffer>(); |
| 183 | buffer->width = SharedBufferWidth; | 170 | buffer->width = SharedBufferWidth; |
| 184 | buffer->height = SharedBufferHeight; | 171 | buffer->height = SharedBufferHeight; |
| 185 | buffer->stride = SharedBufferBlockLinearStride; | 172 | buffer->stride = SharedBufferBlockLinearStride; |
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index bebb45eae..0745434c5 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp | |||
| @@ -47,7 +47,10 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) { | |||
| 47 | vsync_signal.Wait(); | 47 | vsync_signal.Wait(); |
| 48 | 48 | ||
| 49 | const auto lock_guard = Lock(); | 49 | const auto lock_guard = Lock(); |
| 50 | Compose(); | 50 | |
| 51 | if (!is_abandoned) { | ||
| 52 | Compose(); | ||
| 53 | } | ||
| 51 | } | 54 | } |
| 52 | } | 55 | } |
| 53 | 56 | ||
| @@ -98,7 +101,6 @@ Nvnflinger::~Nvnflinger() { | |||
| 98 | } | 101 | } |
| 99 | 102 | ||
| 100 | ShutdownLayers(); | 103 | ShutdownLayers(); |
| 101 | vsync_thread = {}; | ||
| 102 | 104 | ||
| 103 | if (nvdrv) { | 105 | if (nvdrv) { |
| 104 | nvdrv->Close(disp_fd); | 106 | nvdrv->Close(disp_fd); |
| @@ -106,12 +108,20 @@ Nvnflinger::~Nvnflinger() { | |||
| 106 | } | 108 | } |
| 107 | 109 | ||
| 108 | void Nvnflinger::ShutdownLayers() { | 110 | void Nvnflinger::ShutdownLayers() { |
| 109 | const auto lock_guard = Lock(); | 111 | // Abandon consumers. |
| 110 | for (auto& display : displays) { | 112 | { |
| 111 | for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { | 113 | const auto lock_guard = Lock(); |
| 112 | display.GetLayer(layer).Core().NotifyShutdown(); | 114 | for (auto& display : displays) { |
| 115 | for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { | ||
| 116 | display.GetLayer(layer).GetConsumer().Abandon(); | ||
| 117 | } | ||
| 113 | } | 118 | } |
| 119 | |||
| 120 | is_abandoned = true; | ||
| 114 | } | 121 | } |
| 122 | |||
| 123 | // Join the vsync thread, if it exists. | ||
| 124 | vsync_thread = {}; | ||
| 115 | } | 125 | } |
| 116 | 126 | ||
| 117 | void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { | 127 | void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { |
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index 959d8b46b..f5d73acdb 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h | |||
| @@ -140,6 +140,8 @@ private: | |||
| 140 | 140 | ||
| 141 | s32 swap_interval = 1; | 141 | s32 swap_interval = 1; |
| 142 | 142 | ||
| 143 | bool is_abandoned = false; | ||
| 144 | |||
| 143 | /// Event that handles screen composition. | 145 | /// Event that handles screen composition. |
| 144 | std::shared_ptr<Core::Timing::EventType> multi_composition_event; | 146 | std::shared_ptr<Core::Timing::EventType> multi_composition_event; |
| 145 | std::shared_ptr<Core::Timing::EventType> single_composition_event; | 147 | std::shared_ptr<Core::Timing::EventType> single_composition_event; |
diff --git a/src/core/hle/service/nvnflinger/status.h b/src/core/hle/service/nvnflinger/status.h index 7af166c40..3fa0fe15b 100644 --- a/src/core/hle/service/nvnflinger/status.h +++ b/src/core/hle/service/nvnflinger/status.h | |||
| @@ -19,7 +19,7 @@ enum class Status : s32 { | |||
| 19 | Busy = -16, | 19 | Busy = -16, |
| 20 | NoInit = -19, | 20 | NoInit = -19, |
| 21 | BadValue = -22, | 21 | BadValue = -22, |
| 22 | InvalidOperation = -37, | 22 | InvalidOperation = -38, |
| 23 | BufferNeedsReallocation = 1, | 23 | BufferNeedsReallocation = 1, |
| 24 | ReleaseAllBuffers = 2, | 24 | ReleaseAllBuffers = 2, |
| 25 | }; | 25 | }; |
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp new file mode 100644 index 000000000..ce70946ec --- /dev/null +++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 5 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" | ||
| 6 | |||
| 7 | namespace Service::android { | ||
| 8 | |||
| 9 | static NvGraphicBuffer GetBuffer(std::shared_ptr<NvGraphicBuffer>& buffer) { | ||
| 10 | if (buffer) { | ||
| 11 | return *buffer; | ||
| 12 | } else { | ||
| 13 | return {}; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | GraphicBuffer::GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) | ||
| 18 | : NvGraphicBuffer(width_, height_, format_, usage_), m_nvmap(nullptr) {} | ||
| 19 | |||
| 20 | GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap, | ||
| 21 | std::shared_ptr<NvGraphicBuffer> buffer) | ||
| 22 | : NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) { | ||
| 23 | if (this->BufferId() > 0) { | ||
| 24 | m_nvmap->DuplicateHandle(this->BufferId(), true); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | GraphicBuffer::~GraphicBuffer() { | ||
| 29 | if (m_nvmap != nullptr && this->BufferId() > 0) { | ||
| 30 | m_nvmap->FreeHandle(this->BufferId(), true); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | } // namespace Service::android | ||
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h index 3eac5cedd..da430aa75 100644 --- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h +++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h | |||
| @@ -6,16 +6,22 @@ | |||
| 6 | 6 | ||
| 7 | #pragma once | 7 | #pragma once |
| 8 | 8 | ||
| 9 | #include <memory> | ||
| 10 | |||
| 9 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 11 | #include "core/hle/service/nvnflinger/pixel_format.h" | 13 | #include "core/hle/service/nvnflinger/pixel_format.h" |
| 12 | 14 | ||
| 15 | namespace Service::Nvidia::NvCore { | ||
| 16 | class NvMap; | ||
| 17 | } // namespace Service::Nvidia::NvCore | ||
| 18 | |||
| 13 | namespace Service::android { | 19 | namespace Service::android { |
| 14 | 20 | ||
| 15 | struct GraphicBuffer final { | 21 | struct NvGraphicBuffer { |
| 16 | constexpr GraphicBuffer() = default; | 22 | constexpr NvGraphicBuffer() = default; |
| 17 | 23 | ||
| 18 | constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) | 24 | constexpr NvGraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) |
| 19 | : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_}, | 25 | : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_}, |
| 20 | usage{static_cast<s32>(usage_)} {} | 26 | usage{static_cast<s32>(usage_)} {} |
| 21 | 27 | ||
| @@ -93,6 +99,17 @@ struct GraphicBuffer final { | |||
| 93 | u32 offset{}; | 99 | u32 offset{}; |
| 94 | INSERT_PADDING_WORDS(60); | 100 | INSERT_PADDING_WORDS(60); |
| 95 | }; | 101 | }; |
| 96 | static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); | 102 | static_assert(sizeof(NvGraphicBuffer) == 0x16C, "NvGraphicBuffer has wrong size"); |
| 103 | |||
| 104 | class GraphicBuffer final : public NvGraphicBuffer { | ||
| 105 | public: | ||
| 106 | explicit GraphicBuffer(u32 width, u32 height, PixelFormat format, u32 usage); | ||
| 107 | explicit GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap, | ||
| 108 | std::shared_ptr<NvGraphicBuffer> buffer); | ||
| 109 | ~GraphicBuffer(); | ||
| 110 | |||
| 111 | private: | ||
| 112 | Service::Nvidia::NvCore::NvMap* m_nvmap{}; | ||
| 113 | }; | ||
| 97 | 114 | ||
| 98 | } // namespace Service::android | 115 | } // namespace Service::android |
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index ec3af80af..f5edfdc8b 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp | |||
| @@ -19,19 +19,8 @@ | |||
| 19 | 19 | ||
| 20 | namespace Service::Set { | 20 | namespace Service::Set { |
| 21 | 21 | ||
| 22 | namespace { | 22 | Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, |
| 23 | constexpr u64 SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET = 0x05; | 23 | GetFirmwareVersionType type) { |
| 24 | |||
| 25 | enum class GetFirmwareVersionType { | ||
| 26 | Version1, | ||
| 27 | Version2, | ||
| 28 | }; | ||
| 29 | |||
| 30 | void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx, | ||
| 31 | GetFirmwareVersionType type) { | ||
| 32 | ASSERT_MSG(ctx.GetWriteBufferSize() == 0x100, | ||
| 33 | "FirmwareVersion output buffer must be 0x100 bytes in size!"); | ||
| 34 | |||
| 35 | constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; | 24 | constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; |
| 36 | auto& fsc = system.GetFileSystemController(); | 25 | auto& fsc = system.GetFileSystemController(); |
| 37 | 26 | ||
| @@ -52,39 +41,34 @@ void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx, | |||
| 52 | FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); | 41 | FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); |
| 53 | } | 42 | } |
| 54 | 43 | ||
| 55 | const auto early_exit_failure = [&ctx](std::string_view desc, Result code) { | 44 | const auto early_exit_failure = [](std::string_view desc, Result code) { |
| 56 | LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", | 45 | LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", |
| 57 | desc); | 46 | desc); |
| 58 | IPC::ResponseBuilder rb{ctx, 2}; | 47 | return code; |
| 59 | rb.Push(code); | ||
| 60 | }; | 48 | }; |
| 61 | 49 | ||
| 62 | const auto ver_file = romfs->GetFile("file"); | 50 | const auto ver_file = romfs->GetFile("file"); |
| 63 | if (ver_file == nullptr) { | 51 | if (ver_file == nullptr) { |
| 64 | early_exit_failure("The system version archive didn't contain the file 'file'.", | 52 | return early_exit_failure("The system version archive didn't contain the file 'file'.", |
| 65 | FileSys::ERROR_INVALID_ARGUMENT); | 53 | FileSys::ERROR_INVALID_ARGUMENT); |
| 66 | return; | ||
| 67 | } | 54 | } |
| 68 | 55 | ||
| 69 | auto data = ver_file->ReadAllBytes(); | 56 | auto data = ver_file->ReadAllBytes(); |
| 70 | if (data.size() != 0x100) { | 57 | if (data.size() != sizeof(FirmwareVersionFormat)) { |
| 71 | early_exit_failure("The system version file 'file' was not the correct size.", | 58 | return early_exit_failure("The system version file 'file' was not the correct size.", |
| 72 | FileSys::ERROR_OUT_OF_BOUNDS); | 59 | FileSys::ERROR_OUT_OF_BOUNDS); |
| 73 | return; | ||
| 74 | } | 60 | } |
| 75 | 61 | ||
| 62 | std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat)); | ||
| 63 | |||
| 76 | // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will | 64 | // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will |
| 77 | // zero out the REVISION_MINOR field. | 65 | // zero out the REVISION_MINOR field. |
| 78 | if (type == GetFirmwareVersionType::Version1) { | 66 | if (type == GetFirmwareVersionType::Version1) { |
| 79 | data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0; | 67 | out_firmware.revision_minor = 0; |
| 80 | } | 68 | } |
| 81 | 69 | ||
| 82 | ctx.WriteBuffer(data); | 70 | return ResultSuccess; |
| 83 | |||
| 84 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 85 | rb.Push(ResultSuccess); | ||
| 86 | } | 71 | } |
| 87 | } // Anonymous namespace | ||
| 88 | 72 | ||
| 89 | void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { | 73 | void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { |
| 90 | IPC::RequestParser rp{ctx}; | 74 | IPC::RequestParser rp{ctx}; |
| @@ -98,12 +82,32 @@ void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { | |||
| 98 | 82 | ||
| 99 | void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { | 83 | void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { |
| 100 | LOG_DEBUG(Service_SET, "called"); | 84 | LOG_DEBUG(Service_SET, "called"); |
| 101 | GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version1); | 85 | |
| 86 | FirmwareVersionFormat firmware_data{}; | ||
| 87 | const auto result = | ||
| 88 | GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1); | ||
| 89 | |||
| 90 | if (result.IsSuccess()) { | ||
| 91 | ctx.WriteBuffer(firmware_data); | ||
| 92 | } | ||
| 93 | |||
| 94 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 95 | rb.Push(result); | ||
| 102 | } | 96 | } |
| 103 | 97 | ||
| 104 | void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { | 98 | void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { |
| 105 | LOG_DEBUG(Service_SET, "called"); | 99 | LOG_DEBUG(Service_SET, "called"); |
| 106 | GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version2); | 100 | |
| 101 | FirmwareVersionFormat firmware_data{}; | ||
| 102 | const auto result = | ||
| 103 | GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2); | ||
| 104 | |||
| 105 | if (result.IsSuccess()) { | ||
| 106 | ctx.WriteBuffer(firmware_data); | ||
| 107 | } | ||
| 108 | |||
| 109 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 110 | rb.Push(result); | ||
| 107 | } | 111 | } |
| 108 | 112 | ||
| 109 | void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { | 113 | void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { |
| @@ -431,8 +435,7 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) { | |||
| 431 | void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { | 435 | void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { |
| 432 | u8 battery_percentage_flag{1}; | 436 | u8 battery_percentage_flag{1}; |
| 433 | 437 | ||
| 434 | LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}", | 438 | LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag); |
| 435 | battery_percentage_flag); | ||
| 436 | 439 | ||
| 437 | IPC::ResponseBuilder rb{ctx, 3}; | 440 | IPC::ResponseBuilder rb{ctx, 3}; |
| 438 | rb.Push(ResultSuccess); | 441 | rb.Push(ResultSuccess); |
| @@ -492,6 +495,29 @@ void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) { | |||
| 492 | rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); | 495 | rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); |
| 493 | } | 496 | } |
| 494 | 497 | ||
| 498 | void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) { | ||
| 499 | LOG_DEBUG(Service_SET, "(STUBBED) called"); | ||
| 500 | |||
| 501 | const HomeMenuScheme default_color = { | ||
| 502 | .main = 0xFF323232, | ||
| 503 | .back = 0xFF323232, | ||
| 504 | .sub = 0xFFFFFFFF, | ||
| 505 | .bezel = 0xFFFFFFFF, | ||
| 506 | .extra = 0xFF000000, | ||
| 507 | }; | ||
| 508 | |||
| 509 | IPC::ResponseBuilder rb{ctx, 7}; | ||
| 510 | rb.Push(ResultSuccess); | ||
| 511 | rb.PushRaw(default_color); | ||
| 512 | } | ||
| 513 | |||
| 514 | void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) { | ||
| 515 | LOG_WARNING(Service_SET, "(STUBBED) called"); | ||
| 516 | |||
| 517 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 518 | rb.Push(ResultSuccess); | ||
| 519 | rb.Push(0); | ||
| 520 | } | ||
| 495 | void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { | 521 | void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { |
| 496 | LOG_WARNING(Service_SET, "(STUBBED) called"); | 522 | LOG_WARNING(Service_SET, "(STUBBED) called"); |
| 497 | 523 | ||
| @@ -674,7 +700,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { | |||
| 674 | {171, nullptr, "SetChineseTraditionalInputMethod"}, | 700 | {171, nullptr, "SetChineseTraditionalInputMethod"}, |
| 675 | {172, nullptr, "GetPtmCycleCountReliability"}, | 701 | {172, nullptr, "GetPtmCycleCountReliability"}, |
| 676 | {173, nullptr, "SetPtmCycleCountReliability"}, | 702 | {173, nullptr, "SetPtmCycleCountReliability"}, |
| 677 | {174, nullptr, "GetHomeMenuScheme"}, | 703 | {174, &SET_SYS::GetHomeMenuScheme, "GetHomeMenuScheme"}, |
| 678 | {175, nullptr, "GetThemeSettings"}, | 704 | {175, nullptr, "GetThemeSettings"}, |
| 679 | {176, nullptr, "SetThemeSettings"}, | 705 | {176, nullptr, "SetThemeSettings"}, |
| 680 | {177, nullptr, "GetThemeKey"}, | 706 | {177, nullptr, "GetThemeKey"}, |
| @@ -685,7 +711,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { | |||
| 685 | {182, nullptr, "SetT"}, | 711 | {182, nullptr, "SetT"}, |
| 686 | {183, nullptr, "GetPlatformRegion"}, | 712 | {183, nullptr, "GetPlatformRegion"}, |
| 687 | {184, nullptr, "SetPlatformRegion"}, | 713 | {184, nullptr, "SetPlatformRegion"}, |
| 688 | {185, nullptr, "GetHomeMenuSchemeModel"}, | 714 | {185, &SET_SYS::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, |
| 689 | {186, nullptr, "GetMemoryUsageRateFlag"}, | 715 | {186, nullptr, "GetMemoryUsageRateFlag"}, |
| 690 | {187, nullptr, "GetTouchScreenMode"}, | 716 | {187, nullptr, "GetTouchScreenMode"}, |
| 691 | {188, nullptr, "SetTouchScreenMode"}, | 717 | {188, nullptr, "SetTouchScreenMode"}, |
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index c7dba2a9e..5f770fd32 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "common/uuid.h" | 6 | #include "common/uuid.h" |
| 7 | #include "core/hle/result.h" | ||
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | #include "core/hle/service/time/clock_types.h" | 9 | #include "core/hle/service/time/clock_types.h" |
| 9 | 10 | ||
| @@ -12,6 +13,29 @@ class System; | |||
| 12 | } | 13 | } |
| 13 | 14 | ||
| 14 | namespace Service::Set { | 15 | namespace Service::Set { |
| 16 | enum class LanguageCode : u64; | ||
| 17 | enum class GetFirmwareVersionType { | ||
| 18 | Version1, | ||
| 19 | Version2, | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct FirmwareVersionFormat { | ||
| 23 | u8 major; | ||
| 24 | u8 minor; | ||
| 25 | u8 micro; | ||
| 26 | INSERT_PADDING_BYTES(1); | ||
| 27 | u8 revision_major; | ||
| 28 | u8 revision_minor; | ||
| 29 | INSERT_PADDING_BYTES(2); | ||
| 30 | std::array<char, 0x20> platform; | ||
| 31 | std::array<u8, 0x40> version_hash; | ||
| 32 | std::array<char, 0x18> display_version; | ||
| 33 | std::array<char, 0x80> display_title; | ||
| 34 | }; | ||
| 35 | static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size"); | ||
| 36 | |||
| 37 | Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, | ||
| 38 | GetFirmwareVersionType type); | ||
| 15 | 39 | ||
| 16 | class SET_SYS final : public ServiceFramework<SET_SYS> { | 40 | class SET_SYS final : public ServiceFramework<SET_SYS> { |
| 17 | public: | 41 | public: |
| @@ -269,6 +293,16 @@ private: | |||
| 269 | }; | 293 | }; |
| 270 | static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); | 294 | static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); |
| 271 | 295 | ||
| 296 | /// This is nn::settings::system::HomeMenuScheme | ||
| 297 | struct HomeMenuScheme { | ||
| 298 | u32 main; | ||
| 299 | u32 back; | ||
| 300 | u32 sub; | ||
| 301 | u32 bezel; | ||
| 302 | u32 extra; | ||
| 303 | }; | ||
| 304 | static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size"); | ||
| 305 | |||
| 272 | void SetLanguageCode(HLERequestContext& ctx); | 306 | void SetLanguageCode(HLERequestContext& ctx); |
| 273 | void GetFirmwareVersion(HLERequestContext& ctx); | 307 | void GetFirmwareVersion(HLERequestContext& ctx); |
| 274 | void GetFirmwareVersion2(HLERequestContext& ctx); | 308 | void GetFirmwareVersion2(HLERequestContext& ctx); |
| @@ -305,6 +339,8 @@ private: | |||
| 305 | void GetKeyboardLayout(HLERequestContext& ctx); | 339 | void GetKeyboardLayout(HLERequestContext& ctx); |
| 306 | void GetChineseTraditionalInputMethod(HLERequestContext& ctx); | 340 | void GetChineseTraditionalInputMethod(HLERequestContext& ctx); |
| 307 | void GetFieldTestingFlag(HLERequestContext& ctx); | 341 | void GetFieldTestingFlag(HLERequestContext& ctx); |
| 342 | void GetHomeMenuScheme(HLERequestContext& ctx); | ||
| 343 | void GetHomeMenuSchemeModel(HLERequestContext& ctx); | ||
| 308 | 344 | ||
| 309 | AccountSettings account_settings{ | 345 | AccountSettings account_settings{ |
| 310 | .flags = {}, | 346 | .flags = {}, |
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 85849d5f3..dd652ca42 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -39,6 +39,18 @@ bool IsConnectionBased(Type type) { | |||
| 39 | } | 39 | } |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | template <typename T> | ||
| 43 | T GetValue(std::span<const u8> buffer) { | ||
| 44 | T t{}; | ||
| 45 | std::memcpy(&t, buffer.data(), std::min(sizeof(T), buffer.size())); | ||
| 46 | return t; | ||
| 47 | } | ||
| 48 | |||
| 49 | template <typename T> | ||
| 50 | void PutValue(std::span<u8> buffer, const T& t) { | ||
| 51 | std::memcpy(buffer.data(), &t, std::min(sizeof(T), buffer.size())); | ||
| 52 | } | ||
| 53 | |||
| 42 | } // Anonymous namespace | 54 | } // Anonymous namespace |
| 43 | 55 | ||
| 44 | void BSD::PollWork::Execute(BSD* bsd) { | 56 | void BSD::PollWork::Execute(BSD* bsd) { |
| @@ -316,22 +328,12 @@ void BSD::SetSockOpt(HLERequestContext& ctx) { | |||
| 316 | const s32 fd = rp.Pop<s32>(); | 328 | const s32 fd = rp.Pop<s32>(); |
| 317 | const u32 level = rp.Pop<u32>(); | 329 | const u32 level = rp.Pop<u32>(); |
| 318 | const OptName optname = static_cast<OptName>(rp.Pop<u32>()); | 330 | const OptName optname = static_cast<OptName>(rp.Pop<u32>()); |
| 319 | 331 | const auto optval = ctx.ReadBuffer(); | |
| 320 | const auto buffer = ctx.ReadBuffer(); | ||
| 321 | const u8* optval = buffer.empty() ? nullptr : buffer.data(); | ||
| 322 | size_t optlen = buffer.size(); | ||
| 323 | |||
| 324 | std::array<u64, 2> values; | ||
| 325 | if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) { | ||
| 326 | std::memcpy(values.data(), buffer.data(), sizeof(values)); | ||
| 327 | optlen = sizeof(values); | ||
| 328 | optval = reinterpret_cast<const u8*>(values.data()); | ||
| 329 | } | ||
| 330 | 332 | ||
| 331 | LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level, | 333 | LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level, |
| 332 | static_cast<u32>(optname), optlen); | 334 | static_cast<u32>(optname), optval.size()); |
| 333 | 335 | ||
| 334 | BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval)); | 336 | BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval)); |
| 335 | } | 337 | } |
| 336 | 338 | ||
| 337 | void BSD::Shutdown(HLERequestContext& ctx) { | 339 | void BSD::Shutdown(HLERequestContext& ctx) { |
| @@ -521,18 +523,19 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco | |||
| 521 | 523 | ||
| 522 | std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, | 524 | std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, |
| 523 | s32 nfds, s32 timeout) { | 525 | s32 nfds, s32 timeout) { |
| 524 | if (write_buffer.size() < nfds * sizeof(PollFD)) { | 526 | if (nfds <= 0) { |
| 525 | return {-1, Errno::INVAL}; | ||
| 526 | } | ||
| 527 | |||
| 528 | if (nfds == 0) { | ||
| 529 | // When no entries are provided, -1 is returned with errno zero | 527 | // When no entries are provided, -1 is returned with errno zero |
| 530 | return {-1, Errno::SUCCESS}; | 528 | return {-1, Errno::SUCCESS}; |
| 531 | } | 529 | } |
| 530 | if (read_buffer.size() < nfds * sizeof(PollFD)) { | ||
| 531 | return {-1, Errno::INVAL}; | ||
| 532 | } | ||
| 533 | if (write_buffer.size() < nfds * sizeof(PollFD)) { | ||
| 534 | return {-1, Errno::INVAL}; | ||
| 535 | } | ||
| 532 | 536 | ||
| 533 | const size_t length = std::min(read_buffer.size(), write_buffer.size()); | ||
| 534 | std::vector<PollFD> fds(nfds); | 537 | std::vector<PollFD> fds(nfds); |
| 535 | std::memcpy(fds.data(), read_buffer.data(), length); | 538 | std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD)); |
| 536 | 539 | ||
| 537 | if (timeout >= 0) { | 540 | if (timeout >= 0) { |
| 538 | const s64 seconds = timeout / 1000; | 541 | const s64 seconds = timeout / 1000; |
| @@ -580,7 +583,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con | |||
| 580 | for (size_t i = 0; i < num; ++i) { | 583 | for (size_t i = 0; i < num; ++i) { |
| 581 | fds[i].revents = Translate(host_pollfds[i].revents); | 584 | fds[i].revents = Translate(host_pollfds[i].revents); |
| 582 | } | 585 | } |
| 583 | std::memcpy(write_buffer.data(), fds.data(), length); | 586 | std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD)); |
| 584 | 587 | ||
| 585 | return Translate(result); | 588 | return Translate(result); |
| 586 | } | 589 | } |
| @@ -608,8 +611,7 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) { | |||
| 608 | new_descriptor.is_connection_based = descriptor.is_connection_based; | 611 | new_descriptor.is_connection_based = descriptor.is_connection_based; |
| 609 | 612 | ||
| 610 | const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); | 613 | const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); |
| 611 | const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size()); | 614 | PutValue(write_buffer, guest_addr_in); |
| 612 | std::memcpy(write_buffer.data(), &guest_addr_in, length); | ||
| 613 | 615 | ||
| 614 | return {new_fd, Errno::SUCCESS}; | 616 | return {new_fd, Errno::SUCCESS}; |
| 615 | } | 617 | } |
| @@ -619,8 +621,7 @@ Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) { | |||
| 619 | return Errno::BADF; | 621 | return Errno::BADF; |
| 620 | } | 622 | } |
| 621 | ASSERT(addr.size() == sizeof(SockAddrIn)); | 623 | ASSERT(addr.size() == sizeof(SockAddrIn)); |
| 622 | SockAddrIn addr_in; | 624 | auto addr_in = GetValue<SockAddrIn>(addr); |
| 623 | std::memcpy(&addr_in, addr.data(), sizeof(addr_in)); | ||
| 624 | 625 | ||
| 625 | return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); | 626 | return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); |
| 626 | } | 627 | } |
| @@ -631,8 +632,7 @@ Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) { | |||
| 631 | } | 632 | } |
| 632 | 633 | ||
| 633 | UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); | 634 | UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); |
| 634 | SockAddrIn addr_in; | 635 | auto addr_in = GetValue<SockAddrIn>(addr); |
| 635 | std::memcpy(&addr_in, addr.data(), sizeof(addr_in)); | ||
| 636 | 636 | ||
| 637 | return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); | 637 | return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); |
| 638 | } | 638 | } |
| @@ -650,7 +650,7 @@ Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) { | |||
| 650 | 650 | ||
| 651 | ASSERT(write_buffer.size() >= sizeof(guest_addrin)); | 651 | ASSERT(write_buffer.size() >= sizeof(guest_addrin)); |
| 652 | write_buffer.resize(sizeof(guest_addrin)); | 652 | write_buffer.resize(sizeof(guest_addrin)); |
| 653 | std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); | 653 | PutValue(write_buffer, guest_addrin); |
| 654 | return Translate(bsd_errno); | 654 | return Translate(bsd_errno); |
| 655 | } | 655 | } |
| 656 | 656 | ||
| @@ -667,7 +667,7 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) { | |||
| 667 | 667 | ||
| 668 | ASSERT(write_buffer.size() >= sizeof(guest_addrin)); | 668 | ASSERT(write_buffer.size() >= sizeof(guest_addrin)); |
| 669 | write_buffer.resize(sizeof(guest_addrin)); | 669 | write_buffer.resize(sizeof(guest_addrin)); |
| 670 | std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); | 670 | PutValue(write_buffer, guest_addrin); |
| 671 | return Translate(bsd_errno); | 671 | return Translate(bsd_errno); |
| 672 | } | 672 | } |
| 673 | 673 | ||
| @@ -725,7 +725,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o | |||
| 725 | optval.size() == sizeof(Errno), { return Errno::INVAL; }, | 725 | optval.size() == sizeof(Errno), { return Errno::INVAL; }, |
| 726 | "Incorrect getsockopt option size"); | 726 | "Incorrect getsockopt option size"); |
| 727 | optval.resize(sizeof(Errno)); | 727 | optval.resize(sizeof(Errno)); |
| 728 | memcpy(optval.data(), &translated_pending_err, sizeof(Errno)); | 728 | PutValue(optval, translated_pending_err); |
| 729 | } | 729 | } |
| 730 | return Translate(getsockopt_err); | 730 | return Translate(getsockopt_err); |
| 731 | } | 731 | } |
| @@ -735,7 +735,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o | |||
| 735 | } | 735 | } |
| 736 | } | 736 | } |
| 737 | 737 | ||
| 738 | Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { | 738 | Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) { |
| 739 | if (!IsFileDescriptorValid(fd)) { | 739 | if (!IsFileDescriptorValid(fd)) { |
| 740 | return Errno::BADF; | 740 | return Errno::BADF; |
| 741 | } | 741 | } |
| @@ -748,17 +748,15 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con | |||
| 748 | Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); | 748 | Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); |
| 749 | 749 | ||
| 750 | if (optname == OptName::LINGER) { | 750 | if (optname == OptName::LINGER) { |
| 751 | ASSERT(optlen == sizeof(Linger)); | 751 | ASSERT(optval.size() == sizeof(Linger)); |
| 752 | Linger linger; | 752 | auto linger = GetValue<Linger>(optval); |
| 753 | std::memcpy(&linger, optval, sizeof(linger)); | ||
| 754 | ASSERT(linger.onoff == 0 || linger.onoff == 1); | 753 | ASSERT(linger.onoff == 0 || linger.onoff == 1); |
| 755 | 754 | ||
| 756 | return Translate(socket->SetLinger(linger.onoff != 0, linger.linger)); | 755 | return Translate(socket->SetLinger(linger.onoff != 0, linger.linger)); |
| 757 | } | 756 | } |
| 758 | 757 | ||
| 759 | ASSERT(optlen == sizeof(u32)); | 758 | ASSERT(optval.size() == sizeof(u32)); |
| 760 | u32 value; | 759 | auto value = GetValue<u32>(optval); |
| 761 | std::memcpy(&value, optval, sizeof(value)); | ||
| 762 | 760 | ||
| 763 | switch (optname) { | 761 | switch (optname) { |
| 764 | case OptName::REUSEADDR: | 762 | case OptName::REUSEADDR: |
| @@ -862,7 +860,7 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess | |||
| 862 | } else { | 860 | } else { |
| 863 | ASSERT(addr.size() == sizeof(SockAddrIn)); | 861 | ASSERT(addr.size() == sizeof(SockAddrIn)); |
| 864 | const SockAddrIn result = Translate(addr_in); | 862 | const SockAddrIn result = Translate(addr_in); |
| 865 | std::memcpy(addr.data(), &result, sizeof(result)); | 863 | PutValue(addr, result); |
| 866 | } | 864 | } |
| 867 | } | 865 | } |
| 868 | 866 | ||
| @@ -886,8 +884,7 @@ std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> mes | |||
| 886 | Network::SockAddrIn* p_addr_in = nullptr; | 884 | Network::SockAddrIn* p_addr_in = nullptr; |
| 887 | if (!addr.empty()) { | 885 | if (!addr.empty()) { |
| 888 | ASSERT(addr.size() == sizeof(SockAddrIn)); | 886 | ASSERT(addr.size() == sizeof(SockAddrIn)); |
| 889 | SockAddrIn guest_addr_in; | 887 | auto guest_addr_in = GetValue<SockAddrIn>(addr); |
| 890 | std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in)); | ||
| 891 | addr_in = Translate(guest_addr_in); | 888 | addr_in = Translate(guest_addr_in); |
| 892 | p_addr_in = &addr_in; | 889 | p_addr_in = &addr_in; |
| 893 | } | 890 | } |
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 161f22b9b..4f69d382c 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h | |||
| @@ -163,7 +163,7 @@ private: | |||
| 163 | Errno ListenImpl(s32 fd, s32 backlog); | 163 | Errno ListenImpl(s32 fd, s32 backlog); |
| 164 | std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); | 164 | std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); |
| 165 | Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval); | 165 | Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval); |
| 166 | Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); | 166 | Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval); |
| 167 | Errno ShutdownImpl(s32 fd, s32 how); | 167 | Errno ShutdownImpl(s32 fd, s32 how); |
| 168 | std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); | 168 | std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); |
| 169 | std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, | 169 | std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, |
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h index 9fc01ea90..7149fffeb 100644 --- a/src/core/hle/service/time/clock_types.h +++ b/src/core/hle/service/time/clock_types.h | |||
| @@ -11,6 +11,11 @@ | |||
| 11 | #include "core/hle/service/time/errors.h" | 11 | #include "core/hle/service/time/errors.h" |
| 12 | #include "core/hle/service/time/time_zone_types.h" | 12 | #include "core/hle/service/time/time_zone_types.h" |
| 13 | 13 | ||
| 14 | // Defined by WinBase.h on Windows | ||
| 15 | #ifdef GetCurrentTime | ||
| 16 | #undef GetCurrentTime | ||
| 17 | #endif | ||
| 18 | |||
| 14 | namespace Service::Time::Clock { | 19 | namespace Service::Time::Clock { |
| 15 | 20 | ||
| 16 | enum class TimeType : u8 { | 21 | enum class TimeType : u8 { |
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index f0b5eff8a..d30f49877 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp | |||
| @@ -35,7 +35,7 @@ static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_cont | |||
| 35 | return { | 35 | return { |
| 36 | buffer_queue_core, | 36 | buffer_queue_core, |
| 37 | std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), | 37 | std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), |
| 38 | std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)}; | 38 | std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)}; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | Display::Display(u64 id, std::string name_, | 41 | Display::Display(u64 id, std::string name_, |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index fa5273402..a3431772a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -1,8 +1,10 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2015 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2015 Citra Emulator Project |
| 2 | // SPDX-FileCopyrightText: 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 4 | ||
| 4 | #include <algorithm> | 5 | #include <algorithm> |
| 5 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include <mutex> | ||
| 6 | #include <span> | 8 | #include <span> |
| 7 | 9 | ||
| 8 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| @@ -10,6 +12,7 @@ | |||
| 10 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 11 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 12 | #include "common/page_table.h" | 14 | #include "common/page_table.h" |
| 15 | #include "common/scope_exit.h" | ||
| 13 | #include "common/settings.h" | 16 | #include "common/settings.h" |
| 14 | #include "common/swap.h" | 17 | #include "common/swap.h" |
| 15 | #include "core/core.h" | 18 | #include "core/core.h" |
| @@ -41,7 +44,7 @@ struct Memory::Impl { | |||
| 41 | explicit Impl(Core::System& system_) : system{system_} {} | 44 | explicit Impl(Core::System& system_) : system{system_} {} |
| 42 | 45 | ||
| 43 | void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) { | 46 | void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) { |
| 44 | current_page_table = &process.GetPageTable().PageTableImpl(); | 47 | current_page_table = &process.GetPageTable().GetImpl(); |
| 45 | current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer(); | 48 | current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer(); |
| 46 | 49 | ||
| 47 | const std::size_t address_space_width = process.GetPageTable().GetAddressSpaceWidth(); | 50 | const std::size_t address_space_width = process.GetPageTable().GetAddressSpaceWidth(); |
| @@ -195,7 +198,7 @@ struct Memory::Impl { | |||
| 195 | 198 | ||
| 196 | bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped, | 199 | bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped, |
| 197 | auto on_memory, auto on_rasterizer, auto increment) { | 200 | auto on_memory, auto on_rasterizer, auto increment) { |
| 198 | const auto& page_table = system.ApplicationProcess()->GetPageTable().PageTableImpl(); | 201 | const auto& page_table = system.ApplicationProcess()->GetPageTable().GetImpl(); |
| 199 | std::size_t remaining_size = size; | 202 | std::size_t remaining_size = size; |
| 200 | std::size_t page_index = addr >> YUZU_PAGEBITS; | 203 | std::size_t page_index = addr >> YUZU_PAGEBITS; |
| 201 | std::size_t page_offset = addr & YUZU_PAGEMASK; | 204 | std::size_t page_offset = addr & YUZU_PAGEMASK; |
| @@ -318,7 +321,7 @@ struct Memory::Impl { | |||
| 318 | [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, | 321 | [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, |
| 319 | u8* const host_ptr) { | 322 | u8* const host_ptr) { |
| 320 | if constexpr (!UNSAFE) { | 323 | if constexpr (!UNSAFE) { |
| 321 | system.GPU().InvalidateRegion(GetInteger(current_vaddr), copy_amount); | 324 | HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount); |
| 322 | } | 325 | } |
| 323 | std::memcpy(host_ptr, src_buffer, copy_amount); | 326 | std::memcpy(host_ptr, src_buffer, copy_amount); |
| 324 | }, | 327 | }, |
| @@ -351,7 +354,7 @@ struct Memory::Impl { | |||
| 351 | }, | 354 | }, |
| 352 | [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, | 355 | [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, |
| 353 | u8* const host_ptr) { | 356 | u8* const host_ptr) { |
| 354 | system.GPU().InvalidateRegion(GetInteger(current_vaddr), copy_amount); | 357 | HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount); |
| 355 | std::memset(host_ptr, 0, copy_amount); | 358 | std::memset(host_ptr, 0, copy_amount); |
| 356 | }, | 359 | }, |
| 357 | [](const std::size_t copy_amount) {}); | 360 | [](const std::size_t copy_amount) {}); |
| @@ -420,7 +423,7 @@ struct Memory::Impl { | |||
| 420 | const std::size_t block_size) { | 423 | const std::size_t block_size) { |
| 421 | // dc cvac: Store to point of coherency | 424 | // dc cvac: Store to point of coherency |
| 422 | // CPU flush -> GPU invalidate | 425 | // CPU flush -> GPU invalidate |
| 423 | system.GPU().InvalidateRegion(GetInteger(current_vaddr), block_size); | 426 | HandleRasterizerWrite(GetInteger(current_vaddr), block_size); |
| 424 | }; | 427 | }; |
| 425 | return PerformCacheOperation(dest_addr, size, on_rasterizer); | 428 | return PerformCacheOperation(dest_addr, size, on_rasterizer); |
| 426 | } | 429 | } |
| @@ -430,7 +433,7 @@ struct Memory::Impl { | |||
| 430 | const std::size_t block_size) { | 433 | const std::size_t block_size) { |
| 431 | // dc civac: Store to point of coherency, and invalidate from cache | 434 | // dc civac: Store to point of coherency, and invalidate from cache |
| 432 | // CPU flush -> GPU invalidate | 435 | // CPU flush -> GPU invalidate |
| 433 | system.GPU().InvalidateRegion(GetInteger(current_vaddr), block_size); | 436 | HandleRasterizerWrite(GetInteger(current_vaddr), block_size); |
| 434 | }; | 437 | }; |
| 435 | return PerformCacheOperation(dest_addr, size, on_rasterizer); | 438 | return PerformCacheOperation(dest_addr, size, on_rasterizer); |
| 436 | } | 439 | } |
| @@ -767,7 +770,18 @@ struct Memory::Impl { | |||
| 767 | } | 770 | } |
| 768 | 771 | ||
| 769 | void HandleRasterizerWrite(VAddr address, size_t size) { | 772 | void HandleRasterizerWrite(VAddr address, size_t size) { |
| 770 | const size_t core = system.GetCurrentHostThreadID(); | 773 | constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1; |
| 774 | const size_t core = std::min(system.GetCurrentHostThreadID(), | ||
| 775 | sys_core); // any other calls threads go to syscore. | ||
| 776 | // Guard on sys_core; | ||
| 777 | if (core == sys_core) [[unlikely]] { | ||
| 778 | sys_core_guard.lock(); | ||
| 779 | } | ||
| 780 | SCOPE_EXIT({ | ||
| 781 | if (core == sys_core) [[unlikely]] { | ||
| 782 | sys_core_guard.unlock(); | ||
| 783 | } | ||
| 784 | }); | ||
| 771 | auto& current_area = rasterizer_write_areas[core]; | 785 | auto& current_area = rasterizer_write_areas[core]; |
| 772 | VAddr subaddress = address >> YUZU_PAGEBITS; | 786 | VAddr subaddress = address >> YUZU_PAGEBITS; |
| 773 | bool do_collection = current_area.last_address == subaddress; | 787 | bool do_collection = current_area.last_address == subaddress; |
| @@ -799,6 +813,7 @@ struct Memory::Impl { | |||
| 799 | rasterizer_read_areas{}; | 813 | rasterizer_read_areas{}; |
| 800 | std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; | 814 | std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; |
| 801 | std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; | 815 | std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; |
| 816 | std::mutex sys_core_guard; | ||
| 802 | }; | 817 | }; |
| 803 | 818 | ||
| 804 | Memory::Memory(Core::System& system_) : system{system_} { | 819 | Memory::Memory(Core::System& system_) : system{system_} { |
| @@ -826,7 +841,7 @@ void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress b | |||
| 826 | 841 | ||
| 827 | bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { | 842 | bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { |
| 828 | const Kernel::KProcess& process = *system.ApplicationProcess(); | 843 | const Kernel::KProcess& process = *system.ApplicationProcess(); |
| 829 | const auto& page_table = process.GetPageTable().PageTableImpl(); | 844 | const auto& page_table = process.GetPageTable().GetImpl(); |
| 830 | const size_t page = vaddr >> YUZU_PAGEBITS; | 845 | const size_t page = vaddr >> YUZU_PAGEBITS; |
| 831 | if (page >= page_table.pointers.size()) { | 846 | if (page >= page_table.pointers.size()) { |
| 832 | return false; | 847 | return false; |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 53a89cc8f..db30ba598 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -10,7 +10,8 @@ | |||
| 10 | #include "core/hle/kernel/k_page_table.h" | 10 | #include "core/hle/kernel/k_page_table.h" |
| 11 | #include "core/hle/kernel/k_process.h" | 11 | #include "core/hle/kernel/k_process.h" |
| 12 | #include "core/hle/service/hid/controllers/npad.h" | 12 | #include "core/hle/service/hid/controllers/npad.h" |
| 13 | #include "core/hle/service/hid/hid.h" | 13 | #include "core/hle/service/hid/hid_server.h" |
| 14 | #include "core/hle/service/hid/resource_manager.h" | ||
| 14 | #include "core/hle/service/sm/sm.h" | 15 | #include "core/hle/service/sm/sm.h" |
| 15 | #include "core/memory.h" | 16 | #include "core/memory.h" |
| 16 | #include "core/memory/cheat_engine.h" | 17 | #include "core/memory/cheat_engine.h" |
| @@ -54,23 +55,20 @@ void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) | |||
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | u64 StandardVmCallbacks::HidKeysDown() { | 57 | u64 StandardVmCallbacks::HidKeysDown() { |
| 57 | const auto hid = system.ServiceManager().GetService<Service::HID::Hid>("hid"); | 58 | const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid"); |
| 58 | if (hid == nullptr) { | 59 | if (hid == nullptr) { |
| 59 | LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!"); | 60 | LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!"); |
| 60 | return 0; | 61 | return 0; |
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | const auto applet_resource = hid->GetAppletResource(); | 64 | const auto applet_resource = hid->GetResourceManager(); |
| 64 | if (applet_resource == nullptr) { | 65 | if (applet_resource == nullptr) { |
| 65 | LOG_WARNING(CheatEngine, | 66 | LOG_WARNING(CheatEngine, |
| 66 | "Attempted to read input state, but applet resource is not initialized!"); | 67 | "Attempted to read input state, but applet resource is not initialized!"); |
| 67 | return 0; | 68 | return 0; |
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | const auto press_state = | 71 | const auto press_state = applet_resource->GetNpad()->GetAndResetPressState(); |
| 71 | applet_resource | ||
| 72 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad) | ||
| 73 | .GetAndResetPressState(); | ||
| 74 | return static_cast<u64>(press_state & HID::NpadButton::All); | 72 | return static_cast<u64>(press_state & HID::NpadButton::All); |
| 75 | } | 73 | } |
| 76 | 74 | ||
diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt new file mode 100644 index 000000000..22e9337c4 --- /dev/null +++ b/src/frontend_common/CMakeLists.txt | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | add_library(frontend_common STATIC | ||
| 5 | config.cpp | ||
| 6 | config.h | ||
| 7 | ) | ||
| 8 | |||
| 9 | create_target_directory_groups(frontend_common) | ||
| 10 | target_link_libraries(frontend_common PUBLIC core SimpleIni::SimpleIni PRIVATE common Boost::headers) | ||
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp new file mode 100644 index 000000000..7474cb0f9 --- /dev/null +++ b/src/frontend_common/config.cpp | |||
| @@ -0,0 +1,1008 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <array> | ||
| 6 | #include "common/fs/fs.h" | ||
| 7 | #include "common/fs/path_util.h" | ||
| 8 | #include "common/settings.h" | ||
| 9 | #include "common/settings_common.h" | ||
| 10 | #include "common/settings_enums.h" | ||
| 11 | #include "config.h" | ||
| 12 | #include "core/core.h" | ||
| 13 | #include "core/hle/service/acc/profile_manager.h" | ||
| 14 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 15 | #include "network/network.h" | ||
| 16 | |||
| 17 | #include <boost/algorithm/string/replace.hpp> | ||
| 18 | |||
| 19 | #include "common/string_util.h" | ||
| 20 | |||
| 21 | namespace FS = Common::FS; | ||
| 22 | |||
| 23 | Config::Config(const ConfigType config_type) | ||
| 24 | : type(config_type), global{config_type == ConfigType::GlobalConfig} {} | ||
| 25 | |||
| 26 | void Config::Initialize(const std::string& config_name) { | ||
| 27 | const std::filesystem::path fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); | ||
| 28 | const auto config_file = fmt::format("{}.ini", config_name); | ||
| 29 | |||
| 30 | switch (type) { | ||
| 31 | case ConfigType::GlobalConfig: | ||
| 32 | config_loc = FS::PathToUTF8String(fs_config_loc / config_file); | ||
| 33 | void(FS::CreateParentDir(config_loc)); | ||
| 34 | SetUpIni(); | ||
| 35 | Reload(); | ||
| 36 | break; | ||
| 37 | case ConfigType::PerGameConfig: | ||
| 38 | config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); | ||
| 39 | void(FS::CreateParentDir(config_loc)); | ||
| 40 | SetUpIni(); | ||
| 41 | Reload(); | ||
| 42 | break; | ||
| 43 | case ConfigType::InputProfile: | ||
| 44 | config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); | ||
| 45 | void(FS::CreateParentDir(config_loc)); | ||
| 46 | SetUpIni(); | ||
| 47 | break; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | void Config::Initialize(const std::optional<std::string> config_path) { | ||
| 52 | const std::filesystem::path default_sdl_config_path = | ||
| 53 | FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; | ||
| 54 | config_loc = config_path.value_or(FS::PathToUTF8String(default_sdl_config_path)); | ||
| 55 | void(FS::CreateParentDir(config_loc)); | ||
| 56 | SetUpIni(); | ||
| 57 | Reload(); | ||
| 58 | } | ||
| 59 | |||
| 60 | void Config::WriteToIni() const { | ||
| 61 | FILE* fp = nullptr; | ||
| 62 | #ifdef _WIN32 | ||
| 63 | fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb"); | ||
| 64 | #else | ||
| 65 | fp = fopen(config_loc.c_str(), "wb"); | ||
| 66 | #endif | ||
| 67 | |||
| 68 | if (fp == nullptr) { | ||
| 69 | LOG_ERROR(Frontend, "Config file could not be saved!"); | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | CSimpleIniA::FileWriter writer(fp); | ||
| 74 | const SI_Error rc = config->Save(writer, false); | ||
| 75 | if (rc < 0) { | ||
| 76 | LOG_ERROR(Frontend, "Config file could not be saved!"); | ||
| 77 | } | ||
| 78 | fclose(fp); | ||
| 79 | } | ||
| 80 | |||
| 81 | void Config::SetUpIni() { | ||
| 82 | config = std::make_unique<CSimpleIniA>(); | ||
| 83 | config->SetUnicode(true); | ||
| 84 | config->SetSpaces(false); | ||
| 85 | |||
| 86 | FILE* fp = nullptr; | ||
| 87 | #ifdef _WIN32 | ||
| 88 | _wfopen_s(&fp, Common::UTF8ToUTF16W(config_loc).data(), L"rb, ccs=UTF-8"); | ||
| 89 | if (fp == nullptr) { | ||
| 90 | fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb, ccs=UTF-8"); | ||
| 91 | } | ||
| 92 | #else | ||
| 93 | fp = fopen(config_loc.c_str(), "rb"); | ||
| 94 | if (fp == nullptr) { | ||
| 95 | fp = fopen(config_loc.c_str(), "wb"); | ||
| 96 | } | ||
| 97 | #endif | ||
| 98 | |||
| 99 | if (fp == nullptr) { | ||
| 100 | LOG_ERROR(Frontend, "Config file could not be loaded!"); | ||
| 101 | return; | ||
| 102 | } | ||
| 103 | |||
| 104 | if (SI_Error rc = config->LoadFile(fp); rc < 0) { | ||
| 105 | LOG_ERROR(Frontend, "Config file could not be loaded!"); | ||
| 106 | } | ||
| 107 | fclose(fp); | ||
| 108 | } | ||
| 109 | |||
| 110 | bool Config::IsCustomConfig() const { | ||
| 111 | return type == ConfigType::PerGameConfig; | ||
| 112 | } | ||
| 113 | |||
| 114 | void Config::ReadPlayerValues(const std::size_t player_index) { | ||
| 115 | std::string player_prefix; | ||
| 116 | if (type != ConfigType::InputProfile) { | ||
| 117 | player_prefix.append("player_").append(ToString(player_index)).append("_"); | ||
| 118 | } | ||
| 119 | |||
| 120 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 121 | if (IsCustomConfig()) { | ||
| 122 | const auto profile_name = | ||
| 123 | ReadStringSetting(std::string(player_prefix).append("profile_name")); | ||
| 124 | if (profile_name.empty()) { | ||
| 125 | // Use the global input config | ||
| 126 | player = Settings::values.players.GetValue(true)[player_index]; | ||
| 127 | return; | ||
| 128 | } | ||
| 129 | player.profile_name = profile_name; | ||
| 130 | } | ||
| 131 | |||
| 132 | if (player_prefix.empty() && Settings::IsConfiguringGlobal()) { | ||
| 133 | const auto controller = static_cast<Settings::ControllerType>( | ||
| 134 | ReadIntegerSetting(std::string(player_prefix).append("type"), | ||
| 135 | static_cast<u8>(Settings::ControllerType::ProController))); | ||
| 136 | |||
| 137 | if (controller == Settings::ControllerType::LeftJoycon || | ||
| 138 | controller == Settings::ControllerType::RightJoycon) { | ||
| 139 | player.controller_type = controller; | ||
| 140 | } | ||
| 141 | } else { | ||
| 142 | std::string connected_key = player_prefix; | ||
| 143 | player.connected = ReadBooleanSetting(connected_key.append("connected"), | ||
| 144 | std::make_optional(player_index == 0)); | ||
| 145 | |||
| 146 | player.controller_type = static_cast<Settings::ControllerType>( | ||
| 147 | ReadIntegerSetting(std::string(player_prefix).append("type"), | ||
| 148 | static_cast<u8>(Settings::ControllerType::ProController))); | ||
| 149 | |||
| 150 | player.vibration_enabled = ReadBooleanSetting( | ||
| 151 | std::string(player_prefix).append("vibration_enabled"), std::make_optional(true)); | ||
| 152 | |||
| 153 | player.vibration_strength = static_cast<int>( | ||
| 154 | ReadIntegerSetting(std::string(player_prefix).append("vibration_strength"), 100)); | ||
| 155 | |||
| 156 | player.body_color_left = static_cast<u32>(ReadIntegerSetting( | ||
| 157 | std::string(player_prefix).append("body_color_left"), Settings::JOYCON_BODY_NEON_BLUE)); | ||
| 158 | player.body_color_right = static_cast<u32>(ReadIntegerSetting( | ||
| 159 | std::string(player_prefix).append("body_color_right"), Settings::JOYCON_BODY_NEON_RED)); | ||
| 160 | player.button_color_left = static_cast<u32>( | ||
| 161 | ReadIntegerSetting(std::string(player_prefix).append("button_color_left"), | ||
| 162 | Settings::JOYCON_BUTTONS_NEON_BLUE)); | ||
| 163 | player.button_color_right = static_cast<u32>( | ||
| 164 | ReadIntegerSetting(std::string(player_prefix).append("button_color_right"), | ||
| 165 | Settings::JOYCON_BUTTONS_NEON_RED)); | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | void Config::ReadTouchscreenValues() { | ||
| 170 | Settings::values.touchscreen.enabled = | ||
| 171 | ReadBooleanSetting(std::string("touchscreen_enabled"), std::make_optional(true)); | ||
| 172 | Settings::values.touchscreen.rotation_angle = | ||
| 173 | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0)); | ||
| 174 | Settings::values.touchscreen.diameter_x = | ||
| 175 | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15)); | ||
| 176 | Settings::values.touchscreen.diameter_y = | ||
| 177 | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15)); | ||
| 178 | } | ||
| 179 | |||
| 180 | void Config::ReadAudioValues() { | ||
| 181 | BeginGroup(Settings::TranslateCategory(Settings::Category::Audio)); | ||
| 182 | |||
| 183 | ReadCategory(Settings::Category::Audio); | ||
| 184 | ReadCategory(Settings::Category::UiAudio); | ||
| 185 | |||
| 186 | EndGroup(); | ||
| 187 | } | ||
| 188 | |||
| 189 | void Config::ReadControlValues() { | ||
| 190 | BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); | ||
| 191 | |||
| 192 | ReadCategory(Settings::Category::Controls); | ||
| 193 | |||
| 194 | Settings::values.players.SetGlobal(!IsCustomConfig()); | ||
| 195 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 196 | ReadPlayerValues(p); | ||
| 197 | } | ||
| 198 | |||
| 199 | // Disable docked mode if handheld is selected | ||
| 200 | const auto controller_type = Settings::values.players.GetValue()[0].controller_type; | ||
| 201 | if (controller_type == Settings::ControllerType::Handheld) { | ||
| 202 | Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); | ||
| 203 | Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld); | ||
| 204 | } | ||
| 205 | |||
| 206 | if (IsCustomConfig()) { | ||
| 207 | EndGroup(); | ||
| 208 | return; | ||
| 209 | } | ||
| 210 | ReadTouchscreenValues(); | ||
| 211 | ReadMotionTouchValues(); | ||
| 212 | |||
| 213 | EndGroup(); | ||
| 214 | } | ||
| 215 | |||
| 216 | void Config::ReadMotionTouchValues() { | ||
| 217 | int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps")); | ||
| 218 | |||
| 219 | if (num_touch_from_button_maps > 0) { | ||
| 220 | for (int i = 0; i < num_touch_from_button_maps; ++i) { | ||
| 221 | SetArrayIndex(i); | ||
| 222 | |||
| 223 | Settings::TouchFromButtonMap map; | ||
| 224 | map.name = ReadStringSetting(std::string("name"), std::string("default")); | ||
| 225 | |||
| 226 | const int num_touch_maps = BeginArray(std::string("entries")); | ||
| 227 | map.buttons.reserve(num_touch_maps); | ||
| 228 | for (int j = 0; j < num_touch_maps; j++) { | ||
| 229 | SetArrayIndex(j); | ||
| 230 | std::string touch_mapping = ReadStringSetting(std::string("bind")); | ||
| 231 | map.buttons.emplace_back(std::move(touch_mapping)); | ||
| 232 | } | ||
| 233 | EndArray(); // entries | ||
| 234 | Settings::values.touch_from_button_maps.emplace_back(std::move(map)); | ||
| 235 | } | ||
| 236 | } else { | ||
| 237 | Settings::values.touch_from_button_maps.emplace_back( | ||
| 238 | Settings::TouchFromButtonMap{"default", {}}); | ||
| 239 | num_touch_from_button_maps = 1; | ||
| 240 | } | ||
| 241 | EndArray(); // touch_from_button_maps | ||
| 242 | |||
| 243 | Settings::values.touch_from_button_map_index = std::clamp( | ||
| 244 | Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); | ||
| 245 | } | ||
| 246 | |||
| 247 | void Config::ReadCoreValues() { | ||
| 248 | BeginGroup(Settings::TranslateCategory(Settings::Category::Core)); | ||
| 249 | |||
| 250 | ReadCategory(Settings::Category::Core); | ||
| 251 | |||
| 252 | EndGroup(); | ||
| 253 | } | ||
| 254 | |||
| 255 | void Config::ReadDataStorageValues() { | ||
| 256 | BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage)); | ||
| 257 | |||
| 258 | FS::SetYuzuPath(FS::YuzuPath::NANDDir, ReadStringSetting(std::string("nand_directory"))); | ||
| 259 | FS::SetYuzuPath(FS::YuzuPath::SDMCDir, ReadStringSetting(std::string("sdmc_directory"))); | ||
| 260 | FS::SetYuzuPath(FS::YuzuPath::LoadDir, ReadStringSetting(std::string("load_directory"))); | ||
| 261 | FS::SetYuzuPath(FS::YuzuPath::DumpDir, ReadStringSetting(std::string("dump_directory"))); | ||
| 262 | FS::SetYuzuPath(FS::YuzuPath::TASDir, ReadStringSetting(std::string("tas_directory"))); | ||
| 263 | |||
| 264 | ReadCategory(Settings::Category::DataStorage); | ||
| 265 | |||
| 266 | EndGroup(); | ||
| 267 | } | ||
| 268 | |||
| 269 | void Config::ReadDebuggingValues() { | ||
| 270 | BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); | ||
| 271 | |||
| 272 | // Intentionally not using the QT default setting as this is intended to be changed in the ini | ||
| 273 | Settings::values.record_frame_times = | ||
| 274 | ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false)); | ||
| 275 | |||
| 276 | ReadCategory(Settings::Category::Debugging); | ||
| 277 | ReadCategory(Settings::Category::DebuggingGraphics); | ||
| 278 | |||
| 279 | EndGroup(); | ||
| 280 | } | ||
| 281 | |||
| 282 | void Config::ReadServiceValues() { | ||
| 283 | BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); | ||
| 284 | |||
| 285 | ReadCategory(Settings::Category::Services); | ||
| 286 | |||
| 287 | EndGroup(); | ||
| 288 | } | ||
| 289 | |||
| 290 | void Config::ReadDisabledAddOnValues() { | ||
| 291 | // Custom config section | ||
| 292 | BeginGroup(std::string("DisabledAddOns")); | ||
| 293 | |||
| 294 | const int size = BeginArray(std::string("")); | ||
| 295 | for (int i = 0; i < size; ++i) { | ||
| 296 | SetArrayIndex(i); | ||
| 297 | const auto title_id = ReadUnsignedIntegerSetting(std::string("title_id"), 0); | ||
| 298 | std::vector<std::string> out; | ||
| 299 | const int d_size = BeginArray("disabled"); | ||
| 300 | for (int j = 0; j < d_size; ++j) { | ||
| 301 | SetArrayIndex(j); | ||
| 302 | out.push_back(ReadStringSetting(std::string("d"), std::string(""))); | ||
| 303 | } | ||
| 304 | EndArray(); // d | ||
| 305 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | ||
| 306 | } | ||
| 307 | EndArray(); // Base disabled addons array - Has no base key | ||
| 308 | |||
| 309 | EndGroup(); | ||
| 310 | } | ||
| 311 | |||
| 312 | void Config::ReadMiscellaneousValues() { | ||
| 313 | BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous)); | ||
| 314 | |||
| 315 | ReadCategory(Settings::Category::Miscellaneous); | ||
| 316 | |||
| 317 | EndGroup(); | ||
| 318 | } | ||
| 319 | |||
| 320 | void Config::ReadCpuValues() { | ||
| 321 | BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu)); | ||
| 322 | |||
| 323 | ReadCategory(Settings::Category::Cpu); | ||
| 324 | ReadCategory(Settings::Category::CpuDebug); | ||
| 325 | ReadCategory(Settings::Category::CpuUnsafe); | ||
| 326 | |||
| 327 | EndGroup(); | ||
| 328 | } | ||
| 329 | |||
| 330 | void Config::ReadRendererValues() { | ||
| 331 | BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer)); | ||
| 332 | |||
| 333 | ReadCategory(Settings::Category::Renderer); | ||
| 334 | ReadCategory(Settings::Category::RendererAdvanced); | ||
| 335 | ReadCategory(Settings::Category::RendererDebug); | ||
| 336 | |||
| 337 | EndGroup(); | ||
| 338 | } | ||
| 339 | |||
| 340 | void Config::ReadScreenshotValues() { | ||
| 341 | BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots)); | ||
| 342 | |||
| 343 | ReadCategory(Settings::Category::Screenshots); | ||
| 344 | FS::SetYuzuPath(FS::YuzuPath::ScreenshotsDir, | ||
| 345 | ReadStringSetting(std::string("screenshot_path"))); | ||
| 346 | |||
| 347 | EndGroup(); | ||
| 348 | } | ||
| 349 | |||
| 350 | void Config::ReadSystemValues() { | ||
| 351 | BeginGroup(Settings::TranslateCategory(Settings::Category::System)); | ||
| 352 | |||
| 353 | ReadCategory(Settings::Category::System); | ||
| 354 | ReadCategory(Settings::Category::SystemAudio); | ||
| 355 | |||
| 356 | EndGroup(); | ||
| 357 | } | ||
| 358 | |||
| 359 | void Config::ReadWebServiceValues() { | ||
| 360 | BeginGroup(Settings::TranslateCategory(Settings::Category::WebService)); | ||
| 361 | |||
| 362 | ReadCategory(Settings::Category::WebService); | ||
| 363 | |||
| 364 | EndGroup(); | ||
| 365 | } | ||
| 366 | |||
| 367 | void Config::ReadNetworkValues() { | ||
| 368 | BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); | ||
| 369 | |||
| 370 | ReadCategory(Settings::Category::Network); | ||
| 371 | |||
| 372 | EndGroup(); | ||
| 373 | } | ||
| 374 | |||
| 375 | void Config::ReadValues() { | ||
| 376 | if (global) { | ||
| 377 | ReadDataStorageValues(); | ||
| 378 | ReadDebuggingValues(); | ||
| 379 | ReadDisabledAddOnValues(); | ||
| 380 | ReadNetworkValues(); | ||
| 381 | ReadServiceValues(); | ||
| 382 | ReadWebServiceValues(); | ||
| 383 | ReadMiscellaneousValues(); | ||
| 384 | } | ||
| 385 | ReadControlValues(); | ||
| 386 | ReadCoreValues(); | ||
| 387 | ReadCpuValues(); | ||
| 388 | ReadRendererValues(); | ||
| 389 | ReadAudioValues(); | ||
| 390 | ReadSystemValues(); | ||
| 391 | } | ||
| 392 | |||
| 393 | void Config::SavePlayerValues(const std::size_t player_index) { | ||
| 394 | std::string player_prefix; | ||
| 395 | if (type != ConfigType::InputProfile) { | ||
| 396 | player_prefix = std::string("player_").append(ToString(player_index)).append("_"); | ||
| 397 | } | ||
| 398 | |||
| 399 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 400 | if (IsCustomConfig()) { | ||
| 401 | if (player.profile_name.empty()) { | ||
| 402 | // No custom profile selected | ||
| 403 | return; | ||
| 404 | } | ||
| 405 | WriteSetting(std::string(player_prefix).append("profile_name"), player.profile_name, | ||
| 406 | std::make_optional(std::string(""))); | ||
| 407 | } | ||
| 408 | |||
| 409 | WriteSetting(std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type), | ||
| 410 | std::make_optional(static_cast<u8>(Settings::ControllerType::ProController))); | ||
| 411 | |||
| 412 | if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) { | ||
| 413 | WriteSetting(std::string(player_prefix).append("connected"), player.connected, | ||
| 414 | std::make_optional(player_index == 0)); | ||
| 415 | WriteSetting(std::string(player_prefix).append("vibration_enabled"), | ||
| 416 | player.vibration_enabled, std::make_optional(true)); | ||
| 417 | WriteSetting(std::string(player_prefix).append("vibration_strength"), | ||
| 418 | player.vibration_strength, std::make_optional(100)); | ||
| 419 | WriteSetting(std::string(player_prefix).append("body_color_left"), player.body_color_left, | ||
| 420 | std::make_optional(Settings::JOYCON_BODY_NEON_BLUE)); | ||
| 421 | WriteSetting(std::string(player_prefix).append("body_color_right"), player.body_color_right, | ||
| 422 | std::make_optional(Settings::JOYCON_BODY_NEON_RED)); | ||
| 423 | WriteSetting(std::string(player_prefix).append("button_color_left"), | ||
| 424 | player.button_color_left, | ||
| 425 | std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE)); | ||
| 426 | WriteSetting(std::string(player_prefix).append("button_color_right"), | ||
| 427 | player.button_color_right, | ||
| 428 | std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED)); | ||
| 429 | } | ||
| 430 | } | ||
| 431 | |||
| 432 | void Config::SaveTouchscreenValues() { | ||
| 433 | const auto& touchscreen = Settings::values.touchscreen; | ||
| 434 | |||
| 435 | WriteSetting(std::string("touchscreen_enabled"), touchscreen.enabled, std::make_optional(true)); | ||
| 436 | |||
| 437 | WriteSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle, | ||
| 438 | std::make_optional(static_cast<u32>(0))); | ||
| 439 | WriteSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x, | ||
| 440 | std::make_optional(static_cast<u32>(15))); | ||
| 441 | WriteSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y, | ||
| 442 | std::make_optional(static_cast<u32>(15))); | ||
| 443 | } | ||
| 444 | |||
| 445 | void Config::SaveMotionTouchValues() { | ||
| 446 | BeginArray(std::string("touch_from_button_maps")); | ||
| 447 | for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { | ||
| 448 | SetArrayIndex(static_cast<int>(p)); | ||
| 449 | WriteSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name, | ||
| 450 | std::make_optional(std::string("default"))); | ||
| 451 | |||
| 452 | BeginArray(std::string("entries")); | ||
| 453 | for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); | ||
| 454 | ++q) { | ||
| 455 | SetArrayIndex(static_cast<int>(q)); | ||
| 456 | WriteSetting(std::string("bind"), | ||
| 457 | Settings::values.touch_from_button_maps[p].buttons[q]); | ||
| 458 | } | ||
| 459 | EndArray(); // entries | ||
| 460 | } | ||
| 461 | EndArray(); // touch_from_button_maps | ||
| 462 | } | ||
| 463 | |||
| 464 | void Config::SaveValues() { | ||
| 465 | if (global) { | ||
| 466 | SaveDataStorageValues(); | ||
| 467 | SaveDebuggingValues(); | ||
| 468 | SaveDisabledAddOnValues(); | ||
| 469 | SaveNetworkValues(); | ||
| 470 | SaveWebServiceValues(); | ||
| 471 | SaveMiscellaneousValues(); | ||
| 472 | } | ||
| 473 | SaveControlValues(); | ||
| 474 | SaveCoreValues(); | ||
| 475 | SaveCpuValues(); | ||
| 476 | SaveRendererValues(); | ||
| 477 | SaveAudioValues(); | ||
| 478 | SaveSystemValues(); | ||
| 479 | |||
| 480 | WriteToIni(); | ||
| 481 | } | ||
| 482 | |||
| 483 | void Config::SaveAudioValues() { | ||
| 484 | BeginGroup(Settings::TranslateCategory(Settings::Category::Audio)); | ||
| 485 | |||
| 486 | WriteCategory(Settings::Category::Audio); | ||
| 487 | WriteCategory(Settings::Category::UiAudio); | ||
| 488 | |||
| 489 | EndGroup(); | ||
| 490 | } | ||
| 491 | |||
| 492 | void Config::SaveControlValues() { | ||
| 493 | BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); | ||
| 494 | |||
| 495 | WriteCategory(Settings::Category::Controls); | ||
| 496 | |||
| 497 | Settings::values.players.SetGlobal(!IsCustomConfig()); | ||
| 498 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 499 | SavePlayerValues(p); | ||
| 500 | } | ||
| 501 | if (IsCustomConfig()) { | ||
| 502 | EndGroup(); | ||
| 503 | return; | ||
| 504 | } | ||
| 505 | SaveTouchscreenValues(); | ||
| 506 | SaveMotionTouchValues(); | ||
| 507 | |||
| 508 | EndGroup(); | ||
| 509 | } | ||
| 510 | |||
| 511 | void Config::SaveCoreValues() { | ||
| 512 | BeginGroup(Settings::TranslateCategory(Settings::Category::Core)); | ||
| 513 | |||
| 514 | WriteCategory(Settings::Category::Core); | ||
| 515 | |||
| 516 | EndGroup(); | ||
| 517 | } | ||
| 518 | |||
| 519 | void Config::SaveDataStorageValues() { | ||
| 520 | BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage)); | ||
| 521 | |||
| 522 | WriteSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir), | ||
| 523 | std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); | ||
| 524 | WriteSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir), | ||
| 525 | std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); | ||
| 526 | WriteSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir), | ||
| 527 | std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); | ||
| 528 | WriteSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir), | ||
| 529 | std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); | ||
| 530 | WriteSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir), | ||
| 531 | std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); | ||
| 532 | |||
| 533 | WriteCategory(Settings::Category::DataStorage); | ||
| 534 | |||
| 535 | EndGroup(); | ||
| 536 | } | ||
| 537 | |||
| 538 | void Config::SaveDebuggingValues() { | ||
| 539 | BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); | ||
| 540 | |||
| 541 | // Intentionally not using the QT default setting as this is intended to be changed in the ini | ||
| 542 | WriteSetting(std::string("record_frame_times"), Settings::values.record_frame_times); | ||
| 543 | |||
| 544 | WriteCategory(Settings::Category::Debugging); | ||
| 545 | WriteCategory(Settings::Category::DebuggingGraphics); | ||
| 546 | |||
| 547 | EndGroup(); | ||
| 548 | } | ||
| 549 | |||
| 550 | void Config::SaveNetworkValues() { | ||
| 551 | BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); | ||
| 552 | |||
| 553 | WriteCategory(Settings::Category::Network); | ||
| 554 | |||
| 555 | EndGroup(); | ||
| 556 | } | ||
| 557 | |||
| 558 | void Config::SaveDisabledAddOnValues() { | ||
| 559 | // Custom config section | ||
| 560 | BeginGroup(std::string("DisabledAddOns")); | ||
| 561 | |||
| 562 | int i = 0; | ||
| 563 | BeginArray(std::string("")); | ||
| 564 | for (const auto& elem : Settings::values.disabled_addons) { | ||
| 565 | SetArrayIndex(i); | ||
| 566 | WriteSetting(std::string("title_id"), elem.first, std::make_optional(static_cast<u64>(0))); | ||
| 567 | BeginArray(std::string("disabled")); | ||
| 568 | for (std::size_t j = 0; j < elem.second.size(); ++j) { | ||
| 569 | SetArrayIndex(static_cast<int>(j)); | ||
| 570 | WriteSetting(std::string("d"), elem.second[j], std::make_optional(std::string(""))); | ||
| 571 | } | ||
| 572 | EndArray(); // disabled | ||
| 573 | ++i; | ||
| 574 | } | ||
| 575 | EndArray(); // Base disabled addons array - Has no base key | ||
| 576 | |||
| 577 | EndGroup(); | ||
| 578 | } | ||
| 579 | |||
| 580 | void Config::SaveMiscellaneousValues() { | ||
| 581 | BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous)); | ||
| 582 | |||
| 583 | WriteCategory(Settings::Category::Miscellaneous); | ||
| 584 | |||
| 585 | EndGroup(); | ||
| 586 | } | ||
| 587 | |||
| 588 | void Config::SaveCpuValues() { | ||
| 589 | BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu)); | ||
| 590 | |||
| 591 | WriteCategory(Settings::Category::Cpu); | ||
| 592 | WriteCategory(Settings::Category::CpuDebug); | ||
| 593 | WriteCategory(Settings::Category::CpuUnsafe); | ||
| 594 | |||
| 595 | EndGroup(); | ||
| 596 | } | ||
| 597 | |||
| 598 | void Config::SaveRendererValues() { | ||
| 599 | BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer)); | ||
| 600 | |||
| 601 | WriteCategory(Settings::Category::Renderer); | ||
| 602 | WriteCategory(Settings::Category::RendererAdvanced); | ||
| 603 | WriteCategory(Settings::Category::RendererDebug); | ||
| 604 | |||
| 605 | EndGroup(); | ||
| 606 | } | ||
| 607 | |||
| 608 | void Config::SaveScreenshotValues() { | ||
| 609 | BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots)); | ||
| 610 | |||
| 611 | WriteSetting(std::string("screenshot_path"), | ||
| 612 | FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)); | ||
| 613 | WriteCategory(Settings::Category::Screenshots); | ||
| 614 | |||
| 615 | EndGroup(); | ||
| 616 | } | ||
| 617 | |||
| 618 | void Config::SaveSystemValues() { | ||
| 619 | BeginGroup(Settings::TranslateCategory(Settings::Category::System)); | ||
| 620 | |||
| 621 | WriteCategory(Settings::Category::System); | ||
| 622 | WriteCategory(Settings::Category::SystemAudio); | ||
| 623 | |||
| 624 | EndGroup(); | ||
| 625 | } | ||
| 626 | |||
| 627 | void Config::SaveWebServiceValues() { | ||
| 628 | BeginGroup(Settings::TranslateCategory(Settings::Category::WebService)); | ||
| 629 | |||
| 630 | WriteCategory(Settings::Category::WebService); | ||
| 631 | |||
| 632 | EndGroup(); | ||
| 633 | } | ||
| 634 | |||
| 635 | bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) { | ||
| 636 | std::string full_key = GetFullKey(key, false); | ||
| 637 | if (!default_value.has_value()) { | ||
| 638 | return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), false); | ||
| 639 | } | ||
| 640 | |||
| 641 | if (config->GetBoolValue(GetSection().c_str(), | ||
| 642 | std::string(full_key).append("\\default").c_str(), false)) { | ||
| 643 | return static_cast<bool>(default_value.value()); | ||
| 644 | } else { | ||
| 645 | return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), | ||
| 646 | static_cast<bool>(default_value.value())); | ||
| 647 | } | ||
| 648 | } | ||
| 649 | |||
| 650 | s64 Config::ReadIntegerSetting(const std::string& key, const std::optional<s64> default_value) { | ||
| 651 | std::string full_key = GetFullKey(key, false); | ||
| 652 | if (!default_value.has_value()) { | ||
| 653 | try { | ||
| 654 | return std::stoll( | ||
| 655 | std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0"))); | ||
| 656 | } catch (...) { | ||
| 657 | return 0; | ||
| 658 | } | ||
| 659 | } | ||
| 660 | |||
| 661 | s64 result = 0; | ||
| 662 | if (config->GetBoolValue(GetSection().c_str(), | ||
| 663 | std::string(full_key).append("\\default").c_str(), true)) { | ||
| 664 | result = default_value.value(); | ||
| 665 | } else { | ||
| 666 | try { | ||
| 667 | result = std::stoll(std::string(config->GetValue( | ||
| 668 | GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str()))); | ||
| 669 | } catch (...) { | ||
| 670 | result = default_value.value(); | ||
| 671 | } | ||
| 672 | } | ||
| 673 | return result; | ||
| 674 | } | ||
| 675 | |||
| 676 | u64 Config::ReadUnsignedIntegerSetting(const std::string& key, | ||
| 677 | const std::optional<u64> default_value) { | ||
| 678 | std::string full_key = GetFullKey(key, false); | ||
| 679 | if (!default_value.has_value()) { | ||
| 680 | try { | ||
| 681 | return std::stoull( | ||
| 682 | std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0"))); | ||
| 683 | } catch (...) { | ||
| 684 | return 0; | ||
| 685 | } | ||
| 686 | } | ||
| 687 | |||
| 688 | u64 result = 0; | ||
| 689 | if (config->GetBoolValue(GetSection().c_str(), | ||
| 690 | std::string(full_key).append("\\default").c_str(), true)) { | ||
| 691 | result = default_value.value(); | ||
| 692 | } else { | ||
| 693 | try { | ||
| 694 | result = std::stoull(std::string(config->GetValue( | ||
| 695 | GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str()))); | ||
| 696 | } catch (...) { | ||
| 697 | result = default_value.value(); | ||
| 698 | } | ||
| 699 | } | ||
| 700 | return result; | ||
| 701 | } | ||
| 702 | |||
| 703 | double Config::ReadDoubleSetting(const std::string& key, | ||
| 704 | const std::optional<double> default_value) { | ||
| 705 | std::string full_key = GetFullKey(key, false); | ||
| 706 | if (!default_value.has_value()) { | ||
| 707 | return config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), 0); | ||
| 708 | } | ||
| 709 | |||
| 710 | double result; | ||
| 711 | if (config->GetBoolValue(GetSection().c_str(), | ||
| 712 | std::string(full_key).append("\\default").c_str(), true)) { | ||
| 713 | result = default_value.value(); | ||
| 714 | } else { | ||
| 715 | result = | ||
| 716 | config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), default_value.value()); | ||
| 717 | } | ||
| 718 | return result; | ||
| 719 | } | ||
| 720 | |||
| 721 | std::string Config::ReadStringSetting(const std::string& key, | ||
| 722 | const std::optional<std::string> default_value) { | ||
| 723 | std::string result; | ||
| 724 | std::string full_key = GetFullKey(key, false); | ||
| 725 | if (!default_value.has_value()) { | ||
| 726 | result = config->GetValue(GetSection().c_str(), full_key.c_str(), ""); | ||
| 727 | boost::replace_all(result, "\"", ""); | ||
| 728 | return result; | ||
| 729 | } | ||
| 730 | |||
| 731 | if (config->GetBoolValue(GetSection().c_str(), | ||
| 732 | std::string(full_key).append("\\default").c_str(), true)) { | ||
| 733 | result = default_value.value(); | ||
| 734 | } else { | ||
| 735 | result = | ||
| 736 | config->GetValue(GetSection().c_str(), full_key.c_str(), default_value.value().c_str()); | ||
| 737 | } | ||
| 738 | boost::replace_all(result, "\"", ""); | ||
| 739 | boost::replace_all(result, "//", "/"); | ||
| 740 | return result; | ||
| 741 | } | ||
| 742 | |||
| 743 | bool Config::Exists(const std::string& section, const std::string& key) const { | ||
| 744 | const std::string value = config->GetValue(section.c_str(), key.c_str(), ""); | ||
| 745 | return !value.empty(); | ||
| 746 | } | ||
| 747 | |||
| 748 | template <typename Type> | ||
| 749 | void Config::WriteSetting(const std::string& key, const Type& value, | ||
| 750 | const std::optional<Type>& default_value, | ||
| 751 | const std::optional<bool>& use_global) { | ||
| 752 | std::string full_key = GetFullKey(key, false); | ||
| 753 | |||
| 754 | std::string saved_value; | ||
| 755 | std::string string_default; | ||
| 756 | if constexpr (std::is_same_v<Type, std::string>) { | ||
| 757 | saved_value.append(AdjustOutputString(value)); | ||
| 758 | if (default_value.has_value()) { | ||
| 759 | string_default.append(AdjustOutputString(default_value.value())); | ||
| 760 | } | ||
| 761 | } else { | ||
| 762 | saved_value.append(AdjustOutputString(ToString(value))); | ||
| 763 | if (default_value.has_value()) { | ||
| 764 | string_default.append(ToString(default_value.value())); | ||
| 765 | } | ||
| 766 | } | ||
| 767 | |||
| 768 | if (default_value.has_value() && use_global.has_value()) { | ||
| 769 | if (!global) { | ||
| 770 | WriteSettingInternal(std::string(full_key).append("\\global"), | ||
| 771 | ToString(use_global.value())); | ||
| 772 | } | ||
| 773 | if (global || use_global.value() == false) { | ||
| 774 | WriteSettingInternal(std::string(full_key).append("\\default"), | ||
| 775 | ToString(string_default == saved_value)); | ||
| 776 | WriteSettingInternal(full_key, saved_value); | ||
| 777 | } | ||
| 778 | } else if (default_value.has_value() && !use_global.has_value()) { | ||
| 779 | WriteSettingInternal(std::string(full_key).append("\\default"), | ||
| 780 | ToString(string_default == saved_value)); | ||
| 781 | WriteSettingInternal(full_key, saved_value); | ||
| 782 | } else { | ||
| 783 | WriteSettingInternal(full_key, saved_value); | ||
| 784 | } | ||
| 785 | } | ||
| 786 | |||
| 787 | void Config::WriteSettingInternal(const std::string& key, const std::string& value) { | ||
| 788 | config->SetValue(GetSection().c_str(), key.c_str(), value.c_str()); | ||
| 789 | } | ||
| 790 | |||
| 791 | void Config::Reload() { | ||
| 792 | ReadValues(); | ||
| 793 | // To apply default value changes | ||
| 794 | SaveValues(); | ||
| 795 | } | ||
| 796 | |||
| 797 | void Config::Save() { | ||
| 798 | SaveValues(); | ||
| 799 | } | ||
| 800 | |||
| 801 | void Config::ClearControlPlayerValues() const { | ||
| 802 | // If key is an empty string, all keys in the current group() are removed. | ||
| 803 | const char* section = Settings::TranslateCategory(Settings::Category::Controls); | ||
| 804 | CSimpleIniA::TNamesDepend keys; | ||
| 805 | config->GetAllKeys(section, keys); | ||
| 806 | for (const auto& key : keys) { | ||
| 807 | if (std::string(config->GetValue(section, key.pItem)).empty()) { | ||
| 808 | config->Delete(section, key.pItem); | ||
| 809 | } | ||
| 810 | } | ||
| 811 | } | ||
| 812 | |||
| 813 | const std::string& Config::GetConfigFilePath() const { | ||
| 814 | return config_loc; | ||
| 815 | } | ||
| 816 | |||
| 817 | void Config::ReadCategory(const Settings::Category category) { | ||
| 818 | const auto& settings = FindRelevantList(category); | ||
| 819 | std::ranges::for_each(settings, [&](const auto& setting) { ReadSettingGeneric(setting); }); | ||
| 820 | } | ||
| 821 | |||
| 822 | void Config::WriteCategory(const Settings::Category category) { | ||
| 823 | const auto& settings = FindRelevantList(category); | ||
| 824 | std::ranges::for_each(settings, [&](const auto& setting) { WriteSettingGeneric(setting); }); | ||
| 825 | } | ||
| 826 | |||
| 827 | void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) { | ||
| 828 | if (!setting->Save() || (!setting->Switchable() && !global)) { | ||
| 829 | return; | ||
| 830 | } | ||
| 831 | |||
| 832 | const std::string key = AdjustKey(setting->GetLabel()); | ||
| 833 | const std::string default_value(setting->DefaultToString()); | ||
| 834 | |||
| 835 | bool use_global = true; | ||
| 836 | if (setting->Switchable() && !global) { | ||
| 837 | use_global = | ||
| 838 | ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true)); | ||
| 839 | setting->SetGlobal(use_global); | ||
| 840 | } | ||
| 841 | |||
| 842 | if (global || !use_global) { | ||
| 843 | const bool is_default = | ||
| 844 | ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true)); | ||
| 845 | if (!is_default) { | ||
| 846 | const std::string setting_string = ReadStringSetting(key, default_value); | ||
| 847 | setting->LoadString(setting_string); | ||
| 848 | } else { | ||
| 849 | // Empty string resets the Setting to default | ||
| 850 | setting->LoadString(""); | ||
| 851 | } | ||
| 852 | } | ||
| 853 | } | ||
| 854 | |||
| 855 | void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) { | ||
| 856 | if (!setting->Save()) { | ||
| 857 | return; | ||
| 858 | } | ||
| 859 | |||
| 860 | std::string key = AdjustKey(setting->GetLabel()); | ||
| 861 | if (setting->Switchable()) { | ||
| 862 | if (!global) { | ||
| 863 | WriteSetting(std::string(key).append("\\use_global"), setting->UsingGlobal()); | ||
| 864 | } | ||
| 865 | if (global || !setting->UsingGlobal()) { | ||
| 866 | WriteSetting(std::string(key).append("\\default"), | ||
| 867 | setting->ToString() == setting->DefaultToString()); | ||
| 868 | WriteSetting(key, setting->ToString()); | ||
| 869 | } | ||
| 870 | } else if (global) { | ||
| 871 | WriteSetting(std::string(key).append("\\default"), | ||
| 872 | setting->ToString() == setting->DefaultToString()); | ||
| 873 | WriteSetting(key, setting->ToString()); | ||
| 874 | } | ||
| 875 | } | ||
| 876 | |||
| 877 | void Config::BeginGroup(const std::string& group) { | ||
| 878 | // You can't begin a group while reading/writing from a config array | ||
| 879 | ASSERT(array_stack.empty()); | ||
| 880 | |||
| 881 | key_stack.push_back(AdjustKey(group)); | ||
| 882 | } | ||
| 883 | |||
| 884 | void Config::EndGroup() { | ||
| 885 | // You can't end a group if you haven't started one yet | ||
| 886 | ASSERT(!key_stack.empty()); | ||
| 887 | |||
| 888 | // You can't end a group when reading/writing from a config array | ||
| 889 | ASSERT(array_stack.empty()); | ||
| 890 | |||
| 891 | key_stack.pop_back(); | ||
| 892 | } | ||
| 893 | |||
| 894 | std::string Config::GetSection() { | ||
| 895 | if (key_stack.empty()) { | ||
| 896 | return std::string{""}; | ||
| 897 | } | ||
| 898 | |||
| 899 | return key_stack.front(); | ||
| 900 | } | ||
| 901 | |||
| 902 | std::string Config::GetGroup() const { | ||
| 903 | if (key_stack.size() <= 1) { | ||
| 904 | return std::string{""}; | ||
| 905 | } | ||
| 906 | |||
| 907 | std::string key; | ||
| 908 | for (size_t i = 1; i < key_stack.size(); ++i) { | ||
| 909 | key.append(key_stack[i]).append("\\"); | ||
| 910 | } | ||
| 911 | return key; | ||
| 912 | } | ||
| 913 | |||
| 914 | std::string Config::AdjustKey(const std::string& key) { | ||
| 915 | std::string adjusted_key(key); | ||
| 916 | boost::replace_all(adjusted_key, "/", "\\"); | ||
| 917 | boost::replace_all(adjusted_key, " ", "%20"); | ||
| 918 | return adjusted_key; | ||
| 919 | } | ||
| 920 | |||
| 921 | std::string Config::AdjustOutputString(const std::string& string) { | ||
| 922 | std::string adjusted_string(string); | ||
| 923 | boost::replace_all(adjusted_string, "\\", "/"); | ||
| 924 | |||
| 925 | // Windows requires that two forward slashes are used at the start of a path for unmapped | ||
| 926 | // network drives so we have to watch for that here | ||
| 927 | if (string.substr(0, 2) == "//") { | ||
| 928 | boost::replace_all(adjusted_string, "//", "/"); | ||
| 929 | adjusted_string.insert(0, "/"); | ||
| 930 | } else { | ||
| 931 | boost::replace_all(adjusted_string, "//", "/"); | ||
| 932 | } | ||
| 933 | |||
| 934 | // Needed for backwards compatibility with QSettings deserialization | ||
| 935 | for (const auto& special_character : special_characters) { | ||
| 936 | if (adjusted_string.find(special_character) != std::string::npos) { | ||
| 937 | adjusted_string.insert(0, "\""); | ||
| 938 | adjusted_string.append("\""); | ||
| 939 | break; | ||
| 940 | } | ||
| 941 | } | ||
| 942 | return adjusted_string; | ||
| 943 | } | ||
| 944 | |||
| 945 | std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) { | ||
| 946 | if (array_stack.empty()) { | ||
| 947 | return std::string(GetGroup()).append(AdjustKey(key)); | ||
| 948 | } | ||
| 949 | |||
| 950 | std::string array_key; | ||
| 951 | for (size_t i = 0; i < array_stack.size(); ++i) { | ||
| 952 | if (!array_stack[i].name.empty()) { | ||
| 953 | array_key.append(array_stack[i].name).append("\\"); | ||
| 954 | } | ||
| 955 | |||
| 956 | if (!skipArrayIndex || (array_stack.size() - 1 != i && array_stack.size() > 1)) { | ||
| 957 | array_key.append(ToString(array_stack[i].index)).append("\\"); | ||
| 958 | } | ||
| 959 | } | ||
| 960 | std::string final_key = std::string(GetGroup()).append(array_key).append(AdjustKey(key)); | ||
| 961 | return final_key; | ||
| 962 | } | ||
| 963 | |||
| 964 | int Config::BeginArray(const std::string& array) { | ||
| 965 | array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0}); | ||
| 966 | const int size = config->GetLongValue(GetSection().c_str(), | ||
| 967 | GetFullKey(std::string("size"), true).c_str(), 0); | ||
| 968 | array_stack.back().size = size; | ||
| 969 | return size; | ||
| 970 | } | ||
| 971 | |||
| 972 | void Config::EndArray() { | ||
| 973 | // You can't end a config array before starting one | ||
| 974 | ASSERT(!array_stack.empty()); | ||
| 975 | |||
| 976 | // Set the array size to 0 if the array is ended without changing the index | ||
| 977 | int size = 0; | ||
| 978 | if (array_stack.back().index != 0) { | ||
| 979 | size = array_stack.back().size; | ||
| 980 | } | ||
| 981 | |||
| 982 | // Write out the size to config | ||
| 983 | if (key_stack.size() == 1 && array_stack.back().name.empty()) { | ||
| 984 | // Edge-case where the first array created doesn't have a name | ||
| 985 | config->SetValue(GetSection().c_str(), std::string("size").c_str(), ToString(size).c_str()); | ||
| 986 | } else { | ||
| 987 | const auto key = GetFullKey(std::string("size"), true); | ||
| 988 | config->SetValue(GetSection().c_str(), key.c_str(), ToString(size).c_str()); | ||
| 989 | } | ||
| 990 | |||
| 991 | array_stack.pop_back(); | ||
| 992 | } | ||
| 993 | |||
| 994 | void Config::SetArrayIndex(const int index) { | ||
| 995 | // You can't set the array index if you haven't started one yet | ||
| 996 | ASSERT(!array_stack.empty()); | ||
| 997 | |||
| 998 | const int array_index = index + 1; | ||
| 999 | |||
| 1000 | // You can't exceed the known max size of the array by more than 1 | ||
| 1001 | ASSERT(array_stack.front().size + 1 >= array_index); | ||
| 1002 | |||
| 1003 | // Change the config array size to the current index since you may want | ||
| 1004 | // to reduce the number of elements that you read back from the config | ||
| 1005 | // in the future. | ||
| 1006 | array_stack.back().size = array_index; | ||
| 1007 | array_stack.back().index = array_index; | ||
| 1008 | } | ||
diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h new file mode 100644 index 000000000..b3812af17 --- /dev/null +++ b/src/frontend_common/config.h | |||
| @@ -0,0 +1,211 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include <string> | ||
| 8 | #include "common/settings.h" | ||
| 9 | |||
| 10 | #define SI_NO_CONVERSION | ||
| 11 | #include <SimpleIni.h> | ||
| 12 | #include <boost/algorithm/string/replace.hpp> | ||
| 13 | |||
| 14 | // Workaround for conflicting definition in libloaderapi.h caused by SimpleIni | ||
| 15 | #undef LoadString | ||
| 16 | #undef CreateFile | ||
| 17 | #undef DeleteFile | ||
| 18 | #undef CopyFile | ||
| 19 | #undef CreateDirectory | ||
| 20 | #undef MoveFile | ||
| 21 | |||
| 22 | namespace Core { | ||
| 23 | class System; | ||
| 24 | } | ||
| 25 | |||
| 26 | class Config { | ||
| 27 | public: | ||
| 28 | enum class ConfigType { | ||
| 29 | GlobalConfig, | ||
| 30 | PerGameConfig, | ||
| 31 | InputProfile, | ||
| 32 | }; | ||
| 33 | |||
| 34 | virtual ~Config() = default; | ||
| 35 | |||
| 36 | void ClearControlPlayerValues() const; | ||
| 37 | |||
| 38 | [[nodiscard]] const std::string& GetConfigFilePath() const; | ||
| 39 | |||
| 40 | [[nodiscard]] bool Exists(const std::string& section, const std::string& key) const; | ||
| 41 | |||
| 42 | protected: | ||
| 43 | explicit Config(ConfigType config_type = ConfigType::GlobalConfig); | ||
| 44 | |||
| 45 | void Initialize(const std::string& config_name = "config"); | ||
| 46 | void Initialize(std::optional<std::string> config_path); | ||
| 47 | |||
| 48 | void WriteToIni() const; | ||
| 49 | |||
| 50 | void SetUpIni(); | ||
| 51 | [[nodiscard]] bool IsCustomConfig() const; | ||
| 52 | |||
| 53 | void Reload(); | ||
| 54 | void Save(); | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Derived config classes must implement this so they can reload all platform-specific | ||
| 58 | * values and global ones. | ||
| 59 | */ | ||
| 60 | virtual void ReloadAllValues() = 0; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Derived config classes must implement this so they can save all platform-specific | ||
| 64 | * and global values. | ||
| 65 | */ | ||
| 66 | virtual void SaveAllValues() = 0; | ||
| 67 | |||
| 68 | void ReadValues(); | ||
| 69 | void ReadPlayerValues(std::size_t player_index); | ||
| 70 | |||
| 71 | void ReadTouchscreenValues(); | ||
| 72 | void ReadMotionTouchValues(); | ||
| 73 | |||
| 74 | // Read functions bases off the respective config section names. | ||
| 75 | void ReadAudioValues(); | ||
| 76 | void ReadControlValues(); | ||
| 77 | void ReadCoreValues(); | ||
| 78 | void ReadDataStorageValues(); | ||
| 79 | void ReadDebuggingValues(); | ||
| 80 | void ReadServiceValues(); | ||
| 81 | void ReadDisabledAddOnValues(); | ||
| 82 | void ReadMiscellaneousValues(); | ||
| 83 | void ReadCpuValues(); | ||
| 84 | void ReadRendererValues(); | ||
| 85 | void ReadScreenshotValues(); | ||
| 86 | void ReadSystemValues(); | ||
| 87 | void ReadWebServiceValues(); | ||
| 88 | void ReadNetworkValues(); | ||
| 89 | |||
| 90 | // Read platform specific sections | ||
| 91 | virtual void ReadHidbusValues() = 0; | ||
| 92 | virtual void ReadDebugControlValues() = 0; | ||
| 93 | virtual void ReadPathValues() = 0; | ||
| 94 | virtual void ReadShortcutValues() = 0; | ||
| 95 | virtual void ReadUIValues() = 0; | ||
| 96 | virtual void ReadUIGamelistValues() = 0; | ||
| 97 | virtual void ReadUILayoutValues() = 0; | ||
| 98 | virtual void ReadMultiplayerValues() = 0; | ||
| 99 | |||
| 100 | void SaveValues(); | ||
| 101 | void SavePlayerValues(std::size_t player_index); | ||
| 102 | void SaveTouchscreenValues(); | ||
| 103 | void SaveMotionTouchValues(); | ||
| 104 | |||
| 105 | // Save functions based off the respective config section names. | ||
| 106 | void SaveAudioValues(); | ||
| 107 | void SaveControlValues(); | ||
| 108 | void SaveCoreValues(); | ||
| 109 | void SaveDataStorageValues(); | ||
| 110 | void SaveDebuggingValues(); | ||
| 111 | void SaveNetworkValues(); | ||
| 112 | void SaveDisabledAddOnValues(); | ||
| 113 | void SaveMiscellaneousValues(); | ||
| 114 | void SaveCpuValues(); | ||
| 115 | void SaveRendererValues(); | ||
| 116 | void SaveScreenshotValues(); | ||
| 117 | void SaveSystemValues(); | ||
| 118 | void SaveWebServiceValues(); | ||
| 119 | |||
| 120 | // Save platform specific sections | ||
| 121 | virtual void SaveHidbusValues() = 0; | ||
| 122 | virtual void SaveDebugControlValues() = 0; | ||
| 123 | virtual void SavePathValues() = 0; | ||
| 124 | virtual void SaveShortcutValues() = 0; | ||
| 125 | virtual void SaveUIValues() = 0; | ||
| 126 | virtual void SaveUIGamelistValues() = 0; | ||
| 127 | virtual void SaveUILayoutValues() = 0; | ||
| 128 | virtual void SaveMultiplayerValues() = 0; | ||
| 129 | |||
| 130 | virtual std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) = 0; | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Reads a setting from the qt_config. | ||
| 134 | * | ||
| 135 | * @param key The setting's identifier | ||
| 136 | * @param default_value The value to use when the setting is not already present in the config | ||
| 137 | */ | ||
| 138 | bool ReadBooleanSetting(const std::string& key, | ||
| 139 | std::optional<bool> default_value = std::nullopt); | ||
| 140 | s64 ReadIntegerSetting(const std::string& key, std::optional<s64> default_value = std::nullopt); | ||
| 141 | u64 ReadUnsignedIntegerSetting(const std::string& key, | ||
| 142 | std::optional<u64> default_value = std::nullopt); | ||
| 143 | double ReadDoubleSetting(const std::string& key, | ||
| 144 | std::optional<double> default_value = std::nullopt); | ||
| 145 | std::string ReadStringSetting(const std::string& key, | ||
| 146 | std::optional<std::string> default_value = std::nullopt); | ||
| 147 | |||
| 148 | /** | ||
| 149 | * Writes a setting to the qt_config. | ||
| 150 | * | ||
| 151 | * @param key The setting's idetentifier | ||
| 152 | * @param value Value of the setting | ||
| 153 | * @param default_value Default of the setting if not present in config | ||
| 154 | * @param use_global Specifies if the custom or global config should be in use, for custom | ||
| 155 | * configs | ||
| 156 | */ | ||
| 157 | template <typename Type = int> | ||
| 158 | void WriteSetting(const std::string& key, const Type& value, | ||
| 159 | const std::optional<Type>& default_value = std::nullopt, | ||
| 160 | const std::optional<bool>& use_global = std::nullopt); | ||
| 161 | void WriteSettingInternal(const std::string& key, const std::string& value); | ||
| 162 | |||
| 163 | void ReadCategory(Settings::Category category); | ||
| 164 | void WriteCategory(Settings::Category category); | ||
| 165 | void ReadSettingGeneric(Settings::BasicSetting* setting); | ||
| 166 | void WriteSettingGeneric(const Settings::BasicSetting* setting); | ||
| 167 | |||
| 168 | template <typename T> | ||
| 169 | [[nodiscard]] std::string ToString(const T& value_) { | ||
| 170 | if constexpr (std::is_same_v<T, std::string>) { | ||
| 171 | return value_; | ||
| 172 | } else if constexpr (std::is_same_v<T, std::optional<u32>>) { | ||
| 173 | return value_.has_value() ? std::to_string(*value_) : "none"; | ||
| 174 | } else if constexpr (std::is_same_v<T, bool>) { | ||
| 175 | return value_ ? "true" : "false"; | ||
| 176 | } else if constexpr (std::is_same_v<T, u64>) { | ||
| 177 | return std::to_string(static_cast<u64>(value_)); | ||
| 178 | } else { | ||
| 179 | return std::to_string(static_cast<s64>(value_)); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | void BeginGroup(const std::string& group); | ||
| 184 | void EndGroup(); | ||
| 185 | std::string GetSection(); | ||
| 186 | [[nodiscard]] std::string GetGroup() const; | ||
| 187 | static std::string AdjustKey(const std::string& key); | ||
| 188 | static std::string AdjustOutputString(const std::string& string); | ||
| 189 | std::string GetFullKey(const std::string& key, bool skipArrayIndex); | ||
| 190 | int BeginArray(const std::string& array); | ||
| 191 | void EndArray(); | ||
| 192 | void SetArrayIndex(int index); | ||
| 193 | |||
| 194 | const ConfigType type; | ||
| 195 | std::unique_ptr<CSimpleIniA> config; | ||
| 196 | std::string config_loc; | ||
| 197 | const bool global; | ||
| 198 | |||
| 199 | private: | ||
| 200 | inline static std::array<char, 19> special_characters = {'!', '#', '$', '%', '^', '&', '*', | ||
| 201 | '|', ';', '\'', '\"', ',', '<', '.', | ||
| 202 | '>', '?', '`', '~', '='}; | ||
| 203 | |||
| 204 | struct ConfigArray { | ||
| 205 | std::string name; | ||
| 206 | int size; | ||
| 207 | int index; | ||
| 208 | }; | ||
| 209 | std::vector<ConfigArray> array_stack; | ||
| 210 | std::vector<std::string> key_stack; | ||
| 211 | }; | ||
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index 3ad34884d..1ff296af5 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp | |||
| @@ -415,7 +415,7 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p | |||
| 415 | // This list is missing ZL/ZR since those are not considered buttons. | 415 | // This list is missing ZL/ZR since those are not considered buttons. |
| 416 | // We will add those afterwards | 416 | // We will add those afterwards |
| 417 | // This list also excludes any button that can't be really mapped | 417 | // This list also excludes any button that can't be really mapped |
| 418 | static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12> | 418 | static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 14> |
| 419 | switch_to_gcadapter_button = { | 419 | switch_to_gcadapter_button = { |
| 420 | std::pair{Settings::NativeButton::A, PadButton::ButtonA}, | 420 | std::pair{Settings::NativeButton::A, PadButton::ButtonA}, |
| 421 | {Settings::NativeButton::B, PadButton::ButtonB}, | 421 | {Settings::NativeButton::B, PadButton::ButtonB}, |
| @@ -426,8 +426,10 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p | |||
| 426 | {Settings::NativeButton::DUp, PadButton::ButtonUp}, | 426 | {Settings::NativeButton::DUp, PadButton::ButtonUp}, |
| 427 | {Settings::NativeButton::DRight, PadButton::ButtonRight}, | 427 | {Settings::NativeButton::DRight, PadButton::ButtonRight}, |
| 428 | {Settings::NativeButton::DDown, PadButton::ButtonDown}, | 428 | {Settings::NativeButton::DDown, PadButton::ButtonDown}, |
| 429 | {Settings::NativeButton::SL, PadButton::TriggerL}, | 429 | {Settings::NativeButton::SLLeft, PadButton::TriggerL}, |
| 430 | {Settings::NativeButton::SR, PadButton::TriggerR}, | 430 | {Settings::NativeButton::SRLeft, PadButton::TriggerR}, |
| 431 | {Settings::NativeButton::SLRight, PadButton::TriggerL}, | ||
| 432 | {Settings::NativeButton::SRRight, PadButton::TriggerR}, | ||
| 431 | {Settings::NativeButton::R, PadButton::TriggerZ}, | 433 | {Settings::NativeButton::R, PadButton::TriggerZ}, |
| 432 | }; | 434 | }; |
| 433 | if (!params.Has("port")) { | 435 | if (!params.Has("port")) { |
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 0aca5a3a3..72d2951f3 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp | |||
| @@ -680,8 +680,8 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par | |||
| 680 | Common::ParamPackage sr_button_params = button_params; | 680 | Common::ParamPackage sr_button_params = button_params; |
| 681 | sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL)); | 681 | sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL)); |
| 682 | sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR)); | 682 | sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR)); |
| 683 | mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); | 683 | mapping.insert_or_assign(Settings::NativeButton::SLLeft, std::move(sl_button_params)); |
| 684 | mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); | 684 | mapping.insert_or_assign(Settings::NativeButton::SRLeft, std::move(sr_button_params)); |
| 685 | } | 685 | } |
| 686 | 686 | ||
| 687 | // Map SL and SR buttons for right joycons | 687 | // Map SL and SR buttons for right joycons |
| @@ -693,8 +693,8 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par | |||
| 693 | Common::ParamPackage sr_button_params = button_params; | 693 | Common::ParamPackage sr_button_params = button_params; |
| 694 | sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL)); | 694 | sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL)); |
| 695 | sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR)); | 695 | sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR)); |
| 696 | mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); | 696 | mapping.insert_or_assign(Settings::NativeButton::SLRight, std::move(sl_button_params)); |
| 697 | mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); | 697 | mapping.insert_or_assign(Settings::NativeButton::SRRight, std::move(sr_button_params)); |
| 698 | } | 698 | } |
| 699 | 699 | ||
| 700 | return mapping; | 700 | return mapping; |
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 66e3ae9af..78f458afe 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -828,16 +828,18 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p | |||
| 828 | ButtonBindings SDLDriver::GetDefaultButtonBinding( | 828 | ButtonBindings SDLDriver::GetDefaultButtonBinding( |
| 829 | const std::shared_ptr<SDLJoystick>& joystick) const { | 829 | const std::shared_ptr<SDLJoystick>& joystick) const { |
| 830 | // Default SL/SR mapping for other controllers | 830 | // Default SL/SR mapping for other controllers |
| 831 | auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; | 831 | auto sll_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; |
| 832 | auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; | 832 | auto srl_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; |
| 833 | auto slr_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; | ||
| 834 | auto srr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; | ||
| 833 | 835 | ||
| 834 | if (joystick->IsJoyconLeft()) { | 836 | if (joystick->IsJoyconLeft()) { |
| 835 | sl_button = SDL_CONTROLLER_BUTTON_PADDLE2; | 837 | sll_button = SDL_CONTROLLER_BUTTON_PADDLE2; |
| 836 | sr_button = SDL_CONTROLLER_BUTTON_PADDLE4; | 838 | srl_button = SDL_CONTROLLER_BUTTON_PADDLE4; |
| 837 | } | 839 | } |
| 838 | if (joystick->IsJoyconRight()) { | 840 | if (joystick->IsJoyconRight()) { |
| 839 | sl_button = SDL_CONTROLLER_BUTTON_PADDLE3; | 841 | slr_button = SDL_CONTROLLER_BUTTON_PADDLE3; |
| 840 | sr_button = SDL_CONTROLLER_BUTTON_PADDLE1; | 842 | srr_button = SDL_CONTROLLER_BUTTON_PADDLE1; |
| 841 | } | 843 | } |
| 842 | 844 | ||
| 843 | return { | 845 | return { |
| @@ -855,8 +857,10 @@ ButtonBindings SDLDriver::GetDefaultButtonBinding( | |||
| 855 | {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, | 857 | {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, |
| 856 | {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, | 858 | {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, |
| 857 | {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, | 859 | {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, |
| 858 | {Settings::NativeButton::SL, sl_button}, | 860 | {Settings::NativeButton::SLLeft, sll_button}, |
| 859 | {Settings::NativeButton::SR, sr_button}, | 861 | {Settings::NativeButton::SRLeft, srl_button}, |
| 862 | {Settings::NativeButton::SLRight, slr_button}, | ||
| 863 | {Settings::NativeButton::SRRight, srr_button}, | ||
| 860 | {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, | 864 | {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, |
| 861 | {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1}, | 865 | {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1}, |
| 862 | }; | 866 | }; |
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index fcba4e3c6..08e49a0da 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h | |||
| @@ -24,7 +24,7 @@ namespace InputCommon { | |||
| 24 | class SDLJoystick; | 24 | class SDLJoystick; |
| 25 | 25 | ||
| 26 | using ButtonBindings = | 26 | using ButtonBindings = |
| 27 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 18>; | 27 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 20>; |
| 28 | using ZButtonBindings = | 28 | using ZButtonBindings = |
| 29 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; | 29 | std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; |
| 30 | 30 | ||
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index 77db60e92..60821b31a 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp | |||
| @@ -396,7 +396,7 @@ std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const { | |||
| 396 | 396 | ||
| 397 | ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) { | 397 | ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) { |
| 398 | // This list excludes any button that can't be really mapped | 398 | // This list excludes any button that can't be really mapped |
| 399 | static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 20> | 399 | static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 22> |
| 400 | switch_to_dsu_button = { | 400 | switch_to_dsu_button = { |
| 401 | std::pair{Settings::NativeButton::A, PadButton::Circle}, | 401 | std::pair{Settings::NativeButton::A, PadButton::Circle}, |
| 402 | {Settings::NativeButton::B, PadButton::Cross}, | 402 | {Settings::NativeButton::B, PadButton::Cross}, |
| @@ -412,8 +412,10 @@ ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& p | |||
| 412 | {Settings::NativeButton::R, PadButton::R1}, | 412 | {Settings::NativeButton::R, PadButton::R1}, |
| 413 | {Settings::NativeButton::ZL, PadButton::L2}, | 413 | {Settings::NativeButton::ZL, PadButton::L2}, |
| 414 | {Settings::NativeButton::ZR, PadButton::R2}, | 414 | {Settings::NativeButton::ZR, PadButton::R2}, |
| 415 | {Settings::NativeButton::SL, PadButton::L2}, | 415 | {Settings::NativeButton::SLLeft, PadButton::L2}, |
| 416 | {Settings::NativeButton::SR, PadButton::R2}, | 416 | {Settings::NativeButton::SRLeft, PadButton::R2}, |
| 417 | {Settings::NativeButton::SLRight, PadButton::L2}, | ||
| 418 | {Settings::NativeButton::SRRight, PadButton::R2}, | ||
| 417 | {Settings::NativeButton::LStick, PadButton::L3}, | 419 | {Settings::NativeButton::LStick, PadButton::L3}, |
| 418 | {Settings::NativeButton::RStick, PadButton::R3}, | 420 | {Settings::NativeButton::RStick, PadButton::R3}, |
| 419 | {Settings::NativeButton::Home, PadButton::Home}, | 421 | {Settings::NativeButton::Home, PadButton::Home}, |
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 83b763447..19db17c6d 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt | |||
| @@ -231,6 +231,7 @@ add_library(shader_recompiler STATIC | |||
| 231 | ir_opt/rescaling_pass.cpp | 231 | ir_opt/rescaling_pass.cpp |
| 232 | ir_opt/ssa_rewrite_pass.cpp | 232 | ir_opt/ssa_rewrite_pass.cpp |
| 233 | ir_opt/texture_pass.cpp | 233 | ir_opt/texture_pass.cpp |
| 234 | ir_opt/vendor_workaround_pass.cpp | ||
| 234 | ir_opt/verification_pass.cpp | 235 | ir_opt/verification_pass.cpp |
| 235 | object_pool.h | 236 | object_pool.h |
| 236 | precompiled_headers.h | 237 | precompiled_headers.h |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp index d0e308124..64e7bad75 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp | |||
| @@ -559,12 +559,12 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | |||
| 559 | const IR::Value& offset, const IR::Value& lod_clamp) { | 559 | const IR::Value& offset, const IR::Value& lod_clamp) { |
| 560 | const auto info{inst.Flags<IR::TextureInstInfo>()}; | 560 | const auto info{inst.Flags<IR::TextureInstInfo>()}; |
| 561 | ScopedRegister dpdx, dpdy, coords; | 561 | ScopedRegister dpdx, dpdy, coords; |
| 562 | const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp}; | 562 | const bool multi_component{info.num_derivatives > 1 || info.has_lod_clamp}; |
| 563 | if (multi_component) { | 563 | if (multi_component) { |
| 564 | // Allocate this early to avoid aliasing other registers | 564 | // Allocate this early to avoid aliasing other registers |
| 565 | dpdx = ScopedRegister{ctx.reg_alloc}; | 565 | dpdx = ScopedRegister{ctx.reg_alloc}; |
| 566 | dpdy = ScopedRegister{ctx.reg_alloc}; | 566 | dpdy = ScopedRegister{ctx.reg_alloc}; |
| 567 | if (info.num_derivates >= 3) { | 567 | if (info.num_derivatives >= 3) { |
| 568 | coords = ScopedRegister{ctx.reg_alloc}; | 568 | coords = ScopedRegister{ctx.reg_alloc}; |
| 569 | } | 569 | } |
| 570 | } | 570 | } |
| @@ -584,7 +584,7 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | |||
| 584 | dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec, | 584 | dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec, |
| 585 | dpdy.reg, derivatives_vec); | 585 | dpdy.reg, derivatives_vec); |
| 586 | Register final_coord; | 586 | Register final_coord; |
| 587 | if (info.num_derivates >= 3) { | 587 | if (info.num_derivatives >= 3) { |
| 588 | ctx.Add("MOV.F {}.z,{}.x;" | 588 | ctx.Add("MOV.F {}.z,{}.x;" |
| 589 | "MOV.F {}.z,{}.y;", | 589 | "MOV.F {}.z,{}.y;", |
| 590 | dpdx.reg, coord_vec, dpdy.reg, coord_vec); | 590 | dpdx.reg, coord_vec, dpdy.reg, coord_vec); |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp index d9872ecc2..6e940bd5a 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp | |||
| @@ -548,15 +548,15 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | |||
| 548 | if (sparse_inst) { | 548 | if (sparse_inst) { |
| 549 | throw NotImplementedException("EmitImageGradient Sparse"); | 549 | throw NotImplementedException("EmitImageGradient Sparse"); |
| 550 | } | 550 | } |
| 551 | if (!offset.IsEmpty() && info.num_derivates <= 2) { | 551 | if (!offset.IsEmpty() && info.num_derivatives <= 2) { |
| 552 | throw NotImplementedException("EmitImageGradient offset"); | 552 | throw NotImplementedException("EmitImageGradient offset"); |
| 553 | } | 553 | } |
| 554 | const auto texture{Texture(ctx, info, index)}; | 554 | const auto texture{Texture(ctx, info, index)}; |
| 555 | const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)}; | 555 | const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)}; |
| 556 | const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp}; | 556 | const bool multi_component{info.num_derivatives > 1 || info.has_lod_clamp}; |
| 557 | const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)}; | 557 | const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)}; |
| 558 | if (multi_component) { | 558 | if (multi_component) { |
| 559 | if (info.num_derivates >= 3) { | 559 | if (info.num_derivatives >= 3) { |
| 560 | const auto offset_vec{ctx.var_alloc.Consume(offset)}; | 560 | const auto offset_vec{ctx.var_alloc.Consume(offset)}; |
| 561 | ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture, | 561 | ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture, |
| 562 | coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec); | 562 | coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 34592a01f..0031fa5fb 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp | |||
| @@ -407,7 +407,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct | |||
| 407 | } | 407 | } |
| 408 | ctx.AddCapability(spv::Capability::DemoteToHelperInvocation); | 408 | ctx.AddCapability(spv::Capability::DemoteToHelperInvocation); |
| 409 | } | 409 | } |
| 410 | if (info.stores[IR::Attribute::ViewportIndex]) { | 410 | if (info.stores[IR::Attribute::ViewportIndex] && profile.support_multi_viewport) { |
| 411 | ctx.AddCapability(spv::Capability::MultiViewport); | 411 | ctx.AddCapability(spv::Capability::MultiViewport); |
| 412 | } | 412 | } |
| 413 | if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) { | 413 | if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 1d77426e0..e5a78a914 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp | |||
| @@ -84,6 +84,10 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | |||
| 84 | } | 84 | } |
| 85 | return std::nullopt; | 85 | return std::nullopt; |
| 86 | case IR::Attribute::ViewportIndex: | 86 | case IR::Attribute::ViewportIndex: |
| 87 | if (!ctx.profile.support_multi_viewport) { | ||
| 88 | LOG_WARNING(Shader, "Ignoring viewport index store on non-supporting driver"); | ||
| 89 | return std::nullopt; | ||
| 90 | } | ||
| 87 | if (ctx.profile.support_viewport_index_layer_non_geometry || | 91 | if (ctx.profile.support_viewport_index_layer_non_geometry || |
| 88 | ctx.stage == Shader::Stage::Geometry) { | 92 | ctx.stage == Shader::Stage::Geometry) { |
| 89 | return OutAttr{ctx.viewport_index, ctx.U32[1]}; | 93 | return OutAttr{ctx.viewport_index, ctx.U32[1]}; |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 8decdf399..22ceca19c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | |||
| @@ -67,22 +67,22 @@ public: | |||
| 67 | } | 67 | } |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates, u32 num_derivates, | 70 | explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives, |
| 71 | Id offset, Id lod_clamp) { | 71 | u32 num_derivatives, Id offset, Id lod_clamp) { |
| 72 | if (!Sirit::ValidId(derivates)) { | 72 | if (!Sirit::ValidId(derivatives)) { |
| 73 | throw LogicError("Derivates must be present"); | 73 | throw LogicError("Derivatives must be present"); |
| 74 | } | 74 | } |
| 75 | boost::container::static_vector<Id, 3> deriv_x_accum; | 75 | boost::container::static_vector<Id, 3> deriv_x_accum; |
| 76 | boost::container::static_vector<Id, 3> deriv_y_accum; | 76 | boost::container::static_vector<Id, 3> deriv_y_accum; |
| 77 | for (u32 i = 0; i < num_derivates; ++i) { | 77 | for (u32 i = 0; i < num_derivatives; ++i) { |
| 78 | deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2)); | 78 | deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2)); |
| 79 | deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2 + 1)); | 79 | deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2 + 1)); |
| 80 | } | 80 | } |
| 81 | const Id derivates_X{ctx.OpCompositeConstruct( | 81 | const Id derivatives_X{ctx.OpCompositeConstruct( |
| 82 | ctx.F32[num_derivates], std::span{deriv_x_accum.data(), deriv_x_accum.size()})}; | 82 | ctx.F32[num_derivatives], std::span{deriv_x_accum.data(), deriv_x_accum.size()})}; |
| 83 | const Id derivates_Y{ctx.OpCompositeConstruct( | 83 | const Id derivatives_Y{ctx.OpCompositeConstruct( |
| 84 | ctx.F32[num_derivates], std::span{deriv_y_accum.data(), deriv_y_accum.size()})}; | 84 | ctx.F32[num_derivatives], std::span{deriv_y_accum.data(), deriv_y_accum.size()})}; |
| 85 | Add(spv::ImageOperandsMask::Grad, derivates_X, derivates_Y); | 85 | Add(spv::ImageOperandsMask::Grad, derivatives_X, derivatives_Y); |
| 86 | if (Sirit::ValidId(offset)) { | 86 | if (Sirit::ValidId(offset)) { |
| 87 | Add(spv::ImageOperandsMask::Offset, offset); | 87 | Add(spv::ImageOperandsMask::Offset, offset); |
| 88 | } | 88 | } |
| @@ -91,26 +91,26 @@ public: | |||
| 91 | } | 91 | } |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates_1, Id derivates_2, | 94 | explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2, |
| 95 | Id offset, Id lod_clamp) { | 95 | Id offset, Id lod_clamp) { |
| 96 | if (!Sirit::ValidId(derivates_1) || !Sirit::ValidId(derivates_2)) { | 96 | if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) { |
| 97 | throw LogicError("Derivates must be present"); | 97 | throw LogicError("Derivatives must be present"); |
| 98 | } | 98 | } |
| 99 | boost::container::static_vector<Id, 3> deriv_1_accum{ | 99 | boost::container::static_vector<Id, 3> deriv_1_accum{ |
| 100 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 0), | 100 | ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 0), |
| 101 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 2), | 101 | ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 2), |
| 102 | ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 0), | 102 | ctx.OpCompositeExtract(ctx.F32[1], derivatives_2, 0), |
| 103 | }; | 103 | }; |
| 104 | boost::container::static_vector<Id, 3> deriv_2_accum{ | 104 | boost::container::static_vector<Id, 3> deriv_2_accum{ |
| 105 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 1), | 105 | ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 1), |
| 106 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 3), | 106 | ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 3), |
| 107 | ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 1), | 107 | ctx.OpCompositeExtract(ctx.F32[1], derivatives_2, 1), |
| 108 | }; | 108 | }; |
| 109 | const Id derivates_id1{ctx.OpCompositeConstruct( | 109 | const Id derivatives_id1{ctx.OpCompositeConstruct( |
| 110 | ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})}; | 110 | ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})}; |
| 111 | const Id derivates_id2{ctx.OpCompositeConstruct( | 111 | const Id derivatives_id2{ctx.OpCompositeConstruct( |
| 112 | ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})}; | 112 | ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})}; |
| 113 | Add(spv::ImageOperandsMask::Grad, derivates_id1, derivates_id2); | 113 | Add(spv::ImageOperandsMask::Grad, derivatives_id1, derivatives_id2); |
| 114 | if (Sirit::ValidId(offset)) { | 114 | if (Sirit::ValidId(offset)) { |
| 115 | Add(spv::ImageOperandsMask::Offset, offset); | 115 | Add(spv::ImageOperandsMask::Offset, offset); |
| 116 | } | 116 | } |
| @@ -548,12 +548,12 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I | |||
| 548 | } | 548 | } |
| 549 | 549 | ||
| 550 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | 550 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
| 551 | Id derivates, Id offset, Id lod_clamp) { | 551 | Id derivatives, Id offset, Id lod_clamp) { |
| 552 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | 552 | const auto info{inst->Flags<IR::TextureInstInfo>()}; |
| 553 | const auto operands = | 553 | const auto operands = |
| 554 | info.num_derivates == 3 | 554 | info.num_derivatives == 3 |
| 555 | ? ImageOperands(ctx, info.has_lod_clamp != 0, derivates, offset, {}, lod_clamp) | 555 | ? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, offset, {}, lod_clamp) |
| 556 | : ImageOperands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, offset, | 556 | : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, info.num_derivatives, offset, |
| 557 | lod_clamp); | 557 | lod_clamp); |
| 558 | return Emit(&EmitContext::OpImageSparseSampleExplicitLod, | 558 | return Emit(&EmitContext::OpImageSparseSampleExplicitLod, |
| 559 | &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], | 559 | &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index a440b557d..7d34575c8 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h | |||
| @@ -543,7 +543,7 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i | |||
| 543 | const IR::Value& skip_mips); | 543 | const IR::Value& skip_mips); |
| 544 | Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); | 544 | Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); |
| 545 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | 545 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
| 546 | Id derivates, Id offset, Id lod_clamp); | 546 | Id derivatives, Id offset, Id lod_clamp); |
| 547 | Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); | 547 | Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); |
| 548 | void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color); | 548 | void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color); |
| 549 | Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index); | 549 | Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index); |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index b7caa4246..49171c470 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp | |||
| @@ -1864,11 +1864,11 @@ Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, Texture | |||
| 1864 | return Inst(op, Flags{info}, handle, coords); | 1864 | return Inst(op, Flags{info}, handle, coords); |
| 1865 | } | 1865 | } |
| 1866 | 1866 | ||
| 1867 | Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivates, | 1867 | Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivatives, |
| 1868 | const Value& offset, const F32& lod_clamp, TextureInstInfo info) { | 1868 | const Value& offset, const F32& lod_clamp, TextureInstInfo info) { |
| 1869 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGradient | 1869 | const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGradient |
| 1870 | : Opcode::BindlessImageGradient}; | 1870 | : Opcode::BindlessImageGradient}; |
| 1871 | return Inst(op, Flags{info}, handle, coords, derivates, offset, lod_clamp); | 1871 | return Inst(op, Flags{info}, handle, coords, derivatives, offset, lod_clamp); |
| 1872 | } | 1872 | } |
| 1873 | 1873 | ||
| 1874 | Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) { | 1874 | Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) { |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index f3c81dbe1..6c30897f4 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h | |||
| @@ -335,7 +335,7 @@ public: | |||
| 335 | [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset, | 335 | [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset, |
| 336 | const U32& lod, const U32& multisampling, TextureInstInfo info); | 336 | const U32& lod, const U32& multisampling, TextureInstInfo info); |
| 337 | [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, | 337 | [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, |
| 338 | const Value& derivates, const Value& offset, | 338 | const Value& derivatives, const Value& offset, |
| 339 | const F32& lod_clamp, TextureInstInfo info); | 339 | const F32& lod_clamp, TextureInstInfo info); |
| 340 | [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); | 340 | [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); |
| 341 | void ImageWrite(const Value& handle, const Value& coords, const Value& color, | 341 | void ImageWrite(const Value& handle, const Value& coords, const Value& color, |
diff --git a/src/shader_recompiler/frontend/ir/modifiers.h b/src/shader_recompiler/frontend/ir/modifiers.h index 1e9e8c8f5..c20c2401f 100644 --- a/src/shader_recompiler/frontend/ir/modifiers.h +++ b/src/shader_recompiler/frontend/ir/modifiers.h | |||
| @@ -40,7 +40,7 @@ union TextureInstInfo { | |||
| 40 | BitField<21, 1, u32> has_lod_clamp; | 40 | BitField<21, 1, u32> has_lod_clamp; |
| 41 | BitField<22, 1, u32> relaxed_precision; | 41 | BitField<22, 1, u32> relaxed_precision; |
| 42 | BitField<23, 2, u32> gather_component; | 42 | BitField<23, 2, u32> gather_component; |
| 43 | BitField<25, 2, u32> num_derivates; | 43 | BitField<25, 2, u32> num_derivatives; |
| 44 | BitField<27, 3, ImageFormat> image_format; | 44 | BitField<27, 3, ImageFormat> image_format; |
| 45 | BitField<30, 1, u32> ndv_is_active; | 45 | BitField<30, 1, u32> ndv_is_active; |
| 46 | }; | 46 | }; |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp index dd34507bc..4ce3dd0cd 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp | |||
| @@ -59,7 +59,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { | |||
| 59 | BitField<51, 3, IR::Pred> sparse_pred; | 59 | BitField<51, 3, IR::Pred> sparse_pred; |
| 60 | BitField<0, 8, IR::Reg> dest_reg; | 60 | BitField<0, 8, IR::Reg> dest_reg; |
| 61 | BitField<8, 8, IR::Reg> coord_reg; | 61 | BitField<8, 8, IR::Reg> coord_reg; |
| 62 | BitField<20, 8, IR::Reg> derivate_reg; | 62 | BitField<20, 8, IR::Reg> derivative_reg; |
| 63 | BitField<28, 3, TextureType> type; | 63 | BitField<28, 3, TextureType> type; |
| 64 | BitField<31, 4, u64> mask; | 64 | BitField<31, 4, u64> mask; |
| 65 | BitField<36, 13, u64> cbuf_offset; | 65 | BitField<36, 13, u64> cbuf_offset; |
| @@ -71,7 +71,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | IR::Value coords; | 73 | IR::Value coords; |
| 74 | u32 num_derivates{}; | 74 | u32 num_derivatives{}; |
| 75 | IR::Reg base_reg{txd.coord_reg}; | 75 | IR::Reg base_reg{txd.coord_reg}; |
| 76 | IR::Reg last_reg; | 76 | IR::Reg last_reg; |
| 77 | IR::Value handle; | 77 | IR::Value handle; |
| @@ -90,42 +90,42 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { | |||
| 90 | switch (txd.type) { | 90 | switch (txd.type) { |
| 91 | case TextureType::_1D: { | 91 | case TextureType::_1D: { |
| 92 | coords = v.F(base_reg); | 92 | coords = v.F(base_reg); |
| 93 | num_derivates = 1; | 93 | num_derivatives = 1; |
| 94 | last_reg = base_reg + 1; | 94 | last_reg = base_reg + 1; |
| 95 | break; | 95 | break; |
| 96 | } | 96 | } |
| 97 | case TextureType::ARRAY_1D: { | 97 | case TextureType::ARRAY_1D: { |
| 98 | last_reg = base_reg + 1; | 98 | last_reg = base_reg + 1; |
| 99 | coords = v.ir.CompositeConstruct(v.F(base_reg), read_array()); | 99 | coords = v.ir.CompositeConstruct(v.F(base_reg), read_array()); |
| 100 | num_derivates = 1; | 100 | num_derivatives = 1; |
| 101 | break; | 101 | break; |
| 102 | } | 102 | } |
| 103 | case TextureType::_2D: { | 103 | case TextureType::_2D: { |
| 104 | last_reg = base_reg + 2; | 104 | last_reg = base_reg + 2; |
| 105 | coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1)); | 105 | coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1)); |
| 106 | num_derivates = 2; | 106 | num_derivatives = 2; |
| 107 | break; | 107 | break; |
| 108 | } | 108 | } |
| 109 | case TextureType::ARRAY_2D: { | 109 | case TextureType::ARRAY_2D: { |
| 110 | last_reg = base_reg + 2; | 110 | last_reg = base_reg + 2; |
| 111 | coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1), read_array()); | 111 | coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1), read_array()); |
| 112 | num_derivates = 2; | 112 | num_derivatives = 2; |
| 113 | break; | 113 | break; |
| 114 | } | 114 | } |
| 115 | default: | 115 | default: |
| 116 | throw NotImplementedException("Invalid texture type"); | 116 | throw NotImplementedException("Invalid texture type"); |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | const IR::Reg derivate_reg{txd.derivate_reg}; | 119 | const IR::Reg derivative_reg{txd.derivative_reg}; |
| 120 | IR::Value derivates; | 120 | IR::Value derivatives; |
| 121 | switch (num_derivates) { | 121 | switch (num_derivatives) { |
| 122 | case 1: { | 122 | case 1: { |
| 123 | derivates = v.ir.CompositeConstruct(v.F(derivate_reg), v.F(derivate_reg + 1)); | 123 | derivatives = v.ir.CompositeConstruct(v.F(derivative_reg), v.F(derivative_reg + 1)); |
| 124 | break; | 124 | break; |
| 125 | } | 125 | } |
| 126 | case 2: { | 126 | case 2: { |
| 127 | derivates = v.ir.CompositeConstruct(v.F(derivate_reg), v.F(derivate_reg + 1), | 127 | derivatives = v.ir.CompositeConstruct(v.F(derivative_reg), v.F(derivative_reg + 1), |
| 128 | v.F(derivate_reg + 2), v.F(derivate_reg + 3)); | 128 | v.F(derivative_reg + 2), v.F(derivative_reg + 3)); |
| 129 | break; | 129 | break; |
| 130 | } | 130 | } |
| 131 | default: | 131 | default: |
| @@ -150,9 +150,10 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { | |||
| 150 | 150 | ||
| 151 | IR::TextureInstInfo info{}; | 151 | IR::TextureInstInfo info{}; |
| 152 | info.type.Assign(GetType(txd.type)); | 152 | info.type.Assign(GetType(txd.type)); |
| 153 | info.num_derivates.Assign(num_derivates); | 153 | info.num_derivatives.Assign(num_derivatives); |
| 154 | info.has_lod_clamp.Assign(has_lod_clamp ? 1 : 0); | 154 | info.has_lod_clamp.Assign(has_lod_clamp ? 1 : 0); |
| 155 | const IR::Value sample{v.ir.ImageGradient(handle, coords, derivates, offset, lod_clamp, info)}; | 155 | const IR::Value sample{ |
| 156 | v.ir.ImageGradient(handle, coords, derivatives, offset, lod_clamp, info)}; | ||
| 156 | 157 | ||
| 157 | IR::Reg dest_reg{txd.dest_reg}; | 158 | IR::Reg dest_reg{txd.dest_reg}; |
| 158 | for (size_t element = 0; element < 4; ++element) { | 159 | for (size_t element = 0; element < 4; ++element) { |
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 47183cae1..321ea625b 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp | |||
| @@ -310,6 +310,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo | |||
| 310 | } | 310 | } |
| 311 | Optimization::CollectShaderInfoPass(env, program); | 311 | Optimization::CollectShaderInfoPass(env, program); |
| 312 | Optimization::LayerPass(program, host_info); | 312 | Optimization::LayerPass(program, host_info); |
| 313 | Optimization::VendorWorkaroundPass(program); | ||
| 313 | 314 | ||
| 314 | CollectInterpolationInfo(env, program); | 315 | CollectInterpolationInfo(env, program); |
| 315 | AddNVNStorageBuffers(program); | 316 | AddNVNStorageBuffers(program); |
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp index f46e55122..ec12c843a 100644 --- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp | |||
| @@ -428,7 +428,7 @@ void FoldFPAdd32(IR::Inst& inst) { | |||
| 428 | } | 428 | } |
| 429 | } | 429 | } |
| 430 | 430 | ||
| 431 | bool FoldDerivateYFromCorrection(IR::Inst& inst) { | 431 | bool FoldDerivativeYFromCorrection(IR::Inst& inst) { |
| 432 | const IR::Value lhs_value{inst.Arg(0)}; | 432 | const IR::Value lhs_value{inst.Arg(0)}; |
| 433 | const IR::Value rhs_value{inst.Arg(1)}; | 433 | const IR::Value rhs_value{inst.Arg(1)}; |
| 434 | IR::Inst* const lhs_op{lhs_value.InstRecursive()}; | 434 | IR::Inst* const lhs_op{lhs_value.InstRecursive()}; |
| @@ -464,7 +464,7 @@ void FoldFPMul32(IR::Inst& inst) { | |||
| 464 | if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) { | 464 | if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) { |
| 465 | return; | 465 | return; |
| 466 | } | 466 | } |
| 467 | if (FoldDerivateYFromCorrection(inst)) { | 467 | if (FoldDerivativeYFromCorrection(inst)) { |
| 468 | return; | 468 | return; |
| 469 | } | 469 | } |
| 470 | IR::Inst* const lhs_op{lhs_value.InstRecursive()}; | 470 | IR::Inst* const lhs_op{lhs_value.InstRecursive()}; |
| @@ -699,7 +699,7 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) { | |||
| 699 | } | 699 | } |
| 700 | } | 700 | } |
| 701 | 701 | ||
| 702 | bool FindGradient3DDerivates(std::array<IR::Value, 3>& results, IR::Value coord) { | 702 | bool FindGradient3DDerivatives(std::array<IR::Value, 3>& results, IR::Value coord) { |
| 703 | if (coord.IsImmediate()) { | 703 | if (coord.IsImmediate()) { |
| 704 | return false; | 704 | return false; |
| 705 | } | 705 | } |
| @@ -834,7 +834,7 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) { | |||
| 834 | IR::Inst* const inst2 = coords.InstRecursive(); | 834 | IR::Inst* const inst2 = coords.InstRecursive(); |
| 835 | std::array<std::array<IR::Value, 3>, 3> results_matrix; | 835 | std::array<std::array<IR::Value, 3>, 3> results_matrix; |
| 836 | for (size_t i = 0; i < 3; i++) { | 836 | for (size_t i = 0; i < 3; i++) { |
| 837 | if (!FindGradient3DDerivates(results_matrix[i], inst2->Arg(i).Resolve())) { | 837 | if (!FindGradient3DDerivatives(results_matrix[i], inst2->Arg(i).Resolve())) { |
| 838 | return; | 838 | return; |
| 839 | } | 839 | } |
| 840 | } | 840 | } |
| @@ -852,7 +852,7 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) { | |||
| 852 | IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2], | 852 | IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2], |
| 853 | results_matrix[1][1], results_matrix[1][2]); | 853 | results_matrix[1][1], results_matrix[1][2]); |
| 854 | IR::Value derivatives_2 = ir.CompositeConstruct(results_matrix[2][1], results_matrix[2][2]); | 854 | IR::Value derivatives_2 = ir.CompositeConstruct(results_matrix[2][1], results_matrix[2][2]); |
| 855 | info.num_derivates.Assign(3); | 855 | info.num_derivatives.Assign(3); |
| 856 | IR::Value new_gradient_instruction = | 856 | IR::Value new_gradient_instruction = |
| 857 | ir.ImageGradient(handle, new_coords, derivatives_1, derivatives_2, lod_clamp, info); | 857 | ir.ImageGradient(handle, new_coords, derivatives_1, derivatives_2, lod_clamp, info); |
| 858 | IR::Inst* const new_inst = new_gradient_instruction.InstRecursive(); | 858 | IR::Inst* const new_inst = new_gradient_instruction.InstRecursive(); |
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index 7082fc5f2..1e637cb23 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h | |||
| @@ -26,6 +26,7 @@ void SsaRewritePass(IR::Program& program); | |||
| 26 | void PositionPass(Environment& env, IR::Program& program); | 26 | void PositionPass(Environment& env, IR::Program& program); |
| 27 | void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info); | 27 | void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info); |
| 28 | void LayerPass(IR::Program& program, const HostTranslateInfo& host_info); | 28 | void LayerPass(IR::Program& program, const HostTranslateInfo& host_info); |
| 29 | void VendorWorkaroundPass(IR::Program& program); | ||
| 29 | void VerificationPass(const IR::Program& program); | 30 | void VerificationPass(const IR::Program& program); |
| 30 | 31 | ||
| 31 | // Dual Vertex | 32 | // Dual Vertex |
diff --git a/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp b/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp new file mode 100644 index 000000000..08c658cb8 --- /dev/null +++ b/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 5 | #include "shader_recompiler/frontend/ir/ir_emitter.h" | ||
| 6 | #include "shader_recompiler/frontend/ir/value.h" | ||
| 7 | #include "shader_recompiler/ir_opt/passes.h" | ||
| 8 | |||
| 9 | namespace Shader::Optimization { | ||
| 10 | |||
| 11 | namespace { | ||
| 12 | void AddingByteSwapsWorkaround(IR::Block& block, IR::Inst& inst) { | ||
| 13 | /* | ||
| 14 | * Workaround for an NVIDIA bug seen in Super Mario RPG | ||
| 15 | * | ||
| 16 | * We are looking for this pattern: | ||
| 17 | * %lhs_bfe = BitFieldUExtract %factor_a, #0, #16 | ||
| 18 | * %lhs_mul = IMul32 %lhs_bfe, %factor_b // potentially optional? | ||
| 19 | * %lhs_shl = ShiftLeftLogical32 %lhs_mul, #16 | ||
| 20 | * %rhs_bfe = BitFieldUExtract %factor_a, #16, #16 | ||
| 21 | * %result = IAdd32 %lhs_shl, %rhs_bfe | ||
| 22 | * | ||
| 23 | * And replacing the IAdd32 with a BitwiseOr32 | ||
| 24 | * %result = BitwiseOr32 %lhs_shl, %rhs_bfe | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | IR::Inst* const lhs_shl{inst.Arg(0).TryInstRecursive()}; | ||
| 28 | IR::Inst* const rhs_bfe{inst.Arg(1).TryInstRecursive()}; | ||
| 29 | if (!lhs_shl || !rhs_bfe) { | ||
| 30 | return; | ||
| 31 | } | ||
| 32 | if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || | ||
| 33 | lhs_shl->Arg(1) != IR::Value{16U}) { | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | if (rhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract || rhs_bfe->Arg(1) != IR::Value{16U} || | ||
| 37 | rhs_bfe->Arg(2) != IR::Value{16U}) { | ||
| 38 | return; | ||
| 39 | } | ||
| 40 | IR::Inst* const lhs_mul{lhs_shl->Arg(0).TryInstRecursive()}; | ||
| 41 | if (!lhs_mul) { | ||
| 42 | return; | ||
| 43 | } | ||
| 44 | const bool lhs_mul_optional{lhs_mul->GetOpcode() == IR::Opcode::BitFieldUExtract}; | ||
| 45 | if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 && | ||
| 46 | lhs_mul->GetOpcode() != IR::Opcode::BitFieldUExtract) { | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | IR::Inst* const lhs_bfe{lhs_mul_optional ? lhs_mul : lhs_mul->Arg(0).TryInstRecursive()}; | ||
| 50 | if (!lhs_bfe) { | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) { | ||
| 54 | return; | ||
| 55 | } | ||
| 56 | if (lhs_bfe->Arg(1) != IR::Value{0U} || lhs_bfe->Arg(2) != IR::Value{16U}) { | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; | ||
| 60 | inst.ReplaceUsesWith(ir.BitwiseOr(IR::U32{inst.Arg(0)}, IR::U32{inst.Arg(1)})); | ||
| 61 | } | ||
| 62 | |||
| 63 | } // Anonymous namespace | ||
| 64 | |||
| 65 | void VendorWorkaroundPass(IR::Program& program) { | ||
| 66 | for (IR::Block* const block : program.post_order_blocks) { | ||
| 67 | for (IR::Inst& inst : block->Instructions()) { | ||
| 68 | switch (inst.GetOpcode()) { | ||
| 69 | case IR::Opcode::IAdd32: | ||
| 70 | AddingByteSwapsWorkaround(*block, inst); | ||
| 71 | break; | ||
| 72 | default: | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | } // namespace Shader::Optimization | ||
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index e62ba8a20..66901a965 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h | |||
| @@ -43,6 +43,7 @@ struct Profile { | |||
| 43 | bool support_gl_sparse_textures{}; | 43 | bool support_gl_sparse_textures{}; |
| 44 | bool support_gl_derivative_control{}; | 44 | bool support_gl_derivative_control{}; |
| 45 | bool support_scaled_attributes{}; | 45 | bool support_scaled_attributes{}; |
| 46 | bool support_multi_viewport{}; | ||
| 46 | 47 | ||
| 47 | bool warp_size_potentially_larger_than_guest{}; | 48 | bool warp_size_potentially_larger_than_guest{}; |
| 48 | 49 | ||
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index cf9266d54..c22c7631c 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | add_subdirectory(host_shaders) | 4 | add_subdirectory(host_shaders) |
| 5 | 5 | ||
| 6 | if(LIBVA_FOUND) | 6 | if(LIBVA_FOUND) |
| 7 | set_source_files_properties(host1x/codecs/codec.cpp | 7 | set_source_files_properties(host1x/ffmpeg/ffmpeg.cpp |
| 8 | PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) | 8 | PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) |
| 9 | list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES}) | 9 | list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES}) |
| 10 | endif() | 10 | endif() |
| @@ -15,6 +15,7 @@ add_library(video_core STATIC | |||
| 15 | buffer_cache/buffer_cache.cpp | 15 | buffer_cache/buffer_cache.cpp |
| 16 | buffer_cache/buffer_cache.h | 16 | buffer_cache/buffer_cache.h |
| 17 | buffer_cache/memory_tracker_base.h | 17 | buffer_cache/memory_tracker_base.h |
| 18 | buffer_cache/usage_tracker.h | ||
| 18 | buffer_cache/word_manager.h | 19 | buffer_cache/word_manager.h |
| 19 | cache_types.h | 20 | cache_types.h |
| 20 | cdma_pusher.cpp | 21 | cdma_pusher.cpp |
| @@ -66,6 +67,8 @@ add_library(video_core STATIC | |||
| 66 | host1x/codecs/vp9.cpp | 67 | host1x/codecs/vp9.cpp |
| 67 | host1x/codecs/vp9.h | 68 | host1x/codecs/vp9.h |
| 68 | host1x/codecs/vp9_types.h | 69 | host1x/codecs/vp9_types.h |
| 70 | host1x/ffmpeg/ffmpeg.cpp | ||
| 71 | host1x/ffmpeg/ffmpeg.h | ||
| 69 | host1x/control.cpp | 72 | host1x/control.cpp |
| 70 | host1x/control.h | 73 | host1x/control.h |
| 71 | host1x/host1x.cpp | 74 | host1x/host1x.cpp |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 2648970b6..6d1fc3887 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -67,6 +67,7 @@ void BufferCache<P>::TickFrame() { | |||
| 67 | if (!channel_state) { | 67 | if (!channel_state) { |
| 68 | return; | 68 | return; |
| 69 | } | 69 | } |
| 70 | runtime.TickFrame(slot_buffers); | ||
| 70 | 71 | ||
| 71 | // Calculate hits and shots and move hit bits to the right | 72 | // Calculate hits and shots and move hit bits to the right |
| 72 | const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(), | 73 | const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(), |
| @@ -230,7 +231,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am | |||
| 230 | for (const IntervalType& add_interval : tmp_intervals) { | 231 | for (const IntervalType& add_interval : tmp_intervals) { |
| 231 | common_ranges.add(add_interval); | 232 | common_ranges.add(add_interval); |
| 232 | } | 233 | } |
| 233 | runtime.CopyBuffer(dest_buffer, src_buffer, copies); | 234 | const auto& copy = copies[0]; |
| 235 | src_buffer.MarkUsage(copy.src_offset, copy.size); | ||
| 236 | dest_buffer.MarkUsage(copy.dst_offset, copy.size); | ||
| 237 | runtime.CopyBuffer(dest_buffer, src_buffer, copies, true); | ||
| 234 | if (has_new_downloads) { | 238 | if (has_new_downloads) { |
| 235 | memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); | 239 | memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); |
| 236 | } | 240 | } |
| @@ -258,9 +262,10 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) { | |||
| 258 | common_ranges.subtract(subtract_interval); | 262 | common_ranges.subtract(subtract_interval); |
| 259 | 263 | ||
| 260 | const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size)); | 264 | const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size)); |
| 261 | auto& dest_buffer = slot_buffers[buffer]; | 265 | Buffer& dest_buffer = slot_buffers[buffer]; |
| 262 | const u32 offset = dest_buffer.Offset(*cpu_dst_address); | 266 | const u32 offset = dest_buffer.Offset(*cpu_dst_address); |
| 263 | runtime.ClearBuffer(dest_buffer, offset, size, value); | 267 | runtime.ClearBuffer(dest_buffer, offset, size, value); |
| 268 | dest_buffer.MarkUsage(offset, size); | ||
| 264 | return true; | 269 | return true; |
| 265 | } | 270 | } |
| 266 | 271 | ||
| @@ -603,6 +608,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { | |||
| 603 | VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset); | 608 | VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset); |
| 604 | const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size}; | 609 | const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size}; |
| 605 | async_downloads += std::make_pair(base_interval, 1); | 610 | async_downloads += std::make_pair(base_interval, 1); |
| 611 | buffer.MarkUsage(copy.src_offset, copy.size); | ||
| 606 | runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); | 612 | runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); |
| 607 | normalized_copies.push_back(second_copy); | 613 | normalized_copies.push_back(second_copy); |
| 608 | } | 614 | } |
| @@ -621,8 +627,9 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { | |||
| 621 | // Have in mind the staging buffer offset for the copy | 627 | // Have in mind the staging buffer offset for the copy |
| 622 | copy.dst_offset += download_staging.offset; | 628 | copy.dst_offset += download_staging.offset; |
| 623 | const std::array copies{copy}; | 629 | const std::array copies{copy}; |
| 624 | runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies, | 630 | Buffer& buffer = slot_buffers[buffer_id]; |
| 625 | false); | 631 | buffer.MarkUsage(copy.src_offset, copy.size); |
| 632 | runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); | ||
| 626 | } | 633 | } |
| 627 | runtime.PostCopyBarrier(); | 634 | runtime.PostCopyBarrier(); |
| 628 | runtime.Finish(); | 635 | runtime.Finish(); |
| @@ -742,7 +749,7 @@ void BufferCache<P>::BindHostIndexBuffer() { | |||
| 742 | {BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}}; | 749 | {BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}}; |
| 743 | std::memcpy(upload_staging.mapped_span.data(), | 750 | std::memcpy(upload_staging.mapped_span.data(), |
| 744 | draw_state.inline_index_draw_indexes.data(), size); | 751 | draw_state.inline_index_draw_indexes.data(), size); |
| 745 | runtime.CopyBuffer(buffer, upload_staging.buffer, copies); | 752 | runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true); |
| 746 | } else { | 753 | } else { |
| 747 | buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes); | 754 | buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes); |
| 748 | } | 755 | } |
| @@ -754,6 +761,7 @@ void BufferCache<P>::BindHostIndexBuffer() { | |||
| 754 | offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes(); | 761 | offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes(); |
| 755 | runtime.BindIndexBuffer(buffer, new_offset, size); | 762 | runtime.BindIndexBuffer(buffer, new_offset, size); |
| 756 | } else { | 763 | } else { |
| 764 | buffer.MarkUsage(offset, size); | ||
| 757 | runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, | 765 | runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, |
| 758 | draw_state.index_buffer.first, draw_state.index_buffer.count, | 766 | draw_state.index_buffer.first, draw_state.index_buffer.count, |
| 759 | buffer, offset, size); | 767 | buffer, offset, size); |
| @@ -790,6 +798,7 @@ void BufferCache<P>::BindHostVertexBuffers() { | |||
| 790 | 798 | ||
| 791 | const u32 stride = maxwell3d->regs.vertex_streams[index].stride; | 799 | const u32 stride = maxwell3d->regs.vertex_streams[index].stride; |
| 792 | const u32 offset = buffer.Offset(binding.cpu_addr); | 800 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 801 | buffer.MarkUsage(offset, binding.size); | ||
| 793 | 802 | ||
| 794 | host_bindings.buffers.push_back(&buffer); | 803 | host_bindings.buffers.push_back(&buffer); |
| 795 | host_bindings.offsets.push_back(offset); | 804 | host_bindings.offsets.push_back(offset); |
| @@ -895,6 +904,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 | |||
| 895 | if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { | 904 | if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { |
| 896 | channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; | 905 | channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; |
| 897 | } | 906 | } |
| 907 | buffer.MarkUsage(offset, size); | ||
| 898 | if constexpr (NEEDS_BIND_UNIFORM_INDEX) { | 908 | if constexpr (NEEDS_BIND_UNIFORM_INDEX) { |
| 899 | runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size); | 909 | runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size); |
| 900 | } else { | 910 | } else { |
| @@ -913,6 +923,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) { | |||
| 913 | SynchronizeBuffer(buffer, binding.cpu_addr, size); | 923 | SynchronizeBuffer(buffer, binding.cpu_addr, size); |
| 914 | 924 | ||
| 915 | const u32 offset = buffer.Offset(binding.cpu_addr); | 925 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 926 | buffer.MarkUsage(offset, size); | ||
| 916 | const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; | 927 | const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; |
| 917 | 928 | ||
| 918 | if (is_written) { | 929 | if (is_written) { |
| @@ -943,6 +954,7 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) { | |||
| 943 | 954 | ||
| 944 | const u32 offset = buffer.Offset(binding.cpu_addr); | 955 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 945 | const PixelFormat format = binding.format; | 956 | const PixelFormat format = binding.format; |
| 957 | buffer.MarkUsage(offset, size); | ||
| 946 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { | 958 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { |
| 947 | if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) { | 959 | if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) { |
| 948 | runtime.BindImageBuffer(buffer, offset, size, format); | 960 | runtime.BindImageBuffer(buffer, offset, size, format); |
| @@ -975,9 +987,10 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() { | |||
| 975 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); | 987 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); |
| 976 | 988 | ||
| 977 | const u32 offset = buffer.Offset(binding.cpu_addr); | 989 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 990 | buffer.MarkUsage(offset, size); | ||
| 978 | host_bindings.buffers.push_back(&buffer); | 991 | host_bindings.buffers.push_back(&buffer); |
| 979 | host_bindings.offsets.push_back(offset); | 992 | host_bindings.offsets.push_back(offset); |
| 980 | host_bindings.sizes.push_back(binding.size); | 993 | host_bindings.sizes.push_back(size); |
| 981 | } | 994 | } |
| 982 | if (host_bindings.buffers.size() > 0) { | 995 | if (host_bindings.buffers.size() > 0) { |
| 983 | runtime.BindTransformFeedbackBuffers(host_bindings); | 996 | runtime.BindTransformFeedbackBuffers(host_bindings); |
| @@ -1001,6 +1014,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() { | |||
| 1001 | SynchronizeBuffer(buffer, binding.cpu_addr, size); | 1014 | SynchronizeBuffer(buffer, binding.cpu_addr, size); |
| 1002 | 1015 | ||
| 1003 | const u32 offset = buffer.Offset(binding.cpu_addr); | 1016 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 1017 | buffer.MarkUsage(offset, size); | ||
| 1004 | if constexpr (NEEDS_BIND_UNIFORM_INDEX) { | 1018 | if constexpr (NEEDS_BIND_UNIFORM_INDEX) { |
| 1005 | runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size); | 1019 | runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size); |
| 1006 | ++binding_index; | 1020 | ++binding_index; |
| @@ -1021,6 +1035,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() { | |||
| 1021 | SynchronizeBuffer(buffer, binding.cpu_addr, size); | 1035 | SynchronizeBuffer(buffer, binding.cpu_addr, size); |
| 1022 | 1036 | ||
| 1023 | const u32 offset = buffer.Offset(binding.cpu_addr); | 1037 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 1038 | buffer.MarkUsage(offset, size); | ||
| 1024 | const bool is_written = | 1039 | const bool is_written = |
| 1025 | ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; | 1040 | ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; |
| 1026 | 1041 | ||
| @@ -1053,6 +1068,7 @@ void BufferCache<P>::BindHostComputeTextureBuffers() { | |||
| 1053 | 1068 | ||
| 1054 | const u32 offset = buffer.Offset(binding.cpu_addr); | 1069 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 1055 | const PixelFormat format = binding.format; | 1070 | const PixelFormat format = binding.format; |
| 1071 | buffer.MarkUsage(offset, size); | ||
| 1056 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { | 1072 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { |
| 1057 | if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) { | 1073 | if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) { |
| 1058 | runtime.BindImageBuffer(buffer, offset, size, format); | 1074 | runtime.BindImageBuffer(buffer, offset, size, format); |
| @@ -1172,10 +1188,11 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) { | |||
| 1172 | if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { | 1188 | if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { |
| 1173 | size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); | 1189 | size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); |
| 1174 | } | 1190 | } |
| 1191 | const BufferId buffer_id = FindBuffer(*cpu_addr, size); | ||
| 1175 | channel_state->vertex_buffers[index] = Binding{ | 1192 | channel_state->vertex_buffers[index] = Binding{ |
| 1176 | .cpu_addr = *cpu_addr, | 1193 | .cpu_addr = *cpu_addr, |
| 1177 | .size = size, | 1194 | .size = size, |
| 1178 | .buffer_id = FindBuffer(*cpu_addr, size), | 1195 | .buffer_id = buffer_id, |
| 1179 | }; | 1196 | }; |
| 1180 | } | 1197 | } |
| 1181 | 1198 | ||
| @@ -1192,11 +1209,6 @@ void BufferCache<P>::UpdateDrawIndirect() { | |||
| 1192 | .size = static_cast<u32>(size), | 1209 | .size = static_cast<u32>(size), |
| 1193 | .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)), | 1210 | .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)), |
| 1194 | }; | 1211 | }; |
| 1195 | VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64); | ||
| 1196 | VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64); | ||
| 1197 | IntervalType interval{cpu_addr_start, cpu_addr_end}; | ||
| 1198 | ClearDownload(interval); | ||
| 1199 | common_ranges.subtract(interval); | ||
| 1200 | }; | 1212 | }; |
| 1201 | if (current_draw_indirect->include_count) { | 1213 | if (current_draw_indirect->include_count) { |
| 1202 | update(current_draw_indirect->count_start_address, sizeof(u32), | 1214 | update(current_draw_indirect->count_start_address, sizeof(u32), |
| @@ -1406,7 +1418,8 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, | |||
| 1406 | .dst_offset = dst_base_offset, | 1418 | .dst_offset = dst_base_offset, |
| 1407 | .size = overlap.SizeBytes(), | 1419 | .size = overlap.SizeBytes(), |
| 1408 | }); | 1420 | }); |
| 1409 | runtime.CopyBuffer(new_buffer, overlap, copies); | 1421 | new_buffer.MarkUsage(copies[0].dst_offset, copies[0].size); |
| 1422 | runtime.CopyBuffer(new_buffer, overlap, copies, true); | ||
| 1410 | DeleteBuffer(overlap_id, true); | 1423 | DeleteBuffer(overlap_id, true); |
| 1411 | } | 1424 | } |
| 1412 | 1425 | ||
| @@ -1419,7 +1432,9 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) { | |||
| 1419 | const u32 size = static_cast<u32>(overlap.end - overlap.begin); | 1432 | const u32 size = static_cast<u32>(overlap.end - overlap.begin); |
| 1420 | const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); | 1433 | const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); |
| 1421 | auto& new_buffer = slot_buffers[new_buffer_id]; | 1434 | auto& new_buffer = slot_buffers[new_buffer_id]; |
| 1422 | runtime.ClearBuffer(new_buffer, 0, new_buffer.SizeBytes(), 0); | 1435 | const size_t size_bytes = new_buffer.SizeBytes(); |
| 1436 | runtime.ClearBuffer(new_buffer, 0, size_bytes, 0); | ||
| 1437 | new_buffer.MarkUsage(0, size_bytes); | ||
| 1423 | for (const BufferId overlap_id : overlap.ids) { | 1438 | for (const BufferId overlap_id : overlap.ids) { |
| 1424 | JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); | 1439 | JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); |
| 1425 | } | 1440 | } |
| @@ -1472,11 +1487,6 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept { | |||
| 1472 | 1487 | ||
| 1473 | template <class P> | 1488 | template <class P> |
| 1474 | bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { | 1489 | bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { |
| 1475 | return SynchronizeBufferImpl(buffer, cpu_addr, size); | ||
| 1476 | } | ||
| 1477 | |||
| 1478 | template <class P> | ||
| 1479 | bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) { | ||
| 1480 | boost::container::small_vector<BufferCopy, 4> copies; | 1490 | boost::container::small_vector<BufferCopy, 4> copies; |
| 1481 | u64 total_size_bytes = 0; | 1491 | u64 total_size_bytes = 0; |
| 1482 | u64 largest_copy = 0; | 1492 | u64 largest_copy = 0; |
| @@ -1499,51 +1509,6 @@ bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s | |||
| 1499 | } | 1509 | } |
| 1500 | 1510 | ||
| 1501 | template <class P> | 1511 | template <class P> |
| 1502 | bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size) { | ||
| 1503 | boost::container::small_vector<BufferCopy, 4> copies; | ||
| 1504 | u64 total_size_bytes = 0; | ||
| 1505 | u64 largest_copy = 0; | ||
| 1506 | IntervalSet found_sets{}; | ||
| 1507 | auto make_copies = [&] { | ||
| 1508 | for (auto& interval : found_sets) { | ||
| 1509 | const std::size_t sub_size = interval.upper() - interval.lower(); | ||
| 1510 | const VAddr cpu_addr_ = interval.lower(); | ||
| 1511 | copies.push_back(BufferCopy{ | ||
| 1512 | .src_offset = total_size_bytes, | ||
| 1513 | .dst_offset = cpu_addr_ - buffer.CpuAddr(), | ||
| 1514 | .size = sub_size, | ||
| 1515 | }); | ||
| 1516 | total_size_bytes += sub_size; | ||
| 1517 | largest_copy = std::max<u64>(largest_copy, sub_size); | ||
| 1518 | } | ||
| 1519 | const std::span<BufferCopy> copies_span(copies.data(), copies.size()); | ||
| 1520 | UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); | ||
| 1521 | }; | ||
| 1522 | memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) { | ||
| 1523 | const VAddr base_adr = cpu_addr_out; | ||
| 1524 | const VAddr end_adr = base_adr + range_size; | ||
| 1525 | const IntervalType add_interval{base_adr, end_adr}; | ||
| 1526 | found_sets.add(add_interval); | ||
| 1527 | }); | ||
| 1528 | if (found_sets.empty()) { | ||
| 1529 | return true; | ||
| 1530 | } | ||
| 1531 | const IntervalType search_interval{cpu_addr, cpu_addr + size}; | ||
| 1532 | auto it = common_ranges.lower_bound(search_interval); | ||
| 1533 | auto it_end = common_ranges.upper_bound(search_interval); | ||
| 1534 | if (it == common_ranges.end()) { | ||
| 1535 | make_copies(); | ||
| 1536 | return false; | ||
| 1537 | } | ||
| 1538 | while (it != it_end) { | ||
| 1539 | found_sets.subtract(*it); | ||
| 1540 | it++; | ||
| 1541 | } | ||
| 1542 | make_copies(); | ||
| 1543 | return false; | ||
| 1544 | } | ||
| 1545 | |||
| 1546 | template <class P> | ||
| 1547 | void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, | 1512 | void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, |
| 1548 | std::span<BufferCopy> copies) { | 1513 | std::span<BufferCopy> copies) { |
| 1549 | if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { | 1514 | if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { |
| @@ -1591,7 +1556,8 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer, | |||
| 1591 | // Apply the staging offset | 1556 | // Apply the staging offset |
| 1592 | copy.src_offset += upload_staging.offset; | 1557 | copy.src_offset += upload_staging.offset; |
| 1593 | } | 1558 | } |
| 1594 | runtime.CopyBuffer(buffer, upload_staging.buffer, copies); | 1559 | const bool can_reorder = runtime.CanReorderUpload(buffer, copies); |
| 1560 | runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder); | ||
| 1595 | } | 1561 | } |
| 1596 | } | 1562 | } |
| 1597 | 1563 | ||
| @@ -1633,7 +1599,8 @@ void BufferCache<P>::InlineMemoryImplementation(VAddr dest_address, size_t copy_ | |||
| 1633 | }}; | 1599 | }}; |
| 1634 | u8* const src_pointer = upload_staging.mapped_span.data(); | 1600 | u8* const src_pointer = upload_staging.mapped_span.data(); |
| 1635 | std::memcpy(src_pointer, inlined_buffer.data(), copy_size); | 1601 | std::memcpy(src_pointer, inlined_buffer.data(), copy_size); |
| 1636 | runtime.CopyBuffer(buffer, upload_staging.buffer, copies); | 1602 | const bool can_reorder = runtime.CanReorderUpload(buffer, copies); |
| 1603 | runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder); | ||
| 1637 | } else { | 1604 | } else { |
| 1638 | buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); | 1605 | buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); |
| 1639 | } | 1606 | } |
| @@ -1686,8 +1653,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si | |||
| 1686 | for (BufferCopy& copy : copies) { | 1653 | for (BufferCopy& copy : copies) { |
| 1687 | // Modify copies to have the staging offset in mind | 1654 | // Modify copies to have the staging offset in mind |
| 1688 | copy.dst_offset += download_staging.offset; | 1655 | copy.dst_offset += download_staging.offset; |
| 1656 | buffer.MarkUsage(copy.src_offset, copy.size); | ||
| 1689 | } | 1657 | } |
| 1690 | runtime.CopyBuffer(download_staging.buffer, buffer, copies_span); | 1658 | runtime.CopyBuffer(download_staging.buffer, buffer, copies_span, true); |
| 1691 | runtime.Finish(); | 1659 | runtime.Finish(); |
| 1692 | for (const BufferCopy& copy : copies) { | 1660 | for (const BufferCopy& copy : copies) { |
| 1693 | const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset; | 1661 | const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset; |
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index eed267361..d6d696d8c 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h | |||
| @@ -529,10 +529,6 @@ private: | |||
| 529 | 529 | ||
| 530 | bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); | 530 | bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); |
| 531 | 531 | ||
| 532 | bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size); | ||
| 533 | |||
| 534 | bool SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size); | ||
| 535 | |||
| 536 | void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, | 532 | void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, |
| 537 | std::span<BufferCopy> copies); | 533 | std::span<BufferCopy> copies); |
| 538 | 534 | ||
diff --git a/src/video_core/buffer_cache/usage_tracker.h b/src/video_core/buffer_cache/usage_tracker.h new file mode 100644 index 000000000..ab05fe415 --- /dev/null +++ b/src/video_core/buffer_cache/usage_tracker.h | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/alignment.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace VideoCommon { | ||
| 10 | |||
| 11 | class UsageTracker { | ||
| 12 | static constexpr size_t BYTES_PER_BIT_SHIFT = 6; | ||
| 13 | static constexpr size_t PAGE_SHIFT = 6 + BYTES_PER_BIT_SHIFT; | ||
| 14 | static constexpr size_t PAGE_BYTES = 1 << PAGE_SHIFT; | ||
| 15 | |||
| 16 | public: | ||
| 17 | explicit UsageTracker(size_t size) { | ||
| 18 | const size_t num_pages = (size >> PAGE_SHIFT) + 1; | ||
| 19 | pages.resize(num_pages, 0ULL); | ||
| 20 | } | ||
| 21 | |||
| 22 | void Reset() noexcept { | ||
| 23 | std::ranges::fill(pages, 0ULL); | ||
| 24 | } | ||
| 25 | |||
| 26 | void Track(u64 offset, u64 size) noexcept { | ||
| 27 | const size_t page = offset >> PAGE_SHIFT; | ||
| 28 | const size_t page_end = (offset + size) >> PAGE_SHIFT; | ||
| 29 | TrackPage(page, offset, size); | ||
| 30 | if (page == page_end) { | ||
| 31 | return; | ||
| 32 | } | ||
| 33 | for (size_t i = page + 1; i < page_end; i++) { | ||
| 34 | pages[i] = ~u64{0}; | ||
| 35 | } | ||
| 36 | const size_t offset_end = offset + size; | ||
| 37 | const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES); | ||
| 38 | TrackPage(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned); | ||
| 39 | } | ||
| 40 | |||
| 41 | [[nodiscard]] bool IsUsed(u64 offset, u64 size) const noexcept { | ||
| 42 | const size_t page = offset >> PAGE_SHIFT; | ||
| 43 | const size_t page_end = (offset + size) >> PAGE_SHIFT; | ||
| 44 | if (IsPageUsed(page, offset, size)) { | ||
| 45 | return true; | ||
| 46 | } | ||
| 47 | for (size_t i = page + 1; i < page_end; i++) { | ||
| 48 | if (pages[i] != 0) { | ||
| 49 | return true; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | const size_t offset_end = offset + size; | ||
| 53 | const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES); | ||
| 54 | return IsPageUsed(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned); | ||
| 55 | } | ||
| 56 | |||
| 57 | private: | ||
| 58 | void TrackPage(u64 page, u64 offset, u64 size) noexcept { | ||
| 59 | const size_t offset_in_page = offset % PAGE_BYTES; | ||
| 60 | const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT; | ||
| 61 | const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT; | ||
| 62 | const size_t mask = ~u64{0} >> (64 - num_bits); | ||
| 63 | pages[page] |= (~u64{0} & mask) << first_bit; | ||
| 64 | } | ||
| 65 | |||
| 66 | bool IsPageUsed(u64 page, u64 offset, u64 size) const noexcept { | ||
| 67 | const size_t offset_in_page = offset % PAGE_BYTES; | ||
| 68 | const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT; | ||
| 69 | const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT; | ||
| 70 | const size_t mask = ~u64{0} >> (64 - num_bits); | ||
| 71 | const size_t mask2 = (~u64{0} & mask) << first_bit; | ||
| 72 | return (pages[page] & mask2) != 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | private: | ||
| 76 | std::vector<u64> pages; | ||
| 77 | }; | ||
| 78 | |||
| 79 | } // namespace VideoCommon | ||
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 02e161270..91f10aec2 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp | |||
| @@ -72,7 +72,7 @@ void Fermi2D::Blit() { | |||
| 72 | UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled"); | 72 | UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled"); |
| 73 | 73 | ||
| 74 | const auto& args = regs.pixels_from_memory; | 74 | const auto& args = regs.pixels_from_memory; |
| 75 | constexpr s64 null_derivate = 1ULL << 32; | 75 | constexpr s64 null_derivative = 1ULL << 32; |
| 76 | Surface src = regs.src; | 76 | Surface src = regs.src; |
| 77 | const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); | 77 | const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); |
| 78 | const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 && | 78 | const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 && |
| @@ -89,7 +89,7 @@ void Fermi2D::Blit() { | |||
| 89 | .operation = regs.operation, | 89 | .operation = regs.operation, |
| 90 | .filter = args.sample_mode.filter, | 90 | .filter = args.sample_mode.filter, |
| 91 | .must_accelerate = | 91 | .must_accelerate = |
| 92 | args.du_dx != null_derivate || args.dv_dy != null_derivate || delegate_to_gpu, | 92 | args.du_dx != null_derivative || args.dv_dy != null_derivative || delegate_to_gpu, |
| 93 | .dst_x0 = args.dst_x0, | 93 | .dst_x0 = args.dst_x0, |
| 94 | .dst_y0 = args.dst_y0, | 94 | .dst_y0 = args.dst_y0, |
| 95 | .dst_x1 = args.dst_x0 + args.dst_width, | 95 | .dst_x1 = args.dst_x0 + args.dst_width, |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 32d767d85..592c28ba3 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -268,7 +268,7 @@ size_t Maxwell3D::EstimateIndexBufferSize() { | |||
| 268 | std::numeric_limits<u32>::max()}; | 268 | std::numeric_limits<u32>::max()}; |
| 269 | const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); | 269 | const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); |
| 270 | const size_t log2_byte_size = Common::Log2Ceil64(byte_size); | 270 | const size_t log2_byte_size = Common::Log2Ceil64(byte_size); |
| 271 | const size_t cap{GetMaxCurrentVertices() * 3 * byte_size}; | 271 | const size_t cap{GetMaxCurrentVertices() * 4 * byte_size}; |
| 272 | const size_t lower_cap = | 272 | const size_t lower_cap = |
| 273 | std::min<size_t>(static_cast<size_t>(end_address - start_address), cap); | 273 | std::min<size_t>(static_cast<size_t>(end_address - start_address), cap); |
| 274 | return std::min<size_t>( | 274 | return std::min<size_t>( |
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index c0e6471fe..805a89900 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h | |||
| @@ -86,10 +86,7 @@ public: | |||
| 86 | uncommitted_operations.emplace_back(std::move(func)); | 86 | uncommitted_operations.emplace_back(std::move(func)); |
| 87 | } | 87 | } |
| 88 | pending_operations.emplace_back(std::move(uncommitted_operations)); | 88 | pending_operations.emplace_back(std::move(uncommitted_operations)); |
| 89 | { | 89 | QueueFence(new_fence); |
| 90 | std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; | ||
| 91 | QueueFence(new_fence); | ||
| 92 | } | ||
| 93 | if (!delay_fence) { | 90 | if (!delay_fence) { |
| 94 | func(); | 91 | func(); |
| 95 | } | 92 | } |
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp index dbcf508e5..1030db681 100644 --- a/src/video_core/host1x/codecs/codec.cpp +++ b/src/video_core/host1x/codecs/codec.cpp | |||
| @@ -1,11 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | ||
| 5 | #include <fstream> | ||
| 6 | #include <vector> | ||
| 7 | #include "common/assert.h" | 4 | #include "common/assert.h" |
| 8 | #include "common/scope_exit.h" | ||
| 9 | #include "common/settings.h" | 5 | #include "common/settings.h" |
| 10 | #include "video_core/host1x/codecs/codec.h" | 6 | #include "video_core/host1x/codecs/codec.h" |
| 11 | #include "video_core/host1x/codecs/h264.h" | 7 | #include "video_core/host1x/codecs/h264.h" |
| @@ -14,242 +10,17 @@ | |||
| 14 | #include "video_core/host1x/host1x.h" | 10 | #include "video_core/host1x/host1x.h" |
| 15 | #include "video_core/memory_manager.h" | 11 | #include "video_core/memory_manager.h" |
| 16 | 12 | ||
| 17 | extern "C" { | ||
| 18 | #include <libavfilter/buffersink.h> | ||
| 19 | #include <libavfilter/buffersrc.h> | ||
| 20 | #include <libavutil/opt.h> | ||
| 21 | #ifdef LIBVA_FOUND | ||
| 22 | // for querying VAAPI driver information | ||
| 23 | #include <libavutil/hwcontext_vaapi.h> | ||
| 24 | #endif | ||
| 25 | } | ||
| 26 | |||
| 27 | namespace Tegra { | 13 | namespace Tegra { |
| 28 | namespace { | ||
| 29 | constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; | ||
| 30 | constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; | ||
| 31 | constexpr std::array PREFERRED_GPU_DECODERS = { | ||
| 32 | AV_HWDEVICE_TYPE_CUDA, | ||
| 33 | #ifdef _WIN32 | ||
| 34 | AV_HWDEVICE_TYPE_D3D11VA, | ||
| 35 | AV_HWDEVICE_TYPE_DXVA2, | ||
| 36 | #elif defined(__unix__) | ||
| 37 | AV_HWDEVICE_TYPE_VAAPI, | ||
| 38 | AV_HWDEVICE_TYPE_VDPAU, | ||
| 39 | #endif | ||
| 40 | // last resort for Linux Flatpak (w/ NVIDIA) | ||
| 41 | AV_HWDEVICE_TYPE_VULKAN, | ||
| 42 | }; | ||
| 43 | |||
| 44 | void AVPacketDeleter(AVPacket* ptr) { | ||
| 45 | av_packet_free(&ptr); | ||
| 46 | } | ||
| 47 | |||
| 48 | using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&AVPacketDeleter)>; | ||
| 49 | |||
| 50 | AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) { | ||
| 51 | for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { | ||
| 52 | if (*p == av_codec_ctx->pix_fmt) { | ||
| 53 | return av_codec_ctx->pix_fmt; | ||
| 54 | } | ||
| 55 | } | ||
| 56 | LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); | ||
| 57 | av_buffer_unref(&av_codec_ctx->hw_device_ctx); | ||
| 58 | av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; | ||
| 59 | return PREFERRED_CPU_FMT; | ||
| 60 | } | ||
| 61 | |||
| 62 | // List all the currently available hwcontext in ffmpeg | ||
| 63 | std::vector<AVHWDeviceType> ListSupportedContexts() { | ||
| 64 | std::vector<AVHWDeviceType> contexts{}; | ||
| 65 | AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; | ||
| 66 | do { | ||
| 67 | current_device_type = av_hwdevice_iterate_types(current_device_type); | ||
| 68 | contexts.push_back(current_device_type); | ||
| 69 | } while (current_device_type != AV_HWDEVICE_TYPE_NONE); | ||
| 70 | return contexts; | ||
| 71 | } | ||
| 72 | |||
| 73 | } // namespace | ||
| 74 | |||
| 75 | void AVFrameDeleter(AVFrame* ptr) { | ||
| 76 | av_frame_free(&ptr); | ||
| 77 | } | ||
| 78 | 14 | ||
| 79 | Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs) | 15 | Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs) |
| 80 | : host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)), | 16 | : host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)), |
| 81 | vp8_decoder(std::make_unique<Decoder::VP8>(host1x)), | 17 | vp8_decoder(std::make_unique<Decoder::VP8>(host1x)), |
| 82 | vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {} | 18 | vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {} |
| 83 | 19 | ||
| 84 | Codec::~Codec() { | 20 | Codec::~Codec() = default; |
| 85 | if (!initialized) { | ||
| 86 | return; | ||
| 87 | } | ||
| 88 | // Free libav memory | ||
| 89 | avcodec_free_context(&av_codec_ctx); | ||
| 90 | av_buffer_unref(&av_gpu_decoder); | ||
| 91 | |||
| 92 | if (filters_initialized) { | ||
| 93 | avfilter_graph_free(&av_filter_graph); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | bool Codec::CreateGpuAvDevice() { | ||
| 98 | static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; | ||
| 99 | static const auto supported_contexts = ListSupportedContexts(); | ||
| 100 | for (const auto& type : PREFERRED_GPU_DECODERS) { | ||
| 101 | if (std::none_of(supported_contexts.begin(), supported_contexts.end(), | ||
| 102 | [&type](const auto& context) { return context == type; })) { | ||
| 103 | LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); | ||
| 104 | continue; | ||
| 105 | } | ||
| 106 | // Avoid memory leak from not cleaning up after av_hwdevice_ctx_create | ||
| 107 | av_buffer_unref(&av_gpu_decoder); | ||
| 108 | const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); | ||
| 109 | if (hwdevice_res < 0) { | ||
| 110 | LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", | ||
| 111 | av_hwdevice_get_type_name(type), hwdevice_res); | ||
| 112 | continue; | ||
| 113 | } | ||
| 114 | #ifdef LIBVA_FOUND | ||
| 115 | if (type == AV_HWDEVICE_TYPE_VAAPI) { | ||
| 116 | // we need to determine if this is an impersonated VAAPI driver | ||
| 117 | AVHWDeviceContext* hwctx = | ||
| 118 | static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data)); | ||
| 119 | AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx); | ||
| 120 | const char* vendor_name = vaQueryVendorString(vactx->display); | ||
| 121 | if (strstr(vendor_name, "VDPAU backend")) { | ||
| 122 | // VDPAU impersonated VAAPI impl's are super buggy, we need to skip them | ||
| 123 | LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver"); | ||
| 124 | continue; | ||
| 125 | } else { | ||
| 126 | // according to some user testing, certain vaapi driver (Intel?) could be buggy | ||
| 127 | // so let's log the driver name which may help the developers/supporters | ||
| 128 | LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | #endif | ||
| 132 | for (int i = 0;; i++) { | ||
| 133 | const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); | ||
| 134 | if (!config) { | ||
| 135 | LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.", | ||
| 136 | av_codec->name, av_hwdevice_get_type_name(type)); | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) { | ||
| 140 | LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); | ||
| 141 | av_codec_ctx->pix_fmt = config->pix_fmt; | ||
| 142 | return true; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | return false; | ||
| 147 | } | ||
| 148 | |||
| 149 | void Codec::InitializeAvCodecContext() { | ||
| 150 | av_codec_ctx = avcodec_alloc_context3(av_codec); | ||
| 151 | av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); | ||
| 152 | av_codec_ctx->thread_count = 0; | ||
| 153 | av_codec_ctx->thread_type &= ~FF_THREAD_FRAME; | ||
| 154 | } | ||
| 155 | |||
| 156 | void Codec::InitializeGpuDecoder() { | ||
| 157 | if (!CreateGpuAvDevice()) { | ||
| 158 | av_buffer_unref(&av_gpu_decoder); | ||
| 159 | return; | ||
| 160 | } | ||
| 161 | auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder); | ||
| 162 | ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed"); | ||
| 163 | av_codec_ctx->hw_device_ctx = hw_device_ctx; | ||
| 164 | av_codec_ctx->get_format = GetGpuFormat; | ||
| 165 | } | ||
| 166 | |||
| 167 | void Codec::InitializeAvFilters(AVFrame* frame) { | ||
| 168 | const AVFilter* buffer_src = avfilter_get_by_name("buffer"); | ||
| 169 | const AVFilter* buffer_sink = avfilter_get_by_name("buffersink"); | ||
| 170 | AVFilterInOut* inputs = avfilter_inout_alloc(); | ||
| 171 | AVFilterInOut* outputs = avfilter_inout_alloc(); | ||
| 172 | SCOPE_EXIT({ | ||
| 173 | avfilter_inout_free(&inputs); | ||
| 174 | avfilter_inout_free(&outputs); | ||
| 175 | }); | ||
| 176 | |||
| 177 | // Don't know how to get the accurate time_base but it doesn't matter for yadif filter | ||
| 178 | // so just use 1/1 to make buffer filter happy | ||
| 179 | std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame->width, | ||
| 180 | frame->height, frame->format); | ||
| 181 | |||
| 182 | av_filter_graph = avfilter_graph_alloc(); | ||
| 183 | int ret = avfilter_graph_create_filter(&av_filter_src_ctx, buffer_src, "in", args.c_str(), | ||
| 184 | nullptr, av_filter_graph); | ||
| 185 | if (ret < 0) { | ||
| 186 | LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter source error: {}", ret); | ||
| 187 | return; | ||
| 188 | } | ||
| 189 | |||
| 190 | ret = avfilter_graph_create_filter(&av_filter_sink_ctx, buffer_sink, "out", nullptr, nullptr, | ||
| 191 | av_filter_graph); | ||
| 192 | if (ret < 0) { | ||
| 193 | LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter sink error: {}", ret); | ||
| 194 | return; | ||
| 195 | } | ||
| 196 | |||
| 197 | inputs->name = av_strdup("out"); | ||
| 198 | inputs->filter_ctx = av_filter_sink_ctx; | ||
| 199 | inputs->pad_idx = 0; | ||
| 200 | inputs->next = nullptr; | ||
| 201 | |||
| 202 | outputs->name = av_strdup("in"); | ||
| 203 | outputs->filter_ctx = av_filter_src_ctx; | ||
| 204 | outputs->pad_idx = 0; | ||
| 205 | outputs->next = nullptr; | ||
| 206 | |||
| 207 | const char* description = "yadif=1:-1:0"; | ||
| 208 | ret = avfilter_graph_parse_ptr(av_filter_graph, description, &inputs, &outputs, nullptr); | ||
| 209 | if (ret < 0) { | ||
| 210 | LOG_ERROR(Service_NVDRV, "avfilter_graph_parse_ptr error: {}", ret); | ||
| 211 | return; | ||
| 212 | } | ||
| 213 | |||
| 214 | ret = avfilter_graph_config(av_filter_graph, nullptr); | ||
| 215 | if (ret < 0) { | ||
| 216 | LOG_ERROR(Service_NVDRV, "avfilter_graph_config error: {}", ret); | ||
| 217 | return; | ||
| 218 | } | ||
| 219 | |||
| 220 | filters_initialized = true; | ||
| 221 | } | ||
| 222 | 21 | ||
| 223 | void Codec::Initialize() { | 22 | void Codec::Initialize() { |
| 224 | const AVCodecID codec = [&] { | 23 | initialized = decode_api.Initialize(current_codec); |
| 225 | switch (current_codec) { | ||
| 226 | case Host1x::NvdecCommon::VideoCodec::H264: | ||
| 227 | return AV_CODEC_ID_H264; | ||
| 228 | case Host1x::NvdecCommon::VideoCodec::VP8: | ||
| 229 | return AV_CODEC_ID_VP8; | ||
| 230 | case Host1x::NvdecCommon::VideoCodec::VP9: | ||
| 231 | return AV_CODEC_ID_VP9; | ||
| 232 | default: | ||
| 233 | UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); | ||
| 234 | return AV_CODEC_ID_NONE; | ||
| 235 | } | ||
| 236 | }(); | ||
| 237 | av_codec = avcodec_find_decoder(codec); | ||
| 238 | |||
| 239 | InitializeAvCodecContext(); | ||
| 240 | if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) { | ||
| 241 | InitializeGpuDecoder(); | ||
| 242 | } | ||
| 243 | if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { | ||
| 244 | LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res); | ||
| 245 | avcodec_free_context(&av_codec_ctx); | ||
| 246 | av_buffer_unref(&av_gpu_decoder); | ||
| 247 | return; | ||
| 248 | } | ||
| 249 | if (!av_codec_ctx->hw_device_ctx) { | ||
| 250 | LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); | ||
| 251 | } | ||
| 252 | initialized = true; | ||
| 253 | } | 24 | } |
| 254 | 25 | ||
| 255 | void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) { | 26 | void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) { |
| @@ -264,14 +35,18 @@ void Codec::Decode() { | |||
| 264 | if (is_first_frame) { | 35 | if (is_first_frame) { |
| 265 | Initialize(); | 36 | Initialize(); |
| 266 | } | 37 | } |
| 38 | |||
| 267 | if (!initialized) { | 39 | if (!initialized) { |
| 268 | return; | 40 | return; |
| 269 | } | 41 | } |
| 42 | |||
| 43 | // Assemble bitstream. | ||
| 270 | bool vp9_hidden_frame = false; | 44 | bool vp9_hidden_frame = false; |
| 271 | const auto& frame_data = [&]() { | 45 | size_t configuration_size = 0; |
| 46 | const auto packet_data = [&]() { | ||
| 272 | switch (current_codec) { | 47 | switch (current_codec) { |
| 273 | case Tegra::Host1x::NvdecCommon::VideoCodec::H264: | 48 | case Tegra::Host1x::NvdecCommon::VideoCodec::H264: |
| 274 | return h264_decoder->ComposeFrame(state, is_first_frame); | 49 | return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame); |
| 275 | case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: | 50 | case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: |
| 276 | return vp8_decoder->ComposeFrame(state); | 51 | return vp8_decoder->ComposeFrame(state); |
| 277 | case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: | 52 | case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: |
| @@ -283,89 +58,35 @@ void Codec::Decode() { | |||
| 283 | return std::span<const u8>{}; | 58 | return std::span<const u8>{}; |
| 284 | } | 59 | } |
| 285 | }(); | 60 | }(); |
| 286 | AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter}; | 61 | |
| 287 | if (!packet) { | 62 | // Send assembled bitstream to decoder. |
| 288 | LOG_ERROR(Service_NVDRV, "av_packet_alloc failed"); | 63 | if (!decode_api.SendPacket(packet_data, configuration_size)) { |
| 289 | return; | ||
| 290 | } | ||
| 291 | packet->data = const_cast<u8*>(frame_data.data()); | ||
| 292 | packet->size = static_cast<s32>(frame_data.size()); | ||
| 293 | if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) { | ||
| 294 | LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res); | ||
| 295 | return; | 64 | return; |
| 296 | } | 65 | } |
| 297 | // Only receive/store visible frames | 66 | |
| 67 | // Only receive/store visible frames. | ||
| 298 | if (vp9_hidden_frame) { | 68 | if (vp9_hidden_frame) { |
| 299 | return; | 69 | return; |
| 300 | } | 70 | } |
| 301 | AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter}; | ||
| 302 | AVFramePtr final_frame{nullptr, AVFrameDeleter}; | ||
| 303 | ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed"); | ||
| 304 | if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) { | ||
| 305 | LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret); | ||
| 306 | return; | ||
| 307 | } | ||
| 308 | if (initial_frame->width == 0 || initial_frame->height == 0) { | ||
| 309 | LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); | ||
| 310 | return; | ||
| 311 | } | ||
| 312 | bool is_interlaced = initial_frame->interlaced_frame != 0; | ||
| 313 | if (av_codec_ctx->hw_device_ctx) { | ||
| 314 | final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; | ||
| 315 | ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); | ||
| 316 | // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp | ||
| 317 | // because Intel drivers crash unless using AV_PIX_FMT_NV12 | ||
| 318 | final_frame->format = PREFERRED_GPU_FMT; | ||
| 319 | const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0); | ||
| 320 | ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret); | ||
| 321 | } else { | ||
| 322 | final_frame = std::move(initial_frame); | ||
| 323 | } | ||
| 324 | if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) { | ||
| 325 | UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); | ||
| 326 | return; | ||
| 327 | } | ||
| 328 | if (!is_interlaced) { | ||
| 329 | av_frames.push(std::move(final_frame)); | ||
| 330 | } else { | ||
| 331 | if (!filters_initialized) { | ||
| 332 | InitializeAvFilters(final_frame.get()); | ||
| 333 | } | ||
| 334 | if (const int ret = av_buffersrc_add_frame_flags(av_filter_src_ctx, final_frame.get(), | ||
| 335 | AV_BUFFERSRC_FLAG_KEEP_REF); | ||
| 336 | ret) { | ||
| 337 | LOG_DEBUG(Service_NVDRV, "av_buffersrc_add_frame_flags error {}", ret); | ||
| 338 | return; | ||
| 339 | } | ||
| 340 | while (true) { | ||
| 341 | auto filter_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; | ||
| 342 | 71 | ||
| 343 | int ret = av_buffersink_get_frame(av_filter_sink_ctx, filter_frame.get()); | 72 | // Receive output frames from decoder. |
| 73 | decode_api.ReceiveFrames(frames); | ||
| 344 | 74 | ||
| 345 | if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) | 75 | while (frames.size() > 10) { |
| 346 | break; | 76 | LOG_DEBUG(HW_GPU, "ReceiveFrames overflow, dropped frame"); |
| 347 | if (ret < 0) { | 77 | frames.pop(); |
| 348 | LOG_DEBUG(Service_NVDRV, "av_buffersink_get_frame error {}", ret); | ||
| 349 | return; | ||
| 350 | } | ||
| 351 | |||
| 352 | av_frames.push(std::move(filter_frame)); | ||
| 353 | } | ||
| 354 | } | ||
| 355 | while (av_frames.size() > 10) { | ||
| 356 | LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame"); | ||
| 357 | av_frames.pop(); | ||
| 358 | } | 78 | } |
| 359 | } | 79 | } |
| 360 | 80 | ||
| 361 | AVFramePtr Codec::GetCurrentFrame() { | 81 | std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() { |
| 362 | // Sometimes VIC will request more frames than have been decoded. | 82 | // Sometimes VIC will request more frames than have been decoded. |
| 363 | // in this case, return a nullptr and don't overwrite previous frame data | 83 | // in this case, return a blank frame and don't overwrite previous data. |
| 364 | if (av_frames.empty()) { | 84 | if (frames.empty()) { |
| 365 | return AVFramePtr{nullptr, AVFrameDeleter}; | 85 | return {}; |
| 366 | } | 86 | } |
| 367 | AVFramePtr frame = std::move(av_frames.front()); | 87 | |
| 368 | av_frames.pop(); | 88 | auto frame = std::move(frames.front()); |
| 89 | frames.pop(); | ||
| 369 | return frame; | 90 | return frame; |
| 370 | } | 91 | } |
| 371 | 92 | ||
diff --git a/src/video_core/host1x/codecs/codec.h b/src/video_core/host1x/codecs/codec.h index 06fe00a4b..f700ae129 100644 --- a/src/video_core/host1x/codecs/codec.h +++ b/src/video_core/host1x/codecs/codec.h | |||
| @@ -4,28 +4,15 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <optional> | ||
| 7 | #include <string_view> | 8 | #include <string_view> |
| 8 | #include <queue> | 9 | #include <queue> |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "video_core/host1x/ffmpeg/ffmpeg.h" | ||
| 10 | #include "video_core/host1x/nvdec_common.h" | 12 | #include "video_core/host1x/nvdec_common.h" |
| 11 | 13 | ||
| 12 | extern "C" { | ||
| 13 | #if defined(__GNUC__) || defined(__clang__) | ||
| 14 | #pragma GCC diagnostic push | ||
| 15 | #pragma GCC diagnostic ignored "-Wconversion" | ||
| 16 | #endif | ||
| 17 | #include <libavcodec/avcodec.h> | ||
| 18 | #include <libavfilter/avfilter.h> | ||
| 19 | #if defined(__GNUC__) || defined(__clang__) | ||
| 20 | #pragma GCC diagnostic pop | ||
| 21 | #endif | ||
| 22 | } | ||
| 23 | |||
| 24 | namespace Tegra { | 14 | namespace Tegra { |
| 25 | 15 | ||
| 26 | void AVFrameDeleter(AVFrame* ptr); | ||
| 27 | using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>; | ||
| 28 | |||
| 29 | namespace Decoder { | 16 | namespace Decoder { |
| 30 | class H264; | 17 | class H264; |
| 31 | class VP8; | 18 | class VP8; |
| @@ -51,7 +38,7 @@ public: | |||
| 51 | void Decode(); | 38 | void Decode(); |
| 52 | 39 | ||
| 53 | /// Returns next decoded frame | 40 | /// Returns next decoded frame |
| 54 | [[nodiscard]] AVFramePtr GetCurrentFrame(); | 41 | [[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame(); |
| 55 | 42 | ||
| 56 | /// Returns the value of current_codec | 43 | /// Returns the value of current_codec |
| 57 | [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const; | 44 | [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const; |
| @@ -60,25 +47,9 @@ public: | |||
| 60 | [[nodiscard]] std::string_view GetCurrentCodecName() const; | 47 | [[nodiscard]] std::string_view GetCurrentCodecName() const; |
| 61 | 48 | ||
| 62 | private: | 49 | private: |
| 63 | void InitializeAvCodecContext(); | ||
| 64 | |||
| 65 | void InitializeAvFilters(AVFrame* frame); | ||
| 66 | |||
| 67 | void InitializeGpuDecoder(); | ||
| 68 | |||
| 69 | bool CreateGpuAvDevice(); | ||
| 70 | |||
| 71 | bool initialized{}; | 50 | bool initialized{}; |
| 72 | bool filters_initialized{}; | ||
| 73 | Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None}; | 51 | Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None}; |
| 74 | 52 | FFmpeg::DecodeApi decode_api; | |
| 75 | const AVCodec* av_codec{nullptr}; | ||
| 76 | AVCodecContext* av_codec_ctx{nullptr}; | ||
| 77 | AVBufferRef* av_gpu_decoder{nullptr}; | ||
| 78 | |||
| 79 | AVFilterContext* av_filter_src_ctx{nullptr}; | ||
| 80 | AVFilterContext* av_filter_sink_ctx{nullptr}; | ||
| 81 | AVFilterGraph* av_filter_graph{nullptr}; | ||
| 82 | 53 | ||
| 83 | Host1x::Host1x& host1x; | 54 | Host1x::Host1x& host1x; |
| 84 | const Host1x::NvdecCommon::NvdecRegisters& state; | 55 | const Host1x::NvdecCommon::NvdecRegisters& state; |
| @@ -86,7 +57,7 @@ private: | |||
| 86 | std::unique_ptr<Decoder::VP8> vp8_decoder; | 57 | std::unique_ptr<Decoder::VP8> vp8_decoder; |
| 87 | std::unique_ptr<Decoder::VP9> vp9_decoder; | 58 | std::unique_ptr<Decoder::VP9> vp9_decoder; |
| 88 | 59 | ||
| 89 | std::queue<AVFramePtr> av_frames{}; | 60 | std::queue<std::unique_ptr<FFmpeg::Frame>> frames{}; |
| 90 | }; | 61 | }; |
| 91 | 62 | ||
| 92 | } // namespace Tegra | 63 | } // namespace Tegra |
diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp index ece79b1e2..309a7f1d5 100644 --- a/src/video_core/host1x/codecs/h264.cpp +++ b/src/video_core/host1x/codecs/h264.cpp | |||
| @@ -30,7 +30,7 @@ H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {} | |||
| 30 | H264::~H264() = default; | 30 | H264::~H264() = default; |
| 31 | 31 | ||
| 32 | std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, | 32 | std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, |
| 33 | bool is_first_frame) { | 33 | size_t* out_configuration_size, bool is_first_frame) { |
| 34 | H264DecoderContext context; | 34 | H264DecoderContext context; |
| 35 | host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, | 35 | host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, |
| 36 | sizeof(H264DecoderContext)); | 36 | sizeof(H264DecoderContext)); |
| @@ -39,6 +39,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters | |||
| 39 | if (!is_first_frame && frame_number != 0) { | 39 | if (!is_first_frame && frame_number != 0) { |
| 40 | frame.resize_destructive(context.stream_len); | 40 | frame.resize_destructive(context.stream_len); |
| 41 | host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); | 41 | host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); |
| 42 | *out_configuration_size = 0; | ||
| 42 | return frame; | 43 | return frame; |
| 43 | } | 44 | } |
| 44 | 45 | ||
| @@ -157,6 +158,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters | |||
| 157 | frame.resize(encoded_header.size() + context.stream_len); | 158 | frame.resize(encoded_header.size() + context.stream_len); |
| 158 | std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); | 159 | std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); |
| 159 | 160 | ||
| 161 | *out_configuration_size = encoded_header.size(); | ||
| 160 | host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, | 162 | host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, |
| 161 | frame.data() + encoded_header.size(), context.stream_len); | 163 | frame.data() + encoded_header.size(), context.stream_len); |
| 162 | 164 | ||
diff --git a/src/video_core/host1x/codecs/h264.h b/src/video_core/host1x/codecs/h264.h index d6b556322..1deaf4632 100644 --- a/src/video_core/host1x/codecs/h264.h +++ b/src/video_core/host1x/codecs/h264.h | |||
| @@ -67,6 +67,7 @@ public: | |||
| 67 | 67 | ||
| 68 | /// Compose the H264 frame for FFmpeg decoding | 68 | /// Compose the H264 frame for FFmpeg decoding |
| 69 | [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, | 69 | [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, |
| 70 | size_t* out_configuration_size, | ||
| 70 | bool is_first_frame = false); | 71 | bool is_first_frame = false); |
| 71 | 72 | ||
| 72 | private: | 73 | private: |
diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp new file mode 100644 index 000000000..dcd07e6d2 --- /dev/null +++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp | |||
| @@ -0,0 +1,419 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/assert.h" | ||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "common/scope_exit.h" | ||
| 7 | #include "common/settings.h" | ||
| 8 | #include "video_core/host1x/ffmpeg/ffmpeg.h" | ||
| 9 | |||
| 10 | extern "C" { | ||
| 11 | #ifdef LIBVA_FOUND | ||
| 12 | // for querying VAAPI driver information | ||
| 13 | #include <libavutil/hwcontext_vaapi.h> | ||
| 14 | #endif | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace FFmpeg { | ||
| 18 | |||
| 19 | namespace { | ||
| 20 | |||
| 21 | constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12; | ||
| 22 | constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P; | ||
| 23 | constexpr std::array PreferredGpuDecoders = { | ||
| 24 | AV_HWDEVICE_TYPE_CUDA, | ||
| 25 | #ifdef _WIN32 | ||
| 26 | AV_HWDEVICE_TYPE_D3D11VA, | ||
| 27 | AV_HWDEVICE_TYPE_DXVA2, | ||
| 28 | #elif defined(__unix__) | ||
| 29 | AV_HWDEVICE_TYPE_VAAPI, | ||
| 30 | AV_HWDEVICE_TYPE_VDPAU, | ||
| 31 | #endif | ||
| 32 | // last resort for Linux Flatpak (w/ NVIDIA) | ||
| 33 | AV_HWDEVICE_TYPE_VULKAN, | ||
| 34 | }; | ||
| 35 | |||
| 36 | AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) { | ||
| 37 | for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { | ||
| 38 | if (*p == codec_context->pix_fmt) { | ||
| 39 | return codec_context->pix_fmt; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | LOG_INFO(HW_GPU, "Could not find compatible GPU AV format, falling back to CPU"); | ||
| 44 | av_buffer_unref(&codec_context->hw_device_ctx); | ||
| 45 | |||
| 46 | codec_context->pix_fmt = PreferredCpuFormat; | ||
| 47 | return codec_context->pix_fmt; | ||
| 48 | } | ||
| 49 | |||
| 50 | std::string AVError(int errnum) { | ||
| 51 | char errbuf[AV_ERROR_MAX_STRING_SIZE] = {}; | ||
| 52 | av_make_error_string(errbuf, sizeof(errbuf) - 1, errnum); | ||
| 53 | return errbuf; | ||
| 54 | } | ||
| 55 | |||
| 56 | } // namespace | ||
| 57 | |||
| 58 | Packet::Packet(std::span<const u8> data) { | ||
| 59 | m_packet = av_packet_alloc(); | ||
| 60 | m_packet->data = const_cast<u8*>(data.data()); | ||
| 61 | m_packet->size = static_cast<s32>(data.size()); | ||
| 62 | } | ||
| 63 | |||
| 64 | Packet::~Packet() { | ||
| 65 | av_packet_free(&m_packet); | ||
| 66 | } | ||
| 67 | |||
| 68 | Frame::Frame() { | ||
| 69 | m_frame = av_frame_alloc(); | ||
| 70 | } | ||
| 71 | |||
| 72 | Frame::~Frame() { | ||
| 73 | av_frame_free(&m_frame); | ||
| 74 | } | ||
| 75 | |||
| 76 | Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) { | ||
| 77 | const AVCodecID av_codec = [&] { | ||
| 78 | switch (codec) { | ||
| 79 | case Tegra::Host1x::NvdecCommon::VideoCodec::H264: | ||
| 80 | return AV_CODEC_ID_H264; | ||
| 81 | case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: | ||
| 82 | return AV_CODEC_ID_VP8; | ||
| 83 | case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: | ||
| 84 | return AV_CODEC_ID_VP9; | ||
| 85 | default: | ||
| 86 | UNIMPLEMENTED_MSG("Unknown codec {}", codec); | ||
| 87 | return AV_CODEC_ID_NONE; | ||
| 88 | } | ||
| 89 | }(); | ||
| 90 | |||
| 91 | m_codec = avcodec_find_decoder(av_codec); | ||
| 92 | } | ||
| 93 | |||
| 94 | bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const { | ||
| 95 | for (int i = 0;; i++) { | ||
| 96 | const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i); | ||
| 97 | if (!config) { | ||
| 98 | LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name, | ||
| 99 | av_hwdevice_get_type_name(type)); | ||
| 100 | break; | ||
| 101 | } | ||
| 102 | if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) != 0 && | ||
| 103 | config->device_type == type) { | ||
| 104 | LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); | ||
| 105 | *out_pix_fmt = config->pix_fmt; | ||
| 106 | return true; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | return false; | ||
| 111 | } | ||
| 112 | |||
| 113 | std::vector<AVHWDeviceType> HardwareContext::GetSupportedDeviceTypes() { | ||
| 114 | std::vector<AVHWDeviceType> types; | ||
| 115 | AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; | ||
| 116 | |||
| 117 | while (true) { | ||
| 118 | current_device_type = av_hwdevice_iterate_types(current_device_type); | ||
| 119 | if (current_device_type == AV_HWDEVICE_TYPE_NONE) { | ||
| 120 | return types; | ||
| 121 | } | ||
| 122 | |||
| 123 | types.push_back(current_device_type); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | HardwareContext::~HardwareContext() { | ||
| 128 | av_buffer_unref(&m_gpu_decoder); | ||
| 129 | } | ||
| 130 | |||
| 131 | bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context, | ||
| 132 | const Decoder& decoder) { | ||
| 133 | const auto supported_types = GetSupportedDeviceTypes(); | ||
| 134 | for (const auto type : PreferredGpuDecoders) { | ||
| 135 | AVPixelFormat hw_pix_fmt; | ||
| 136 | |||
| 137 | if (std::ranges::find(supported_types, type) == supported_types.end()) { | ||
| 138 | LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); | ||
| 139 | continue; | ||
| 140 | } | ||
| 141 | |||
| 142 | if (!this->InitializeWithType(type)) { | ||
| 143 | continue; | ||
| 144 | } | ||
| 145 | |||
| 146 | if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) { | ||
| 147 | decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt); | ||
| 148 | return true; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | return false; | ||
| 153 | } | ||
| 154 | |||
| 155 | bool HardwareContext::InitializeWithType(AVHWDeviceType type) { | ||
| 156 | av_buffer_unref(&m_gpu_decoder); | ||
| 157 | |||
| 158 | if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0); | ||
| 159 | ret < 0) { | ||
| 160 | LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), | ||
| 161 | AVError(ret)); | ||
| 162 | return false; | ||
| 163 | } | ||
| 164 | |||
| 165 | #ifdef LIBVA_FOUND | ||
| 166 | if (type == AV_HWDEVICE_TYPE_VAAPI) { | ||
| 167 | // We need to determine if this is an impersonated VAAPI driver. | ||
| 168 | auto* hwctx = reinterpret_cast<AVHWDeviceContext*>(m_gpu_decoder->data); | ||
| 169 | auto* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx); | ||
| 170 | const char* vendor_name = vaQueryVendorString(vactx->display); | ||
| 171 | if (strstr(vendor_name, "VDPAU backend")) { | ||
| 172 | // VDPAU impersonated VAAPI impls are super buggy, we need to skip them. | ||
| 173 | LOG_DEBUG(HW_GPU, "Skipping VDPAU impersonated VAAPI driver"); | ||
| 174 | return false; | ||
| 175 | } else { | ||
| 176 | // According to some user testing, certain VAAPI drivers (Intel?) could be buggy. | ||
| 177 | // Log the driver name just in case. | ||
| 178 | LOG_DEBUG(HW_GPU, "Using VAAPI driver: {}", vendor_name); | ||
| 179 | } | ||
| 180 | } | ||
| 181 | #endif | ||
| 182 | |||
| 183 | return true; | ||
| 184 | } | ||
| 185 | |||
| 186 | DecoderContext::DecoderContext(const Decoder& decoder) { | ||
| 187 | m_codec_context = avcodec_alloc_context3(decoder.GetCodec()); | ||
| 188 | av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0); | ||
| 189 | m_codec_context->thread_count = 0; | ||
| 190 | m_codec_context->thread_type &= ~FF_THREAD_FRAME; | ||
| 191 | } | ||
| 192 | |||
| 193 | DecoderContext::~DecoderContext() { | ||
| 194 | av_buffer_unref(&m_codec_context->hw_device_ctx); | ||
| 195 | avcodec_free_context(&m_codec_context); | ||
| 196 | } | ||
| 197 | |||
| 198 | void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context, | ||
| 199 | AVPixelFormat hw_pix_fmt) { | ||
| 200 | m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef()); | ||
| 201 | m_codec_context->get_format = GetGpuFormat; | ||
| 202 | m_codec_context->pix_fmt = hw_pix_fmt; | ||
| 203 | } | ||
| 204 | |||
| 205 | bool DecoderContext::OpenContext(const Decoder& decoder) { | ||
| 206 | if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) { | ||
| 207 | LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret)); | ||
| 208 | return false; | ||
| 209 | } | ||
| 210 | |||
| 211 | if (!m_codec_context->hw_device_ctx) { | ||
| 212 | LOG_INFO(HW_GPU, "Using FFmpeg software decoding"); | ||
| 213 | } | ||
| 214 | |||
| 215 | return true; | ||
| 216 | } | ||
| 217 | |||
| 218 | bool DecoderContext::SendPacket(const Packet& packet) { | ||
| 219 | if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) { | ||
| 220 | LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret)); | ||
| 221 | return false; | ||
| 222 | } | ||
| 223 | |||
| 224 | return true; | ||
| 225 | } | ||
| 226 | |||
| 227 | std::unique_ptr<Frame> DecoderContext::ReceiveFrame(bool* out_is_interlaced) { | ||
| 228 | auto dst_frame = std::make_unique<Frame>(); | ||
| 229 | |||
| 230 | const auto ReceiveImpl = [&](AVFrame* frame) { | ||
| 231 | if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) { | ||
| 232 | LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret)); | ||
| 233 | return false; | ||
| 234 | } | ||
| 235 | |||
| 236 | *out_is_interlaced = frame->interlaced_frame != 0; | ||
| 237 | return true; | ||
| 238 | }; | ||
| 239 | |||
| 240 | if (m_codec_context->hw_device_ctx) { | ||
| 241 | // If we have a hardware context, make a separate frame here to receive the | ||
| 242 | // hardware result before sending it to the output. | ||
| 243 | Frame intermediate_frame; | ||
| 244 | |||
| 245 | if (!ReceiveImpl(intermediate_frame.GetFrame())) { | ||
| 246 | return {}; | ||
| 247 | } | ||
| 248 | |||
| 249 | dst_frame->SetFormat(PreferredGpuFormat); | ||
| 250 | if (const int ret = | ||
| 251 | av_hwframe_transfer_data(dst_frame->GetFrame(), intermediate_frame.GetFrame(), 0); | ||
| 252 | ret < 0) { | ||
| 253 | LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret)); | ||
| 254 | return {}; | ||
| 255 | } | ||
| 256 | } else { | ||
| 257 | // Otherwise, decode the frame as normal. | ||
| 258 | if (!ReceiveImpl(dst_frame->GetFrame())) { | ||
| 259 | return {}; | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | return dst_frame; | ||
| 264 | } | ||
| 265 | |||
| 266 | DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) { | ||
| 267 | const AVFilter* buffer_src = avfilter_get_by_name("buffer"); | ||
| 268 | const AVFilter* buffer_sink = avfilter_get_by_name("buffersink"); | ||
| 269 | AVFilterInOut* inputs = avfilter_inout_alloc(); | ||
| 270 | AVFilterInOut* outputs = avfilter_inout_alloc(); | ||
| 271 | SCOPE_EXIT({ | ||
| 272 | avfilter_inout_free(&inputs); | ||
| 273 | avfilter_inout_free(&outputs); | ||
| 274 | }); | ||
| 275 | |||
| 276 | // Don't know how to get the accurate time_base but it doesn't matter for yadif filter | ||
| 277 | // so just use 1/1 to make buffer filter happy | ||
| 278 | std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame.GetWidth(), | ||
| 279 | frame.GetHeight(), static_cast<int>(frame.GetPixelFormat())); | ||
| 280 | |||
| 281 | m_filter_graph = avfilter_graph_alloc(); | ||
| 282 | int ret = avfilter_graph_create_filter(&m_source_context, buffer_src, "in", args.c_str(), | ||
| 283 | nullptr, m_filter_graph); | ||
| 284 | if (ret < 0) { | ||
| 285 | LOG_ERROR(HW_GPU, "avfilter_graph_create_filter source error: {}", AVError(ret)); | ||
| 286 | return; | ||
| 287 | } | ||
| 288 | |||
| 289 | ret = avfilter_graph_create_filter(&m_sink_context, buffer_sink, "out", nullptr, nullptr, | ||
| 290 | m_filter_graph); | ||
| 291 | if (ret < 0) { | ||
| 292 | LOG_ERROR(HW_GPU, "avfilter_graph_create_filter sink error: {}", AVError(ret)); | ||
| 293 | return; | ||
| 294 | } | ||
| 295 | |||
| 296 | inputs->name = av_strdup("out"); | ||
| 297 | inputs->filter_ctx = m_sink_context; | ||
| 298 | inputs->pad_idx = 0; | ||
| 299 | inputs->next = nullptr; | ||
| 300 | |||
| 301 | outputs->name = av_strdup("in"); | ||
| 302 | outputs->filter_ctx = m_source_context; | ||
| 303 | outputs->pad_idx = 0; | ||
| 304 | outputs->next = nullptr; | ||
| 305 | |||
| 306 | const char* description = "yadif=1:-1:0"; | ||
| 307 | ret = avfilter_graph_parse_ptr(m_filter_graph, description, &inputs, &outputs, nullptr); | ||
| 308 | if (ret < 0) { | ||
| 309 | LOG_ERROR(HW_GPU, "avfilter_graph_parse_ptr error: {}", AVError(ret)); | ||
| 310 | return; | ||
| 311 | } | ||
| 312 | |||
| 313 | ret = avfilter_graph_config(m_filter_graph, nullptr); | ||
| 314 | if (ret < 0) { | ||
| 315 | LOG_ERROR(HW_GPU, "avfilter_graph_config error: {}", AVError(ret)); | ||
| 316 | return; | ||
| 317 | } | ||
| 318 | |||
| 319 | m_initialized = true; | ||
| 320 | } | ||
| 321 | |||
| 322 | bool DeinterlaceFilter::AddSourceFrame(const Frame& frame) { | ||
| 323 | if (const int ret = av_buffersrc_add_frame_flags(m_source_context, frame.GetFrame(), | ||
| 324 | AV_BUFFERSRC_FLAG_KEEP_REF); | ||
| 325 | ret < 0) { | ||
| 326 | LOG_ERROR(HW_GPU, "av_buffersrc_add_frame_flags error: {}", AVError(ret)); | ||
| 327 | return false; | ||
| 328 | } | ||
| 329 | |||
| 330 | return true; | ||
| 331 | } | ||
| 332 | |||
| 333 | std::unique_ptr<Frame> DeinterlaceFilter::DrainSinkFrame() { | ||
| 334 | auto dst_frame = std::make_unique<Frame>(); | ||
| 335 | const int ret = av_buffersink_get_frame(m_sink_context, dst_frame->GetFrame()); | ||
| 336 | |||
| 337 | if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) { | ||
| 338 | return {}; | ||
| 339 | } | ||
| 340 | |||
| 341 | if (ret < 0) { | ||
| 342 | LOG_ERROR(HW_GPU, "av_buffersink_get_frame error: {}", AVError(ret)); | ||
| 343 | return {}; | ||
| 344 | } | ||
| 345 | |||
| 346 | return dst_frame; | ||
| 347 | } | ||
| 348 | |||
| 349 | DeinterlaceFilter::~DeinterlaceFilter() { | ||
| 350 | avfilter_graph_free(&m_filter_graph); | ||
| 351 | } | ||
| 352 | |||
| 353 | void DecodeApi::Reset() { | ||
| 354 | m_deinterlace_filter.reset(); | ||
| 355 | m_hardware_context.reset(); | ||
| 356 | m_decoder_context.reset(); | ||
| 357 | m_decoder.reset(); | ||
| 358 | } | ||
| 359 | |||
| 360 | bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) { | ||
| 361 | this->Reset(); | ||
| 362 | m_decoder.emplace(codec); | ||
| 363 | m_decoder_context.emplace(*m_decoder); | ||
| 364 | |||
| 365 | // Enable GPU decoding if requested. | ||
| 366 | if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) { | ||
| 367 | m_hardware_context.emplace(); | ||
| 368 | m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder); | ||
| 369 | } | ||
| 370 | |||
| 371 | // Open the decoder context. | ||
| 372 | if (!m_decoder_context->OpenContext(*m_decoder)) { | ||
| 373 | this->Reset(); | ||
| 374 | return false; | ||
| 375 | } | ||
| 376 | |||
| 377 | return true; | ||
| 378 | } | ||
| 379 | |||
| 380 | bool DecodeApi::SendPacket(std::span<const u8> packet_data, size_t configuration_size) { | ||
| 381 | FFmpeg::Packet packet(packet_data); | ||
| 382 | return m_decoder_context->SendPacket(packet); | ||
| 383 | } | ||
| 384 | |||
| 385 | void DecodeApi::ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue) { | ||
| 386 | // Receive raw frame from decoder. | ||
| 387 | bool is_interlaced; | ||
| 388 | auto frame = m_decoder_context->ReceiveFrame(&is_interlaced); | ||
| 389 | if (!frame) { | ||
| 390 | return; | ||
| 391 | } | ||
| 392 | |||
| 393 | if (!is_interlaced) { | ||
| 394 | // If the frame is not interlaced, we can pend it now. | ||
| 395 | frame_queue.push(std::move(frame)); | ||
| 396 | } else { | ||
| 397 | // Create the deinterlacer if needed. | ||
| 398 | if (!m_deinterlace_filter) { | ||
| 399 | m_deinterlace_filter.emplace(*frame); | ||
| 400 | } | ||
| 401 | |||
| 402 | // Add the frame we just received. | ||
| 403 | if (!m_deinterlace_filter->AddSourceFrame(*frame)) { | ||
| 404 | return; | ||
| 405 | } | ||
| 406 | |||
| 407 | // Pend output fields. | ||
| 408 | while (true) { | ||
| 409 | auto filter_frame = m_deinterlace_filter->DrainSinkFrame(); | ||
| 410 | if (!filter_frame) { | ||
| 411 | break; | ||
| 412 | } | ||
| 413 | |||
| 414 | frame_queue.push(std::move(filter_frame)); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | } // namespace FFmpeg | ||
diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.h b/src/video_core/host1x/ffmpeg/ffmpeg.h new file mode 100644 index 000000000..1de0bbd83 --- /dev/null +++ b/src/video_core/host1x/ffmpeg/ffmpeg.h | |||
| @@ -0,0 +1,213 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include <optional> | ||
| 8 | #include <span> | ||
| 9 | #include <vector> | ||
| 10 | #include <queue> | ||
| 11 | |||
| 12 | #include "common/common_funcs.h" | ||
| 13 | #include "common/common_types.h" | ||
| 14 | #include "video_core/host1x/nvdec_common.h" | ||
| 15 | |||
| 16 | extern "C" { | ||
| 17 | #if defined(__GNUC__) || defined(__clang__) | ||
| 18 | #pragma GCC diagnostic push | ||
| 19 | #pragma GCC diagnostic ignored "-Wconversion" | ||
| 20 | #endif | ||
| 21 | |||
| 22 | #include <libavcodec/avcodec.h> | ||
| 23 | #include <libavfilter/avfilter.h> | ||
| 24 | #include <libavfilter/buffersink.h> | ||
| 25 | #include <libavfilter/buffersrc.h> | ||
| 26 | #include <libavutil/avutil.h> | ||
| 27 | #include <libavutil/opt.h> | ||
| 28 | |||
| 29 | #if defined(__GNUC__) || defined(__clang__) | ||
| 30 | #pragma GCC diagnostic pop | ||
| 31 | #endif | ||
| 32 | } | ||
| 33 | |||
| 34 | namespace FFmpeg { | ||
| 35 | |||
| 36 | class Packet; | ||
| 37 | class Frame; | ||
| 38 | class Decoder; | ||
| 39 | class HardwareContext; | ||
| 40 | class DecoderContext; | ||
| 41 | class DeinterlaceFilter; | ||
| 42 | |||
| 43 | // Wraps an AVPacket, a container for compressed bitstream data. | ||
| 44 | class Packet { | ||
| 45 | public: | ||
| 46 | YUZU_NON_COPYABLE(Packet); | ||
| 47 | YUZU_NON_MOVEABLE(Packet); | ||
| 48 | |||
| 49 | explicit Packet(std::span<const u8> data); | ||
| 50 | ~Packet(); | ||
| 51 | |||
| 52 | AVPacket* GetPacket() const { | ||
| 53 | return m_packet; | ||
| 54 | } | ||
| 55 | |||
| 56 | private: | ||
| 57 | AVPacket* m_packet{}; | ||
| 58 | }; | ||
| 59 | |||
| 60 | // Wraps an AVFrame, a container for audio and video stream data. | ||
| 61 | class Frame { | ||
| 62 | public: | ||
| 63 | YUZU_NON_COPYABLE(Frame); | ||
| 64 | YUZU_NON_MOVEABLE(Frame); | ||
| 65 | |||
| 66 | explicit Frame(); | ||
| 67 | ~Frame(); | ||
| 68 | |||
| 69 | int GetWidth() const { | ||
| 70 | return m_frame->width; | ||
| 71 | } | ||
| 72 | |||
| 73 | int GetHeight() const { | ||
| 74 | return m_frame->height; | ||
| 75 | } | ||
| 76 | |||
| 77 | AVPixelFormat GetPixelFormat() const { | ||
| 78 | return static_cast<AVPixelFormat>(m_frame->format); | ||
| 79 | } | ||
| 80 | |||
| 81 | int GetStride(int plane) const { | ||
| 82 | return m_frame->linesize[plane]; | ||
| 83 | } | ||
| 84 | |||
| 85 | int* GetStrides() const { | ||
| 86 | return m_frame->linesize; | ||
| 87 | } | ||
| 88 | |||
| 89 | u8* GetData(int plane) const { | ||
| 90 | return m_frame->data[plane]; | ||
| 91 | } | ||
| 92 | |||
| 93 | u8** GetPlanes() const { | ||
| 94 | return m_frame->data; | ||
| 95 | } | ||
| 96 | |||
| 97 | void SetFormat(int format) { | ||
| 98 | m_frame->format = format; | ||
| 99 | } | ||
| 100 | |||
| 101 | AVFrame* GetFrame() const { | ||
| 102 | return m_frame; | ||
| 103 | } | ||
| 104 | |||
| 105 | private: | ||
| 106 | AVFrame* m_frame{}; | ||
| 107 | }; | ||
| 108 | |||
| 109 | // Wraps an AVCodec, a type containing information about a codec. | ||
| 110 | class Decoder { | ||
| 111 | public: | ||
| 112 | YUZU_NON_COPYABLE(Decoder); | ||
| 113 | YUZU_NON_MOVEABLE(Decoder); | ||
| 114 | |||
| 115 | explicit Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec); | ||
| 116 | ~Decoder() = default; | ||
| 117 | |||
| 118 | bool SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const; | ||
| 119 | |||
| 120 | const AVCodec* GetCodec() const { | ||
| 121 | return m_codec; | ||
| 122 | } | ||
| 123 | |||
| 124 | private: | ||
| 125 | const AVCodec* m_codec{}; | ||
| 126 | }; | ||
| 127 | |||
| 128 | // Wraps AVBufferRef for an accelerated decoder. | ||
| 129 | class HardwareContext { | ||
| 130 | public: | ||
| 131 | YUZU_NON_COPYABLE(HardwareContext); | ||
| 132 | YUZU_NON_MOVEABLE(HardwareContext); | ||
| 133 | |||
| 134 | static std::vector<AVHWDeviceType> GetSupportedDeviceTypes(); | ||
| 135 | |||
| 136 | explicit HardwareContext() = default; | ||
| 137 | ~HardwareContext(); | ||
| 138 | |||
| 139 | bool InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder); | ||
| 140 | |||
| 141 | AVBufferRef* GetBufferRef() const { | ||
| 142 | return m_gpu_decoder; | ||
| 143 | } | ||
| 144 | |||
| 145 | private: | ||
| 146 | bool InitializeWithType(AVHWDeviceType type); | ||
| 147 | |||
| 148 | AVBufferRef* m_gpu_decoder{}; | ||
| 149 | }; | ||
| 150 | |||
| 151 | // Wraps an AVCodecContext. | ||
| 152 | class DecoderContext { | ||
| 153 | public: | ||
| 154 | YUZU_NON_COPYABLE(DecoderContext); | ||
| 155 | YUZU_NON_MOVEABLE(DecoderContext); | ||
| 156 | |||
| 157 | explicit DecoderContext(const Decoder& decoder); | ||
| 158 | ~DecoderContext(); | ||
| 159 | |||
| 160 | void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt); | ||
| 161 | bool OpenContext(const Decoder& decoder); | ||
| 162 | bool SendPacket(const Packet& packet); | ||
| 163 | std::unique_ptr<Frame> ReceiveFrame(bool* out_is_interlaced); | ||
| 164 | |||
| 165 | AVCodecContext* GetCodecContext() const { | ||
| 166 | return m_codec_context; | ||
| 167 | } | ||
| 168 | |||
| 169 | private: | ||
| 170 | AVCodecContext* m_codec_context{}; | ||
| 171 | }; | ||
| 172 | |||
| 173 | // Wraps an AVFilterGraph. | ||
| 174 | class DeinterlaceFilter { | ||
| 175 | public: | ||
| 176 | YUZU_NON_COPYABLE(DeinterlaceFilter); | ||
| 177 | YUZU_NON_MOVEABLE(DeinterlaceFilter); | ||
| 178 | |||
| 179 | explicit DeinterlaceFilter(const Frame& frame); | ||
| 180 | ~DeinterlaceFilter(); | ||
| 181 | |||
| 182 | bool AddSourceFrame(const Frame& frame); | ||
| 183 | std::unique_ptr<Frame> DrainSinkFrame(); | ||
| 184 | |||
| 185 | private: | ||
| 186 | AVFilterGraph* m_filter_graph{}; | ||
| 187 | AVFilterContext* m_source_context{}; | ||
| 188 | AVFilterContext* m_sink_context{}; | ||
| 189 | bool m_initialized{}; | ||
| 190 | }; | ||
| 191 | |||
| 192 | class DecodeApi { | ||
| 193 | public: | ||
| 194 | YUZU_NON_COPYABLE(DecodeApi); | ||
| 195 | YUZU_NON_MOVEABLE(DecodeApi); | ||
| 196 | |||
| 197 | DecodeApi() = default; | ||
| 198 | ~DecodeApi() = default; | ||
| 199 | |||
| 200 | bool Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec); | ||
| 201 | void Reset(); | ||
| 202 | |||
| 203 | bool SendPacket(std::span<const u8> packet_data, size_t configuration_size); | ||
| 204 | void ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue); | ||
| 205 | |||
| 206 | private: | ||
| 207 | std::optional<FFmpeg::Decoder> m_decoder; | ||
| 208 | std::optional<FFmpeg::DecoderContext> m_decoder_context; | ||
| 209 | std::optional<FFmpeg::HardwareContext> m_hardware_context; | ||
| 210 | std::optional<FFmpeg::DeinterlaceFilter> m_deinterlace_filter; | ||
| 211 | }; | ||
| 212 | |||
| 213 | } // namespace FFmpeg | ||
diff --git a/src/video_core/host1x/nvdec.cpp b/src/video_core/host1x/nvdec.cpp index a4bd5b79f..b8f5866d3 100644 --- a/src/video_core/host1x/nvdec.cpp +++ b/src/video_core/host1x/nvdec.cpp | |||
| @@ -28,7 +28,7 @@ void Nvdec::ProcessMethod(u32 method, u32 argument) { | |||
| 28 | } | 28 | } |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | AVFramePtr Nvdec::GetFrame() { | 31 | std::unique_ptr<FFmpeg::Frame> Nvdec::GetFrame() { |
| 32 | return codec->GetCurrentFrame(); | 32 | return codec->GetCurrentFrame(); |
| 33 | } | 33 | } |
| 34 | 34 | ||
diff --git a/src/video_core/host1x/nvdec.h b/src/video_core/host1x/nvdec.h index 3949d5181..ddddb8d28 100644 --- a/src/video_core/host1x/nvdec.h +++ b/src/video_core/host1x/nvdec.h | |||
| @@ -23,7 +23,7 @@ public: | |||
| 23 | void ProcessMethod(u32 method, u32 argument); | 23 | void ProcessMethod(u32 method, u32 argument); |
| 24 | 24 | ||
| 25 | /// Return most recently decoded frame | 25 | /// Return most recently decoded frame |
| 26 | [[nodiscard]] AVFramePtr GetFrame(); | 26 | [[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetFrame(); |
| 27 | 27 | ||
| 28 | private: | 28 | private: |
| 29 | /// Invoke codec to decode a frame | 29 | /// Invoke codec to decode a frame |
diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp index 10d7ef884..2a5eba415 100644 --- a/src/video_core/host1x/vic.cpp +++ b/src/video_core/host1x/vic.cpp | |||
| @@ -82,27 +82,26 @@ void Vic::Execute() { | |||
| 82 | return; | 82 | return; |
| 83 | } | 83 | } |
| 84 | const VicConfig config{host1x.MemoryManager().Read<u64>(config_struct_address + 0x20)}; | 84 | const VicConfig config{host1x.MemoryManager().Read<u64>(config_struct_address + 0x20)}; |
| 85 | const AVFramePtr frame_ptr = nvdec_processor->GetFrame(); | 85 | auto frame = nvdec_processor->GetFrame(); |
| 86 | const auto* frame = frame_ptr.get(); | ||
| 87 | if (!frame) { | 86 | if (!frame) { |
| 88 | return; | 87 | return; |
| 89 | } | 88 | } |
| 90 | const u64 surface_width = config.surface_width_minus1 + 1; | 89 | const u64 surface_width = config.surface_width_minus1 + 1; |
| 91 | const u64 surface_height = config.surface_height_minus1 + 1; | 90 | const u64 surface_height = config.surface_height_minus1 + 1; |
| 92 | if (static_cast<u64>(frame->width) != surface_width || | 91 | if (static_cast<u64>(frame->GetWidth()) != surface_width || |
| 93 | static_cast<u64>(frame->height) != surface_height) { | 92 | static_cast<u64>(frame->GetHeight()) != surface_height) { |
| 94 | // TODO: Properly support multiple video streams with differing frame dimensions | 93 | // TODO: Properly support multiple video streams with differing frame dimensions |
| 95 | LOG_WARNING(Service_NVDRV, "Frame dimensions {}x{} don't match surface dimensions {}x{}", | 94 | LOG_WARNING(Service_NVDRV, "Frame dimensions {}x{} don't match surface dimensions {}x{}", |
| 96 | frame->width, frame->height, surface_width, surface_height); | 95 | frame->GetWidth(), frame->GetHeight(), surface_width, surface_height); |
| 97 | } | 96 | } |
| 98 | switch (config.pixel_format) { | 97 | switch (config.pixel_format) { |
| 99 | case VideoPixelFormat::RGBA8: | 98 | case VideoPixelFormat::RGBA8: |
| 100 | case VideoPixelFormat::BGRA8: | 99 | case VideoPixelFormat::BGRA8: |
| 101 | case VideoPixelFormat::RGBX8: | 100 | case VideoPixelFormat::RGBX8: |
| 102 | WriteRGBFrame(frame, config); | 101 | WriteRGBFrame(std::move(frame), config); |
| 103 | break; | 102 | break; |
| 104 | case VideoPixelFormat::YUV420: | 103 | case VideoPixelFormat::YUV420: |
| 105 | WriteYUVFrame(frame, config); | 104 | WriteYUVFrame(std::move(frame), config); |
| 106 | break; | 105 | break; |
| 107 | default: | 106 | default: |
| 108 | UNIMPLEMENTED_MSG("Unknown video pixel format {:X}", config.pixel_format.Value()); | 107 | UNIMPLEMENTED_MSG("Unknown video pixel format {:X}", config.pixel_format.Value()); |
| @@ -110,10 +109,14 @@ void Vic::Execute() { | |||
| 110 | } | 109 | } |
| 111 | } | 110 | } |
| 112 | 111 | ||
| 113 | void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { | 112 | void Vic::WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) { |
| 114 | LOG_TRACE(Service_NVDRV, "Writing RGB Frame"); | 113 | LOG_TRACE(Service_NVDRV, "Writing RGB Frame"); |
| 115 | 114 | ||
| 116 | if (!scaler_ctx || frame->width != scaler_width || frame->height != scaler_height) { | 115 | const auto frame_width = frame->GetWidth(); |
| 116 | const auto frame_height = frame->GetHeight(); | ||
| 117 | const auto frame_format = frame->GetPixelFormat(); | ||
| 118 | |||
| 119 | if (!scaler_ctx || frame_width != scaler_width || frame_height != scaler_height) { | ||
| 117 | const AVPixelFormat target_format = [pixel_format = config.pixel_format]() { | 120 | const AVPixelFormat target_format = [pixel_format = config.pixel_format]() { |
| 118 | switch (pixel_format) { | 121 | switch (pixel_format) { |
| 119 | case VideoPixelFormat::RGBA8: | 122 | case VideoPixelFormat::RGBA8: |
| @@ -129,27 +132,26 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { | |||
| 129 | 132 | ||
| 130 | sws_freeContext(scaler_ctx); | 133 | sws_freeContext(scaler_ctx); |
| 131 | // Frames are decoded into either YUV420 or NV12 formats. Convert to desired RGB format | 134 | // Frames are decoded into either YUV420 or NV12 formats. Convert to desired RGB format |
| 132 | scaler_ctx = sws_getContext(frame->width, frame->height, | 135 | scaler_ctx = sws_getContext(frame_width, frame_height, frame_format, frame_width, |
| 133 | static_cast<AVPixelFormat>(frame->format), frame->width, | 136 | frame_height, target_format, 0, nullptr, nullptr, nullptr); |
| 134 | frame->height, target_format, 0, nullptr, nullptr, nullptr); | 137 | scaler_width = frame_width; |
| 135 | scaler_width = frame->width; | 138 | scaler_height = frame_height; |
| 136 | scaler_height = frame->height; | ||
| 137 | converted_frame_buffer.reset(); | 139 | converted_frame_buffer.reset(); |
| 138 | } | 140 | } |
| 139 | if (!converted_frame_buffer) { | 141 | if (!converted_frame_buffer) { |
| 140 | const size_t frame_size = frame->width * frame->height * 4; | 142 | const size_t frame_size = frame_width * frame_height * 4; |
| 141 | converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(frame_size)), av_free}; | 143 | converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(frame_size)), av_free}; |
| 142 | } | 144 | } |
| 143 | const std::array<int, 4> converted_stride{frame->width * 4, frame->height * 4, 0, 0}; | 145 | const std::array<int, 4> converted_stride{frame_width * 4, frame_height * 4, 0, 0}; |
| 144 | u8* const converted_frame_buf_addr{converted_frame_buffer.get()}; | 146 | u8* const converted_frame_buf_addr{converted_frame_buffer.get()}; |
| 145 | sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height, &converted_frame_buf_addr, | 147 | sws_scale(scaler_ctx, frame->GetPlanes(), frame->GetStrides(), 0, frame_height, |
| 146 | converted_stride.data()); | 148 | &converted_frame_buf_addr, converted_stride.data()); |
| 147 | 149 | ||
| 148 | // Use the minimum of surface/frame dimensions to avoid buffer overflow. | 150 | // Use the minimum of surface/frame dimensions to avoid buffer overflow. |
| 149 | const u32 surface_width = static_cast<u32>(config.surface_width_minus1) + 1; | 151 | const u32 surface_width = static_cast<u32>(config.surface_width_minus1) + 1; |
| 150 | const u32 surface_height = static_cast<u32>(config.surface_height_minus1) + 1; | 152 | const u32 surface_height = static_cast<u32>(config.surface_height_minus1) + 1; |
| 151 | const u32 width = std::min(surface_width, static_cast<u32>(frame->width)); | 153 | const u32 width = std::min(surface_width, static_cast<u32>(frame_width)); |
| 152 | const u32 height = std::min(surface_height, static_cast<u32>(frame->height)); | 154 | const u32 height = std::min(surface_height, static_cast<u32>(frame_height)); |
| 153 | const u32 blk_kind = static_cast<u32>(config.block_linear_kind); | 155 | const u32 blk_kind = static_cast<u32>(config.block_linear_kind); |
| 154 | if (blk_kind != 0) { | 156 | if (blk_kind != 0) { |
| 155 | // swizzle pitch linear to block linear | 157 | // swizzle pitch linear to block linear |
| @@ -169,23 +171,23 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { | |||
| 169 | } | 171 | } |
| 170 | } | 172 | } |
| 171 | 173 | ||
| 172 | void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { | 174 | void Vic::WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) { |
| 173 | LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame"); | 175 | LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame"); |
| 174 | 176 | ||
| 175 | const std::size_t surface_width = config.surface_width_minus1 + 1; | 177 | const std::size_t surface_width = config.surface_width_minus1 + 1; |
| 176 | const std::size_t surface_height = config.surface_height_minus1 + 1; | 178 | const std::size_t surface_height = config.surface_height_minus1 + 1; |
| 177 | const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL; | 179 | const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL; |
| 178 | // Use the minimum of surface/frame dimensions to avoid buffer overflow. | 180 | // Use the minimum of surface/frame dimensions to avoid buffer overflow. |
| 179 | const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->width)); | 181 | const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->GetWidth())); |
| 180 | const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height)); | 182 | const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->GetHeight())); |
| 181 | 183 | ||
| 182 | const auto stride = static_cast<size_t>(frame->linesize[0]); | 184 | const auto stride = static_cast<size_t>(frame->GetStride(0)); |
| 183 | 185 | ||
| 184 | luma_buffer.resize_destructive(aligned_width * surface_height); | 186 | luma_buffer.resize_destructive(aligned_width * surface_height); |
| 185 | chroma_buffer.resize_destructive(aligned_width * surface_height / 2); | 187 | chroma_buffer.resize_destructive(aligned_width * surface_height / 2); |
| 186 | 188 | ||
| 187 | // Populate luma buffer | 189 | // Populate luma buffer |
| 188 | const u8* luma_src = frame->data[0]; | 190 | const u8* luma_src = frame->GetData(0); |
| 189 | for (std::size_t y = 0; y < frame_height; ++y) { | 191 | for (std::size_t y = 0; y < frame_height; ++y) { |
| 190 | const std::size_t src = y * stride; | 192 | const std::size_t src = y * stride; |
| 191 | const std::size_t dst = y * aligned_width; | 193 | const std::size_t dst = y * aligned_width; |
| @@ -196,16 +198,16 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { | |||
| 196 | 198 | ||
| 197 | // Chroma | 199 | // Chroma |
| 198 | const std::size_t half_height = frame_height / 2; | 200 | const std::size_t half_height = frame_height / 2; |
| 199 | const auto half_stride = static_cast<size_t>(frame->linesize[1]); | 201 | const auto half_stride = static_cast<size_t>(frame->GetStride(1)); |
| 200 | 202 | ||
| 201 | switch (frame->format) { | 203 | switch (frame->GetPixelFormat()) { |
| 202 | case AV_PIX_FMT_YUV420P: { | 204 | case AV_PIX_FMT_YUV420P: { |
| 203 | // Frame from FFmpeg software | 205 | // Frame from FFmpeg software |
| 204 | // Populate chroma buffer from both channels with interleaving. | 206 | // Populate chroma buffer from both channels with interleaving. |
| 205 | const std::size_t half_width = frame_width / 2; | 207 | const std::size_t half_width = frame_width / 2; |
| 206 | u8* chroma_buffer_data = chroma_buffer.data(); | 208 | u8* chroma_buffer_data = chroma_buffer.data(); |
| 207 | const u8* chroma_b_src = frame->data[1]; | 209 | const u8* chroma_b_src = frame->GetData(1); |
| 208 | const u8* chroma_r_src = frame->data[2]; | 210 | const u8* chroma_r_src = frame->GetData(2); |
| 209 | for (std::size_t y = 0; y < half_height; ++y) { | 211 | for (std::size_t y = 0; y < half_height; ++y) { |
| 210 | const std::size_t src = y * half_stride; | 212 | const std::size_t src = y * half_stride; |
| 211 | const std::size_t dst = y * aligned_width; | 213 | const std::size_t dst = y * aligned_width; |
| @@ -219,7 +221,7 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { | |||
| 219 | case AV_PIX_FMT_NV12: { | 221 | case AV_PIX_FMT_NV12: { |
| 220 | // Frame from VA-API hardware | 222 | // Frame from VA-API hardware |
| 221 | // This is already interleaved so just copy | 223 | // This is already interleaved so just copy |
| 222 | const u8* chroma_src = frame->data[1]; | 224 | const u8* chroma_src = frame->GetData(1); |
| 223 | for (std::size_t y = 0; y < half_height; ++y) { | 225 | for (std::size_t y = 0; y < half_height; ++y) { |
| 224 | const std::size_t src = y * stride; | 226 | const std::size_t src = y * stride; |
| 225 | const std::size_t dst = y * aligned_width; | 227 | const std::size_t dst = y * aligned_width; |
diff --git a/src/video_core/host1x/vic.h b/src/video_core/host1x/vic.h index 3d9753047..6c868f062 100644 --- a/src/video_core/host1x/vic.h +++ b/src/video_core/host1x/vic.h | |||
| @@ -39,9 +39,9 @@ public: | |||
| 39 | private: | 39 | private: |
| 40 | void Execute(); | 40 | void Execute(); |
| 41 | 41 | ||
| 42 | void WriteRGBFrame(const AVFrame* frame, const VicConfig& config); | 42 | void WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config); |
| 43 | 43 | ||
| 44 | void WriteYUVFrame(const AVFrame* frame, const VicConfig& config); | 44 | void WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config); |
| 45 | 45 | ||
| 46 | Host1x& host1x; | 46 | Host1x& host1x; |
| 47 | std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor; | 47 | std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor; |
diff --git a/src/video_core/query_cache/query_cache.h b/src/video_core/query_cache/query_cache.h index 78b42b518..efa9adf7a 100644 --- a/src/video_core/query_cache/query_cache.h +++ b/src/video_core/query_cache/query_cache.h | |||
| @@ -266,7 +266,7 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type | |||
| 266 | return; | 266 | return; |
| 267 | } | 267 | } |
| 268 | if (False(query_base->flags & QueryFlagBits::IsFinalValueSynced)) [[unlikely]] { | 268 | if (False(query_base->flags & QueryFlagBits::IsFinalValueSynced)) [[unlikely]] { |
| 269 | UNREACHABLE(); | 269 | ASSERT(false); |
| 270 | return; | 270 | return; |
| 271 | } | 271 | } |
| 272 | query_base->value += streamer->GetAmmendValue(); | 272 | query_base->value += streamer->GetAmmendValue(); |
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp index 65cd5aa06..4f1d5b548 100644 --- a/src/video_core/renderer_null/null_rasterizer.cpp +++ b/src/video_core/renderer_null/null_rasterizer.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include "common/alignment.h" | 4 | #include "common/alignment.h" |
| 5 | #include "core/memory.h" | 5 | #include "core/memory.h" |
| 6 | #include "video_core/control/channel_state.h" | ||
| 6 | #include "video_core/host1x/host1x.h" | 7 | #include "video_core/host1x/host1x.h" |
| 7 | #include "video_core/memory_manager.h" | 8 | #include "video_core/memory_manager.h" |
| 8 | #include "video_core/renderer_null/null_rasterizer.h" | 9 | #include "video_core/renderer_null/null_rasterizer.h" |
| @@ -99,8 +100,14 @@ bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config, | |||
| 99 | } | 100 | } |
| 100 | void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, | 101 | void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, |
| 101 | const VideoCore::DiskResourceLoadCallback& callback) {} | 102 | const VideoCore::DiskResourceLoadCallback& callback) {} |
| 102 | void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {} | 103 | void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) { |
| 103 | void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) {} | 104 | CreateChannel(channel); |
| 104 | void RasterizerNull::ReleaseChannel(s32 channel_id) {} | 105 | } |
| 106 | void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) { | ||
| 107 | BindToChannel(channel.bind_id); | ||
| 108 | } | ||
| 109 | void RasterizerNull::ReleaseChannel(s32 channel_id) { | ||
| 110 | EraseChannel(channel_id); | ||
| 111 | } | ||
| 105 | 112 | ||
| 106 | } // namespace Null | 113 | } // namespace Null |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 38d553d3c..dfd696de6 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -178,13 +178,14 @@ void BufferCacheRuntime::CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, | |||
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, | 180 | void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, |
| 181 | std::span<const VideoCommon::BufferCopy> copies, bool barrier) { | 181 | std::span<const VideoCommon::BufferCopy> copies, bool barrier, |
| 182 | bool) { | ||
| 182 | CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier); | 183 | CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier); |
| 183 | } | 184 | } |
| 184 | 185 | ||
| 185 | void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, | 186 | void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, |
| 186 | std::span<const VideoCommon::BufferCopy> copies) { | 187 | std::span<const VideoCommon::BufferCopy> copies, bool) { |
| 187 | CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies); | 188 | CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies, true); |
| 188 | } | 189 | } |
| 189 | 190 | ||
| 190 | void BufferCacheRuntime::PreCopyBarrier() { | 191 | void BufferCacheRuntime::PreCopyBarrier() { |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index e8dbbd3a2..000f29a82 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -30,6 +30,8 @@ public: | |||
| 30 | 30 | ||
| 31 | void MakeResident(GLenum access) noexcept; | 31 | void MakeResident(GLenum access) noexcept; |
| 32 | 32 | ||
| 33 | void MarkUsage(u64 offset, u64 size) {} | ||
| 34 | |||
| 33 | [[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format); | 35 | [[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format); |
| 34 | 36 | ||
| 35 | [[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept { | 37 | [[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept { |
| @@ -66,22 +68,29 @@ public: | |||
| 66 | 68 | ||
| 67 | [[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size); | 69 | [[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size); |
| 68 | 70 | ||
| 71 | bool CanReorderUpload(const Buffer&, std::span<const VideoCommon::BufferCopy>) { | ||
| 72 | return false; | ||
| 73 | } | ||
| 74 | |||
| 69 | void CopyBuffer(GLuint dst_buffer, GLuint src_buffer, | 75 | void CopyBuffer(GLuint dst_buffer, GLuint src_buffer, |
| 70 | std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); | 76 | std::span<const VideoCommon::BufferCopy> copies, bool barrier); |
| 71 | 77 | ||
| 72 | void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, | 78 | void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, |
| 73 | std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); | 79 | std::span<const VideoCommon::BufferCopy> copies, bool barrier); |
| 74 | 80 | ||
| 75 | void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, | 81 | void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, |
| 76 | std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); | 82 | std::span<const VideoCommon::BufferCopy> copies, bool barrier, |
| 83 | bool can_reorder_upload = false); | ||
| 77 | 84 | ||
| 78 | void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, | 85 | void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, |
| 79 | std::span<const VideoCommon::BufferCopy> copies); | 86 | std::span<const VideoCommon::BufferCopy> copies, bool); |
| 80 | 87 | ||
| 81 | void PreCopyBarrier(); | 88 | void PreCopyBarrier(); |
| 82 | void PostCopyBarrier(); | 89 | void PostCopyBarrier(); |
| 83 | void Finish(); | 90 | void Finish(); |
| 84 | 91 | ||
| 92 | void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {} | ||
| 93 | |||
| 85 | void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); | 94 | void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); |
| 86 | 95 | ||
| 87 | void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); | 96 | void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); |
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 44a771d65..af0a453ee 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp | |||
| @@ -559,7 +559,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | |||
| 559 | } | 559 | } |
| 560 | 560 | ||
| 561 | void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { | 561 | void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { |
| 562 | glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), GL_SEPARATE_ATTRIBS); | 562 | const GLenum buffer_mode = |
| 563 | num_xfb_buffers_active == 1 ? GL_INTERLEAVED_ATTRIBS : GL_SEPARATE_ATTRIBS; | ||
| 564 | glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), buffer_mode); | ||
| 563 | } | 565 | } |
| 564 | 566 | ||
| 565 | void GraphicsPipeline::GenerateTransformFeedbackState() { | 567 | void GraphicsPipeline::GenerateTransformFeedbackState() { |
| @@ -567,12 +569,14 @@ void GraphicsPipeline::GenerateTransformFeedbackState() { | |||
| 567 | // when this is required. | 569 | // when this is required. |
| 568 | GLint* cursor{xfb_attribs.data()}; | 570 | GLint* cursor{xfb_attribs.data()}; |
| 569 | 571 | ||
| 572 | num_xfb_buffers_active = 0; | ||
| 570 | for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) { | 573 | for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) { |
| 571 | const auto& layout = key.xfb_state.layouts[feedback]; | 574 | const auto& layout = key.xfb_state.layouts[feedback]; |
| 572 | UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding"); | 575 | UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding"); |
| 573 | if (layout.varying_count == 0) { | 576 | if (layout.varying_count == 0) { |
| 574 | continue; | 577 | continue; |
| 575 | } | 578 | } |
| 579 | num_xfb_buffers_active++; | ||
| 576 | 580 | ||
| 577 | const auto& locations = key.xfb_state.varyings[feedback]; | 581 | const auto& locations = key.xfb_state.varyings[feedback]; |
| 578 | std::optional<u32> current_index; | 582 | std::optional<u32> current_index; |
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index 74fc9cc3d..2f70c1ae9 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h | |||
| @@ -154,6 +154,7 @@ private: | |||
| 154 | 154 | ||
| 155 | static constexpr std::size_t XFB_ENTRY_STRIDE = 3; | 155 | static constexpr std::size_t XFB_ENTRY_STRIDE = 3; |
| 156 | GLsizei num_xfb_attribs{}; | 156 | GLsizei num_xfb_attribs{}; |
| 157 | u32 num_xfb_buffers_active{}; | ||
| 157 | std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{}; | 158 | std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{}; |
| 158 | 159 | ||
| 159 | std::mutex built_mutex; | 160 | std::mutex built_mutex; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 27e2de1bf..9995b6dd4 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -555,7 +555,7 @@ void RasterizerOpenGL::OnCacheInvalidation(VAddr addr, u64 size) { | |||
| 555 | } | 555 | } |
| 556 | { | 556 | { |
| 557 | std::scoped_lock lock{buffer_cache.mutex}; | 557 | std::scoped_lock lock{buffer_cache.mutex}; |
| 558 | buffer_cache.CachedWriteMemory(addr, size); | 558 | buffer_cache.WriteMemory(addr, size); |
| 559 | } | 559 | } |
| 560 | shader_cache.InvalidateRegion(addr, size); | 560 | shader_cache.InvalidateRegion(addr, size); |
| 561 | } | 561 | } |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 7e7a80740..c4c30d807 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -132,16 +132,12 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 132 | const bool use_accelerated = | 132 | const bool use_accelerated = |
| 133 | rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); | 133 | rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); |
| 134 | const bool is_srgb = use_accelerated && screen_info.is_srgb; | 134 | const bool is_srgb = use_accelerated && screen_info.is_srgb; |
| 135 | RenderScreenshot(*framebuffer, use_accelerated); | ||
| 135 | 136 | ||
| 136 | { | 137 | Frame* frame = present_manager.GetRenderFrame(); |
| 137 | std::scoped_lock lock{rasterizer.LockCaches()}; | 138 | blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb); |
| 138 | RenderScreenshot(*framebuffer, use_accelerated); | 139 | scheduler.Flush(*frame->render_ready); |
| 139 | 140 | present_manager.Present(frame); | |
| 140 | Frame* frame = present_manager.GetRenderFrame(); | ||
| 141 | blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb); | ||
| 142 | scheduler.Flush(*frame->render_ready); | ||
| 143 | present_manager.Present(frame); | ||
| 144 | } | ||
| 145 | 141 | ||
| 146 | gpu.RendererFrameEndNotify(); | 142 | gpu.RendererFrameEndNotify(); |
| 147 | rasterizer.TickFrame(); | 143 | rasterizer.TickFrame(); |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 52fc142d1..66483a900 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -137,6 +137,56 @@ BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWin | |||
| 137 | 137 | ||
| 138 | BlitScreen::~BlitScreen() = default; | 138 | BlitScreen::~BlitScreen() = default; |
| 139 | 139 | ||
| 140 | static Common::Rectangle<f32> NormalizeCrop(const Tegra::FramebufferConfig& framebuffer, | ||
| 141 | const ScreenInfo& screen_info) { | ||
| 142 | f32 left, top, right, bottom; | ||
| 143 | |||
| 144 | if (!framebuffer.crop_rect.IsEmpty()) { | ||
| 145 | // If crop rectangle is not empty, apply properties from rectangle. | ||
| 146 | left = static_cast<f32>(framebuffer.crop_rect.left); | ||
| 147 | top = static_cast<f32>(framebuffer.crop_rect.top); | ||
| 148 | right = static_cast<f32>(framebuffer.crop_rect.right); | ||
| 149 | bottom = static_cast<f32>(framebuffer.crop_rect.bottom); | ||
| 150 | } else { | ||
| 151 | // Otherwise, fall back to framebuffer dimensions. | ||
| 152 | left = 0; | ||
| 153 | top = 0; | ||
| 154 | right = static_cast<f32>(framebuffer.width); | ||
| 155 | bottom = static_cast<f32>(framebuffer.height); | ||
| 156 | } | ||
| 157 | |||
| 158 | // Apply transformation flags. | ||
| 159 | auto framebuffer_transform_flags = framebuffer.transform_flags; | ||
| 160 | |||
| 161 | if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipH)) { | ||
| 162 | // Switch left and right. | ||
| 163 | std::swap(left, right); | ||
| 164 | } | ||
| 165 | if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipV)) { | ||
| 166 | // Switch top and bottom. | ||
| 167 | std::swap(top, bottom); | ||
| 168 | } | ||
| 169 | |||
| 170 | framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipH; | ||
| 171 | framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipV; | ||
| 172 | if (True(framebuffer_transform_flags)) { | ||
| 173 | UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}", | ||
| 174 | static_cast<u32>(framebuffer_transform_flags)); | ||
| 175 | } | ||
| 176 | |||
| 177 | // Get the screen properties. | ||
| 178 | const f32 screen_width = static_cast<f32>(screen_info.width); | ||
| 179 | const f32 screen_height = static_cast<f32>(screen_info.height); | ||
| 180 | |||
| 181 | // Normalize coordinate space. | ||
| 182 | left /= screen_width; | ||
| 183 | top /= screen_height; | ||
| 184 | right /= screen_width; | ||
| 185 | bottom /= screen_height; | ||
| 186 | |||
| 187 | return Common::Rectangle<f32>(left, top, right, bottom); | ||
| 188 | } | ||
| 189 | |||
| 140 | void BlitScreen::Recreate() { | 190 | void BlitScreen::Recreate() { |
| 141 | present_manager.WaitPresent(); | 191 | present_manager.WaitPresent(); |
| 142 | scheduler.Finish(); | 192 | scheduler.Finish(); |
| @@ -354,17 +404,10 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, | |||
| 354 | source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view); | 404 | source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view); |
| 355 | } | 405 | } |
| 356 | if (fsr) { | 406 | if (fsr) { |
| 357 | auto crop_rect = framebuffer.crop_rect; | 407 | const auto crop_rect = NormalizeCrop(framebuffer, screen_info); |
| 358 | if (crop_rect.GetWidth() == 0) { | 408 | const VkExtent2D fsr_input_size{ |
| 359 | crop_rect.right = framebuffer.width; | 409 | .width = Settings::values.resolution_info.ScaleUp(screen_info.width), |
| 360 | } | 410 | .height = Settings::values.resolution_info.ScaleUp(screen_info.height), |
| 361 | if (crop_rect.GetHeight() == 0) { | ||
| 362 | crop_rect.bottom = framebuffer.height; | ||
| 363 | } | ||
| 364 | crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor); | ||
| 365 | VkExtent2D fsr_input_size{ | ||
| 366 | .width = Settings::values.resolution_info.ScaleUp(framebuffer.width), | ||
| 367 | .height = Settings::values.resolution_info.ScaleUp(framebuffer.height), | ||
| 368 | }; | 411 | }; |
| 369 | VkImageView fsr_image_view = | 412 | VkImageView fsr_image_view = |
| 370 | fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect); | 413 | fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect); |
| @@ -1397,61 +1440,37 @@ void BlitScreen::SetUniformData(BufferData& data, const Layout::FramebufferLayou | |||
| 1397 | 1440 | ||
| 1398 | void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, | 1441 | void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, |
| 1399 | const Layout::FramebufferLayout layout) const { | 1442 | const Layout::FramebufferLayout layout) const { |
| 1400 | const auto& framebuffer_transform_flags = framebuffer.transform_flags; | 1443 | f32 left, top, right, bottom; |
| 1401 | const auto& framebuffer_crop_rect = framebuffer.crop_rect; | ||
| 1402 | |||
| 1403 | static constexpr Common::Rectangle<f32> texcoords{0.f, 0.f, 1.f, 1.f}; | ||
| 1404 | auto left = texcoords.left; | ||
| 1405 | auto right = texcoords.right; | ||
| 1406 | |||
| 1407 | switch (framebuffer_transform_flags) { | ||
| 1408 | case Service::android::BufferTransformFlags::Unset: | ||
| 1409 | break; | ||
| 1410 | case Service::android::BufferTransformFlags::FlipV: | ||
| 1411 | // Flip the framebuffer vertically | ||
| 1412 | left = texcoords.right; | ||
| 1413 | right = texcoords.left; | ||
| 1414 | break; | ||
| 1415 | default: | ||
| 1416 | UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}", | ||
| 1417 | static_cast<u32>(framebuffer_transform_flags)); | ||
| 1418 | break; | ||
| 1419 | } | ||
| 1420 | 1444 | ||
| 1421 | UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0); | 1445 | if (fsr) { |
| 1422 | 1446 | // FSR has already applied the crop, so we just want to render the image | |
| 1423 | f32 left_start{}; | 1447 | // it has produced. |
| 1424 | if (framebuffer_crop_rect.Top() > 0) { | 1448 | left = 0; |
| 1425 | left_start = static_cast<f32>(framebuffer_crop_rect.Top()) / | 1449 | top = 0; |
| 1426 | static_cast<f32>(framebuffer_crop_rect.Bottom()); | 1450 | right = 1; |
| 1427 | } | 1451 | bottom = 1; |
| 1428 | f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width); | 1452 | } else { |
| 1429 | f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height); | 1453 | // Get the normalized crop rectangle. |
| 1430 | // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering | 1454 | const auto crop = NormalizeCrop(framebuffer, screen_info); |
| 1431 | // (e.g. handheld mode) on a 1920x1080 framebuffer. | 1455 | |
| 1432 | if (!fsr) { | 1456 | // Apply the crop. |
| 1433 | if (framebuffer_crop_rect.GetWidth() > 0) { | 1457 | left = crop.left; |
| 1434 | scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / | 1458 | top = crop.top; |
| 1435 | static_cast<f32>(screen_info.width); | 1459 | right = crop.right; |
| 1436 | } | 1460 | bottom = crop.bottom; |
| 1437 | if (framebuffer_crop_rect.GetHeight() > 0) { | ||
| 1438 | scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / | ||
| 1439 | static_cast<f32>(screen_info.height); | ||
| 1440 | } | ||
| 1441 | } | 1461 | } |
| 1442 | 1462 | ||
| 1463 | // Map the coordinates to the screen. | ||
| 1443 | const auto& screen = layout.screen; | 1464 | const auto& screen = layout.screen; |
| 1444 | const auto x = static_cast<f32>(screen.left); | 1465 | const auto x = static_cast<f32>(screen.left); |
| 1445 | const auto y = static_cast<f32>(screen.top); | 1466 | const auto y = static_cast<f32>(screen.top); |
| 1446 | const auto w = static_cast<f32>(screen.GetWidth()); | 1467 | const auto w = static_cast<f32>(screen.GetWidth()); |
| 1447 | const auto h = static_cast<f32>(screen.GetHeight()); | 1468 | const auto h = static_cast<f32>(screen.GetHeight()); |
| 1448 | data.vertices[0] = ScreenRectVertex(x, y, texcoords.top * scale_u, left_start + left * scale_v); | 1469 | |
| 1449 | data.vertices[1] = | 1470 | data.vertices[0] = ScreenRectVertex(x, y, left, top); |
| 1450 | ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left_start + left * scale_v); | 1471 | data.vertices[1] = ScreenRectVertex(x + w, y, right, top); |
| 1451 | data.vertices[2] = | 1472 | data.vertices[2] = ScreenRectVertex(x, y + h, left, bottom); |
| 1452 | ScreenRectVertex(x, y + h, texcoords.top * scale_u, left_start + right * scale_v); | 1473 | data.vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom); |
| 1453 | data.vertices[3] = | ||
| 1454 | ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, left_start + right * scale_v); | ||
| 1455 | } | 1474 | } |
| 1456 | 1475 | ||
| 1457 | void BlitScreen::CreateSMAA(VkExtent2D smaa_size) { | 1476 | void BlitScreen::CreateSMAA(VkExtent2D smaa_size) { |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 976c3f6a6..5958f52f7 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -79,13 +79,13 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo | |||
| 79 | } // Anonymous namespace | 79 | } // Anonymous namespace |
| 80 | 80 | ||
| 81 | Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params) | 81 | Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params) |
| 82 | : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params) {} | 82 | : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {} |
| 83 | 83 | ||
| 84 | Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_, | 84 | Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_, |
| 85 | VAddr cpu_addr_, u64 size_bytes_) | 85 | VAddr cpu_addr_, u64 size_bytes_) |
| 86 | : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_), | 86 | : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_), |
| 87 | device{&runtime.device}, buffer{ | 87 | device{&runtime.device}, buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())}, |
| 88 | CreateBuffer(*device, runtime.memory_allocator, SizeBytes())} { | 88 | tracker{SizeBytes()} { |
| 89 | if (runtime.device.HasDebuggingToolAttached()) { | 89 | if (runtime.device.HasDebuggingToolAttached()) { |
| 90 | buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str()); | 90 | buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str()); |
| 91 | } | 91 | } |
| @@ -359,12 +359,31 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const { | |||
| 359 | return static_cast<u32>(device.GetStorageBufferAlignment()); | 359 | return static_cast<u32>(device.GetStorageBufferAlignment()); |
| 360 | } | 360 | } |
| 361 | 361 | ||
| 362 | void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept { | ||
| 363 | for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { | ||
| 364 | it->ResetUsageTracking(); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 362 | void BufferCacheRuntime::Finish() { | 368 | void BufferCacheRuntime::Finish() { |
| 363 | scheduler.Finish(); | 369 | scheduler.Finish(); |
| 364 | } | 370 | } |
| 365 | 371 | ||
| 372 | bool BufferCacheRuntime::CanReorderUpload(const Buffer& buffer, | ||
| 373 | std::span<const VideoCommon::BufferCopy> copies) { | ||
| 374 | if (Settings::values.disable_buffer_reorder) { | ||
| 375 | return false; | ||
| 376 | } | ||
| 377 | const bool can_use_upload_cmdbuf = | ||
| 378 | std::ranges::all_of(copies, [&](const VideoCommon::BufferCopy& copy) { | ||
| 379 | return !buffer.IsRegionUsed(copy.dst_offset, copy.size); | ||
| 380 | }); | ||
| 381 | return can_use_upload_cmdbuf; | ||
| 382 | } | ||
| 383 | |||
| 366 | void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, | 384 | void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, |
| 367 | std::span<const VideoCommon::BufferCopy> copies, bool barrier) { | 385 | std::span<const VideoCommon::BufferCopy> copies, bool barrier, |
| 386 | bool can_reorder_upload) { | ||
| 368 | if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { | 387 | if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { |
| 369 | return; | 388 | return; |
| 370 | } | 389 | } |
| @@ -380,9 +399,18 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, | |||
| 380 | .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, | 399 | .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, |
| 381 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | 400 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, |
| 382 | }; | 401 | }; |
| 402 | |||
| 383 | // Measuring a popular game, this number never exceeds the specified size once data is warmed up | 403 | // Measuring a popular game, this number never exceeds the specified size once data is warmed up |
| 384 | boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size()); | 404 | boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size()); |
| 385 | std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); | 405 | std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); |
| 406 | if (src_buffer == staging_pool.StreamBuf() && can_reorder_upload) { | ||
| 407 | scheduler.RecordWithUploadBuffer([src_buffer, dst_buffer, vk_copies]( | ||
| 408 | vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) { | ||
| 409 | upload_cmdbuf.CopyBuffer(src_buffer, dst_buffer, vk_copies); | ||
| 410 | }); | ||
| 411 | return; | ||
| 412 | } | ||
| 413 | |||
| 386 | scheduler.RequestOutsideRenderPassOperationContext(); | 414 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 387 | scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { | 415 | scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { |
| 388 | if (barrier) { | 416 | if (barrier) { |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 833dfac45..0b3fbd6d0 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include "video_core/buffer_cache/buffer_cache_base.h" | 6 | #include "video_core/buffer_cache/buffer_cache_base.h" |
| 7 | #include "video_core/buffer_cache/memory_tracker_base.h" | 7 | #include "video_core/buffer_cache/memory_tracker_base.h" |
| 8 | #include "video_core/buffer_cache/usage_tracker.h" | ||
| 8 | #include "video_core/engines/maxwell_3d.h" | 9 | #include "video_core/engines/maxwell_3d.h" |
| 9 | #include "video_core/renderer_vulkan/vk_compute_pass.h" | 10 | #include "video_core/renderer_vulkan/vk_compute_pass.h" |
| 10 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" | 11 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" |
| @@ -34,6 +35,18 @@ public: | |||
| 34 | return *buffer; | 35 | return *buffer; |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 38 | [[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept { | ||
| 39 | return tracker.IsUsed(offset, size); | ||
| 40 | } | ||
| 41 | |||
| 42 | void MarkUsage(u64 offset, u64 size) noexcept { | ||
| 43 | tracker.Track(offset, size); | ||
| 44 | } | ||
| 45 | |||
| 46 | void ResetUsageTracking() noexcept { | ||
| 47 | tracker.Reset(); | ||
| 48 | } | ||
| 49 | |||
| 37 | operator VkBuffer() const noexcept { | 50 | operator VkBuffer() const noexcept { |
| 38 | return *buffer; | 51 | return *buffer; |
| 39 | } | 52 | } |
| @@ -49,6 +62,7 @@ private: | |||
| 49 | const Device* device{}; | 62 | const Device* device{}; |
| 50 | vk::Buffer buffer; | 63 | vk::Buffer buffer; |
| 51 | std::vector<BufferView> views; | 64 | std::vector<BufferView> views; |
| 65 | VideoCommon::UsageTracker tracker; | ||
| 52 | }; | 66 | }; |
| 53 | 67 | ||
| 54 | class QuadArrayIndexBuffer; | 68 | class QuadArrayIndexBuffer; |
| @@ -67,6 +81,8 @@ public: | |||
| 67 | ComputePassDescriptorQueue& compute_pass_descriptor_queue, | 81 | ComputePassDescriptorQueue& compute_pass_descriptor_queue, |
| 68 | DescriptorPool& descriptor_pool); | 82 | DescriptorPool& descriptor_pool); |
| 69 | 83 | ||
| 84 | void TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept; | ||
| 85 | |||
| 70 | void Finish(); | 86 | void Finish(); |
| 71 | 87 | ||
| 72 | u64 GetDeviceLocalMemory() const; | 88 | u64 GetDeviceLocalMemory() const; |
| @@ -81,12 +97,15 @@ public: | |||
| 81 | 97 | ||
| 82 | [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); | 98 | [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); |
| 83 | 99 | ||
| 100 | bool CanReorderUpload(const Buffer& buffer, std::span<const VideoCommon::BufferCopy> copies); | ||
| 101 | |||
| 84 | void FreeDeferredStagingBuffer(StagingBufferRef& ref); | 102 | void FreeDeferredStagingBuffer(StagingBufferRef& ref); |
| 85 | 103 | ||
| 86 | void PreCopyBarrier(); | 104 | void PreCopyBarrier(); |
| 87 | 105 | ||
| 88 | void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, | 106 | void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, |
| 89 | std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); | 107 | std::span<const VideoCommon::BufferCopy> copies, bool barrier, |
| 108 | bool can_reorder_upload = false); | ||
| 90 | 109 | ||
| 91 | void PostCopyBarrier(); | 110 | void PostCopyBarrier(); |
| 92 | 111 | ||
diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp index ce8f3f3c2..f7a05fbc0 100644 --- a/src/video_core/renderer_vulkan/vk_fsr.cpp +++ b/src/video_core/renderer_vulkan/vk_fsr.cpp | |||
| @@ -34,7 +34,7 @@ FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image | |||
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, | 36 | VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, |
| 37 | VkExtent2D input_image_extent, const Common::Rectangle<int>& crop_rect) { | 37 | VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect) { |
| 38 | 38 | ||
| 39 | UpdateDescriptorSet(image_index, image_view); | 39 | UpdateDescriptorSet(image_index, image_view); |
| 40 | 40 | ||
| @@ -61,15 +61,21 @@ VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView imag | |||
| 61 | 61 | ||
| 62 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *easu_pipeline); | 62 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *easu_pipeline); |
| 63 | 63 | ||
| 64 | const f32 input_image_width = static_cast<f32>(input_image_extent.width); | ||
| 65 | const f32 input_image_height = static_cast<f32>(input_image_extent.height); | ||
| 66 | const f32 output_image_width = static_cast<f32>(output_size.width); | ||
| 67 | const f32 output_image_height = static_cast<f32>(output_size.height); | ||
| 68 | const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width; | ||
| 69 | const f32 viewport_x = crop_rect.left * input_image_width; | ||
| 70 | const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height; | ||
| 71 | const f32 viewport_y = crop_rect.top * input_image_height; | ||
| 72 | |||
| 64 | std::array<u32, 4 * 4> push_constants; | 73 | std::array<u32, 4 * 4> push_constants; |
| 65 | FsrEasuConOffset( | 74 | FsrEasuConOffset(push_constants.data() + 0, push_constants.data() + 4, |
| 66 | push_constants.data() + 0, push_constants.data() + 4, push_constants.data() + 8, | 75 | push_constants.data() + 8, push_constants.data() + 12, |
| 67 | push_constants.data() + 12, | 76 | |
| 68 | 77 | viewport_width, viewport_height, input_image_width, input_image_height, | |
| 69 | static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()), | 78 | output_image_width, output_image_height, viewport_x, viewport_y); |
| 70 | static_cast<f32>(input_image_extent.width), static_cast<f32>(input_image_extent.height), | ||
| 71 | static_cast<f32>(output_size.width), static_cast<f32>(output_size.height), | ||
| 72 | static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top)); | ||
| 73 | cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); | 79 | cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); |
| 74 | 80 | ||
| 75 | { | 81 | { |
diff --git a/src/video_core/renderer_vulkan/vk_fsr.h b/src/video_core/renderer_vulkan/vk_fsr.h index 8bb9fc23a..3505c1416 100644 --- a/src/video_core/renderer_vulkan/vk_fsr.h +++ b/src/video_core/renderer_vulkan/vk_fsr.h | |||
| @@ -17,7 +17,7 @@ public: | |||
| 17 | explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, | 17 | explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, |
| 18 | VkExtent2D output_size); | 18 | VkExtent2D output_size); |
| 19 | VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, | 19 | VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, |
| 20 | VkExtent2D input_image_extent, const Common::Rectangle<int>& crop_rect); | 20 | VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect); |
| 21 | 21 | ||
| 22 | private: | 22 | private: |
| 23 | void CreateDescriptorPool(); | 23 | void CreateDescriptorPool(); |
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index 6b288b994..ac8b6e838 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp | |||
| @@ -100,12 +100,14 @@ void MasterSemaphore::Wait(u64 tick) { | |||
| 100 | Refresh(); | 100 | Refresh(); |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, | 103 | VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, |
| 104 | VkSemaphore wait_semaphore, u64 host_tick) { | 104 | VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, |
| 105 | u64 host_tick) { | ||
| 105 | if (semaphore) { | 106 | if (semaphore) { |
| 106 | return SubmitQueueTimeline(cmdbuf, signal_semaphore, wait_semaphore, host_tick); | 107 | return SubmitQueueTimeline(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, |
| 108 | host_tick); | ||
| 107 | } else { | 109 | } else { |
| 108 | return SubmitQueueFence(cmdbuf, signal_semaphore, wait_semaphore, host_tick); | 110 | return SubmitQueueFence(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, host_tick); |
| 109 | } | 111 | } |
| 110 | } | 112 | } |
| 111 | 113 | ||
| @@ -115,6 +117,7 @@ static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{ | |||
| 115 | }; | 117 | }; |
| 116 | 118 | ||
| 117 | VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, | 119 | VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, |
| 120 | vk::CommandBuffer& upload_cmdbuf, | ||
| 118 | VkSemaphore signal_semaphore, | 121 | VkSemaphore signal_semaphore, |
| 119 | VkSemaphore wait_semaphore, u64 host_tick) { | 122 | VkSemaphore wait_semaphore, u64 host_tick) { |
| 120 | const VkSemaphore timeline_semaphore = *semaphore; | 123 | const VkSemaphore timeline_semaphore = *semaphore; |
| @@ -123,6 +126,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, | |||
| 123 | const std::array signal_values{host_tick, u64(0)}; | 126 | const std::array signal_values{host_tick, u64(0)}; |
| 124 | const std::array signal_semaphores{timeline_semaphore, signal_semaphore}; | 127 | const std::array signal_semaphores{timeline_semaphore, signal_semaphore}; |
| 125 | 128 | ||
| 129 | const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf}; | ||
| 130 | |||
| 126 | const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; | 131 | const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; |
| 127 | const VkTimelineSemaphoreSubmitInfo timeline_si{ | 132 | const VkTimelineSemaphoreSubmitInfo timeline_si{ |
| 128 | .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, | 133 | .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, |
| @@ -138,8 +143,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, | |||
| 138 | .waitSemaphoreCount = num_wait_semaphores, | 143 | .waitSemaphoreCount = num_wait_semaphores, |
| 139 | .pWaitSemaphores = &wait_semaphore, | 144 | .pWaitSemaphores = &wait_semaphore, |
| 140 | .pWaitDstStageMask = wait_stage_masks.data(), | 145 | .pWaitDstStageMask = wait_stage_masks.data(), |
| 141 | .commandBufferCount = 1, | 146 | .commandBufferCount = static_cast<u32>(cmdbuffers.size()), |
| 142 | .pCommandBuffers = cmdbuf.address(), | 147 | .pCommandBuffers = cmdbuffers.data(), |
| 143 | .signalSemaphoreCount = num_signal_semaphores, | 148 | .signalSemaphoreCount = num_signal_semaphores, |
| 144 | .pSignalSemaphores = signal_semaphores.data(), | 149 | .pSignalSemaphores = signal_semaphores.data(), |
| 145 | }; | 150 | }; |
| @@ -147,19 +152,23 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, | |||
| 147 | return device.GetGraphicsQueue().Submit(submit_info); | 152 | return device.GetGraphicsQueue().Submit(submit_info); |
| 148 | } | 153 | } |
| 149 | 154 | ||
| 150 | VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, | 155 | VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, |
| 151 | VkSemaphore wait_semaphore, u64 host_tick) { | 156 | vk::CommandBuffer& upload_cmdbuf, |
| 157 | VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, | ||
| 158 | u64 host_tick) { | ||
| 152 | const u32 num_signal_semaphores = signal_semaphore ? 1 : 0; | 159 | const u32 num_signal_semaphores = signal_semaphore ? 1 : 0; |
| 153 | const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; | 160 | const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; |
| 154 | 161 | ||
| 162 | const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf}; | ||
| 163 | |||
| 155 | const VkSubmitInfo submit_info{ | 164 | const VkSubmitInfo submit_info{ |
| 156 | .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | 165 | .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, |
| 157 | .pNext = nullptr, | 166 | .pNext = nullptr, |
| 158 | .waitSemaphoreCount = num_wait_semaphores, | 167 | .waitSemaphoreCount = num_wait_semaphores, |
| 159 | .pWaitSemaphores = &wait_semaphore, | 168 | .pWaitSemaphores = &wait_semaphore, |
| 160 | .pWaitDstStageMask = wait_stage_masks.data(), | 169 | .pWaitDstStageMask = wait_stage_masks.data(), |
| 161 | .commandBufferCount = 1, | 170 | .commandBufferCount = static_cast<u32>(cmdbuffers.size()), |
| 162 | .pCommandBuffers = cmdbuf.address(), | 171 | .pCommandBuffers = cmdbuffers.data(), |
| 163 | .signalSemaphoreCount = num_signal_semaphores, | 172 | .signalSemaphoreCount = num_signal_semaphores, |
| 164 | .pSignalSemaphores = &signal_semaphore, | 173 | .pSignalSemaphores = &signal_semaphore, |
| 165 | }; | 174 | }; |
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h index 3f599d7bd..7dfb93ffb 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h | |||
| @@ -52,14 +52,16 @@ public: | |||
| 52 | void Wait(u64 tick); | 52 | void Wait(u64 tick); |
| 53 | 53 | ||
| 54 | /// Submits the device graphics queue, updating the tick as necessary | 54 | /// Submits the device graphics queue, updating the tick as necessary |
| 55 | VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, | 55 | VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, |
| 56 | VkSemaphore wait_semaphore, u64 host_tick); | 56 | VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, u64 host_tick); |
| 57 | 57 | ||
| 58 | private: | 58 | private: |
| 59 | VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, | 59 | VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, |
| 60 | VkSemaphore wait_semaphore, u64 host_tick); | 60 | VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, |
| 61 | VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, | 61 | u64 host_tick); |
| 62 | VkSemaphore wait_semaphore, u64 host_tick); | 62 | VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, |
| 63 | VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, | ||
| 64 | u64 host_tick); | ||
| 63 | 65 | ||
| 64 | void WaitThread(std::stop_token token); | 66 | void WaitThread(std::stop_token token); |
| 65 | 67 | ||
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 0d604eee3..2a13b2a72 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -263,6 +263,22 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program | |||
| 263 | info.y_negate = key.state.y_negate != 0; | 263 | info.y_negate = key.state.y_negate != 0; |
| 264 | return info; | 264 | return info; |
| 265 | } | 265 | } |
| 266 | |||
| 267 | size_t GetTotalPipelineWorkers() { | ||
| 268 | const size_t max_core_threads = | ||
| 269 | std::max<size_t>(static_cast<size_t>(std::thread::hardware_concurrency()), 2ULL) - 1ULL; | ||
| 270 | #ifdef ANDROID | ||
| 271 | // Leave at least a few cores free in android | ||
| 272 | constexpr size_t free_cores = 3ULL; | ||
| 273 | if (max_core_threads <= free_cores) { | ||
| 274 | return 1ULL; | ||
| 275 | } | ||
| 276 | return max_core_threads - free_cores; | ||
| 277 | #else | ||
| 278 | return max_core_threads; | ||
| 279 | #endif | ||
| 280 | } | ||
| 281 | |||
| 266 | } // Anonymous namespace | 282 | } // Anonymous namespace |
| 267 | 283 | ||
| 268 | size_t ComputePipelineCacheKey::Hash() const noexcept { | 284 | size_t ComputePipelineCacheKey::Hash() const noexcept { |
| @@ -294,11 +310,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device | |||
| 294 | texture_cache{texture_cache_}, shader_notify{shader_notify_}, | 310 | texture_cache{texture_cache_}, shader_notify{shader_notify_}, |
| 295 | use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, | 311 | use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, |
| 296 | use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()}, | 312 | use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()}, |
| 297 | #ifdef ANDROID | 313 | workers(device.HasBrokenParallelShaderCompiling() ? 1ULL : GetTotalPipelineWorkers(), |
| 298 | workers(1, "VkPipelineBuilder"), | 314 | "VkPipelineBuilder"), |
| 299 | #else | ||
| 300 | workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), | ||
| 301 | #endif | ||
| 302 | serialization_thread(1, "VkPipelineSerialization") { | 315 | serialization_thread(1, "VkPipelineSerialization") { |
| 303 | const auto& float_control{device.FloatControlProperties()}; | 316 | const auto& float_control{device.FloatControlProperties()}; |
| 304 | const VkDriverId driver_id{device.GetDriverID()}; | 317 | const VkDriverId driver_id{device.GetDriverID()}; |
| @@ -338,6 +351,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device | |||
| 338 | .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), | 351 | .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), |
| 339 | .support_native_ndc = device.IsExtDepthClipControlSupported(), | 352 | .support_native_ndc = device.IsExtDepthClipControlSupported(), |
| 340 | .support_scaled_attributes = !device.MustEmulateScaledFormats(), | 353 | .support_scaled_attributes = !device.MustEmulateScaledFormats(), |
| 354 | .support_multi_viewport = device.SupportsMultiViewport(), | ||
| 341 | 355 | ||
| 342 | .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), | 356 | .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), |
| 343 | 357 | ||
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 66c03bf17..078777cdd 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp | |||
| @@ -211,6 +211,13 @@ public: | |||
| 211 | return; | 211 | return; |
| 212 | } | 212 | } |
| 213 | PauseCounter(); | 213 | PauseCounter(); |
| 214 | const auto driver_id = device.GetDriverID(); | ||
| 215 | if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || | ||
| 216 | driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { | ||
| 217 | pending_sync.clear(); | ||
| 218 | sync_values_stash.clear(); | ||
| 219 | return; | ||
| 220 | } | ||
| 214 | sync_values_stash.clear(); | 221 | sync_values_stash.clear(); |
| 215 | sync_values_stash.emplace_back(); | 222 | sync_values_stash.emplace_back(); |
| 216 | std::vector<HostSyncValues>* sync_values = &sync_values_stash.back(); | 223 | std::vector<HostSyncValues>* sync_values = &sync_values_stash.back(); |
| @@ -1378,6 +1385,12 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku | |||
| 1378 | return true; | 1385 | return true; |
| 1379 | } | 1386 | } |
| 1380 | 1387 | ||
| 1388 | auto driver_id = impl->device.GetDriverID(); | ||
| 1389 | if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || | ||
| 1390 | driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { | ||
| 1391 | return true; | ||
| 1392 | } | ||
| 1393 | |||
| 1381 | for (size_t i = 0; i < 2; i++) { | 1394 | for (size_t i = 0; i < 2; i++) { |
| 1382 | is_null[i] = !is_in_ac[i] && check_value(objects[i]->address); | 1395 | is_null[i] = !is_in_ac[i] && check_value(objects[i]->address); |
| 1383 | } | 1396 | } |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 059b7cb40..e0ab1eaac 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -82,7 +82,7 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in | |||
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | if (y_negate) { | 84 | if (y_negate) { |
| 85 | y += height; | 85 | y += conv(static_cast<f32>(regs.surface_clip.height)); |
| 86 | height = -height; | 86 | height = -height; |
| 87 | } | 87 | } |
| 88 | 88 | ||
| @@ -199,7 +199,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { | |||
| 199 | if (!pipeline) { | 199 | if (!pipeline) { |
| 200 | return; | 200 | return; |
| 201 | } | 201 | } |
| 202 | std::scoped_lock lock{LockCaches()}; | 202 | std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; |
| 203 | // update engine as channel may be different. | 203 | // update engine as channel may be different. |
| 204 | pipeline->SetEngine(maxwell3d, gpu_memory); | 204 | pipeline->SetEngine(maxwell3d, gpu_memory); |
| 205 | pipeline->Configure(is_indexed); | 205 | pipeline->Configure(is_indexed); |
| @@ -621,7 +621,7 @@ void RasterizerVulkan::OnCacheInvalidation(VAddr addr, u64 size) { | |||
| 621 | } | 621 | } |
| 622 | { | 622 | { |
| 623 | std::scoped_lock lock{buffer_cache.mutex}; | 623 | std::scoped_lock lock{buffer_cache.mutex}; |
| 624 | buffer_cache.CachedWriteMemory(addr, size); | 624 | buffer_cache.WriteMemory(addr, size); |
| 625 | } | 625 | } |
| 626 | pipeline_cache.InvalidateRegion(addr, size); | 626 | pipeline_cache.InvalidateRegion(addr, size); |
| 627 | } | 627 | } |
| @@ -710,7 +710,6 @@ void RasterizerVulkan::TiledCacheBarrier() { | |||
| 710 | } | 710 | } |
| 711 | 711 | ||
| 712 | void RasterizerVulkan::FlushCommands() { | 712 | void RasterizerVulkan::FlushCommands() { |
| 713 | std::scoped_lock lock{LockCaches()}; | ||
| 714 | if (draw_counter == 0) { | 713 | if (draw_counter == 0) { |
| 715 | return; | 714 | return; |
| 716 | } | 715 | } |
| @@ -808,7 +807,6 @@ void RasterizerVulkan::FlushWork() { | |||
| 808 | if ((++draw_counter & 7) != 7) { | 807 | if ((++draw_counter & 7) != 7) { |
| 809 | return; | 808 | return; |
| 810 | } | 809 | } |
| 811 | std::scoped_lock lock{LockCaches()}; | ||
| 812 | if (draw_counter < DRAWS_TO_DISPATCH) { | 810 | if (draw_counter < DRAWS_TO_DISPATCH) { |
| 813 | // Send recorded tasks to the worker thread | 811 | // Send recorded tasks to the worker thread |
| 814 | scheduler.DispatchWork(); | 812 | scheduler.DispatchWork(); |
| @@ -923,9 +921,13 @@ void RasterizerVulkan::UpdateDynamicStates() { | |||
| 923 | } | 921 | } |
| 924 | 922 | ||
| 925 | void RasterizerVulkan::HandleTransformFeedback() { | 923 | void RasterizerVulkan::HandleTransformFeedback() { |
| 924 | static std::once_flag warn_unsupported; | ||
| 925 | |||
| 926 | const auto& regs = maxwell3d->regs; | 926 | const auto& regs = maxwell3d->regs; |
| 927 | if (!device.IsExtTransformFeedbackSupported()) { | 927 | if (!device.IsExtTransformFeedbackSupported()) { |
| 928 | LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); | 928 | std::call_once(warn_unsupported, [&] { |
| 929 | LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported"); | ||
| 930 | }); | ||
| 929 | return; | 931 | return; |
| 930 | } | 932 | } |
| 931 | query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount, | 933 | query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount, |
| @@ -1503,7 +1505,7 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) | |||
| 1503 | void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) { | 1505 | void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) { |
| 1504 | CreateChannel(channel); | 1506 | CreateChannel(channel); |
| 1505 | { | 1507 | { |
| 1506 | std::scoped_lock lock{LockCaches()}; | 1508 | std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; |
| 1507 | texture_cache.CreateChannel(channel); | 1509 | texture_cache.CreateChannel(channel); |
| 1508 | buffer_cache.CreateChannel(channel); | 1510 | buffer_cache.CreateChannel(channel); |
| 1509 | } | 1511 | } |
| @@ -1516,7 +1518,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) { | |||
| 1516 | const s32 channel_id = channel.bind_id; | 1518 | const s32 channel_id = channel.bind_id; |
| 1517 | BindToChannel(channel_id); | 1519 | BindToChannel(channel_id); |
| 1518 | { | 1520 | { |
| 1519 | std::scoped_lock lock{LockCaches()}; | 1521 | std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; |
| 1520 | texture_cache.BindToChannel(channel_id); | 1522 | texture_cache.BindToChannel(channel_id); |
| 1521 | buffer_cache.BindToChannel(channel_id); | 1523 | buffer_cache.BindToChannel(channel_id); |
| 1522 | } | 1524 | } |
| @@ -1529,7 +1531,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) { | |||
| 1529 | void RasterizerVulkan::ReleaseChannel(s32 channel_id) { | 1531 | void RasterizerVulkan::ReleaseChannel(s32 channel_id) { |
| 1530 | EraseChannel(channel_id); | 1532 | EraseChannel(channel_id); |
| 1531 | { | 1533 | { |
| 1532 | std::scoped_lock lock{LockCaches()}; | 1534 | std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; |
| 1533 | texture_cache.EraseChannel(channel_id); | 1535 | texture_cache.EraseChannel(channel_id); |
| 1534 | buffer_cache.EraseChannel(channel_id); | 1536 | buffer_cache.EraseChannel(channel_id); |
| 1535 | } | 1537 | } |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index ce3dfbaab..ad069556c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -133,10 +133,6 @@ public: | |||
| 133 | 133 | ||
| 134 | void ReleaseChannel(s32 channel_id) override; | 134 | void ReleaseChannel(s32 channel_id) override; |
| 135 | 135 | ||
| 136 | std::scoped_lock<std::recursive_mutex, std::recursive_mutex> LockCaches() { | ||
| 137 | return std::scoped_lock{buffer_cache.mutex, texture_cache.mutex}; | ||
| 138 | } | ||
| 139 | |||
| 140 | private: | 136 | private: |
| 141 | static constexpr size_t MAX_TEXTURES = 192; | 137 | static constexpr size_t MAX_TEXTURES = 192; |
| 142 | static constexpr size_t MAX_IMAGES = 48; | 138 | static constexpr size_t MAX_IMAGES = 48; |
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 3be7837f4..146923db4 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp | |||
| @@ -22,11 +22,12 @@ namespace Vulkan { | |||
| 22 | 22 | ||
| 23 | MICROPROFILE_DECLARE(Vulkan_WaitForWorker); | 23 | MICROPROFILE_DECLARE(Vulkan_WaitForWorker); |
| 24 | 24 | ||
| 25 | void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) { | 25 | void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf, |
| 26 | vk::CommandBuffer upload_cmdbuf) { | ||
| 26 | auto command = first; | 27 | auto command = first; |
| 27 | while (command != nullptr) { | 28 | while (command != nullptr) { |
| 28 | auto next = command->GetNext(); | 29 | auto next = command->GetNext(); |
| 29 | command->Execute(cmdbuf); | 30 | command->Execute(cmdbuf, upload_cmdbuf); |
| 30 | command->~Command(); | 31 | command->~Command(); |
| 31 | command = next; | 32 | command = next; |
| 32 | } | 33 | } |
| @@ -180,7 +181,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) { | |||
| 180 | // Perform the work, tracking whether the chunk was a submission | 181 | // Perform the work, tracking whether the chunk was a submission |
| 181 | // before executing. | 182 | // before executing. |
| 182 | const bool has_submit = work->HasSubmit(); | 183 | const bool has_submit = work->HasSubmit(); |
| 183 | work->ExecuteAll(current_cmdbuf); | 184 | work->ExecuteAll(current_cmdbuf, current_upload_cmdbuf); |
| 184 | 185 | ||
| 185 | // If the chunk was a submission, reallocate the command buffer. | 186 | // If the chunk was a submission, reallocate the command buffer. |
| 186 | if (has_submit) { | 187 | if (has_submit) { |
| @@ -205,6 +206,13 @@ void Scheduler::AllocateWorkerCommandBuffer() { | |||
| 205 | .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, | 206 | .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, |
| 206 | .pInheritanceInfo = nullptr, | 207 | .pInheritanceInfo = nullptr, |
| 207 | }); | 208 | }); |
| 209 | current_upload_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader()); | ||
| 210 | current_upload_cmdbuf.Begin({ | ||
| 211 | .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | ||
| 212 | .pNext = nullptr, | ||
| 213 | .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, | ||
| 214 | .pInheritanceInfo = nullptr, | ||
| 215 | }); | ||
| 208 | } | 216 | } |
| 209 | 217 | ||
| 210 | u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { | 218 | u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { |
| @@ -212,7 +220,17 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se | |||
| 212 | InvalidateState(); | 220 | InvalidateState(); |
| 213 | 221 | ||
| 214 | const u64 signal_value = master_semaphore->NextTick(); | 222 | const u64 signal_value = master_semaphore->NextTick(); |
| 215 | Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) { | 223 | RecordWithUploadBuffer([signal_semaphore, wait_semaphore, signal_value, |
| 224 | this](vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) { | ||
| 225 | static constexpr VkMemoryBarrier WRITE_BARRIER{ | ||
| 226 | .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, | ||
| 227 | .pNext = nullptr, | ||
| 228 | .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, | ||
| 229 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 230 | }; | ||
| 231 | upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, | ||
| 232 | VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER); | ||
| 233 | upload_cmdbuf.End(); | ||
| 216 | cmdbuf.End(); | 234 | cmdbuf.End(); |
| 217 | 235 | ||
| 218 | if (on_submit) { | 236 | if (on_submit) { |
| @@ -221,7 +239,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se | |||
| 221 | 239 | ||
| 222 | std::scoped_lock lock{submit_mutex}; | 240 | std::scoped_lock lock{submit_mutex}; |
| 223 | switch (const VkResult result = master_semaphore->SubmitQueue( | 241 | switch (const VkResult result = master_semaphore->SubmitQueue( |
| 224 | cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { | 242 | cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { |
| 225 | case VK_SUCCESS: | 243 | case VK_SUCCESS: |
| 226 | break; | 244 | break; |
| 227 | case VK_ERROR_DEVICE_LOST: | 245 | case VK_ERROR_DEVICE_LOST: |
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index da03803aa..f8d8ca80a 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h | |||
| @@ -80,7 +80,8 @@ public: | |||
| 80 | 80 | ||
| 81 | /// Send work to a separate thread. | 81 | /// Send work to a separate thread. |
| 82 | template <typename T> | 82 | template <typename T> |
| 83 | void Record(T&& command) { | 83 | requires std::is_invocable_v<T, vk::CommandBuffer, vk::CommandBuffer> |
| 84 | void RecordWithUploadBuffer(T&& command) { | ||
| 84 | if (chunk->Record(command)) { | 85 | if (chunk->Record(command)) { |
| 85 | return; | 86 | return; |
| 86 | } | 87 | } |
| @@ -88,6 +89,15 @@ public: | |||
| 88 | (void)chunk->Record(command); | 89 | (void)chunk->Record(command); |
| 89 | } | 90 | } |
| 90 | 91 | ||
| 92 | template <typename T> | ||
| 93 | requires std::is_invocable_v<T, vk::CommandBuffer> | ||
| 94 | void Record(T&& c) { | ||
| 95 | this->RecordWithUploadBuffer( | ||
| 96 | [command = std::move(c)](vk::CommandBuffer cmdbuf, vk::CommandBuffer) { | ||
| 97 | command(cmdbuf); | ||
| 98 | }); | ||
| 99 | } | ||
| 100 | |||
| 91 | /// Returns the current command buffer tick. | 101 | /// Returns the current command buffer tick. |
| 92 | [[nodiscard]] u64 CurrentTick() const noexcept { | 102 | [[nodiscard]] u64 CurrentTick() const noexcept { |
| 93 | return master_semaphore->CurrentTick(); | 103 | return master_semaphore->CurrentTick(); |
| @@ -119,7 +129,7 @@ private: | |||
| 119 | public: | 129 | public: |
| 120 | virtual ~Command() = default; | 130 | virtual ~Command() = default; |
| 121 | 131 | ||
| 122 | virtual void Execute(vk::CommandBuffer cmdbuf) const = 0; | 132 | virtual void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const = 0; |
| 123 | 133 | ||
| 124 | Command* GetNext() const { | 134 | Command* GetNext() const { |
| 125 | return next; | 135 | return next; |
| @@ -142,8 +152,8 @@ private: | |||
| 142 | TypedCommand(TypedCommand&&) = delete; | 152 | TypedCommand(TypedCommand&&) = delete; |
| 143 | TypedCommand& operator=(TypedCommand&&) = delete; | 153 | TypedCommand& operator=(TypedCommand&&) = delete; |
| 144 | 154 | ||
| 145 | void Execute(vk::CommandBuffer cmdbuf) const override { | 155 | void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const override { |
| 146 | command(cmdbuf); | 156 | command(cmdbuf, upload_cmdbuf); |
| 147 | } | 157 | } |
| 148 | 158 | ||
| 149 | private: | 159 | private: |
| @@ -152,7 +162,7 @@ private: | |||
| 152 | 162 | ||
| 153 | class CommandChunk final { | 163 | class CommandChunk final { |
| 154 | public: | 164 | public: |
| 155 | void ExecuteAll(vk::CommandBuffer cmdbuf); | 165 | void ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf); |
| 156 | 166 | ||
| 157 | template <typename T> | 167 | template <typename T> |
| 158 | bool Record(T& command) { | 168 | bool Record(T& command) { |
| @@ -228,6 +238,7 @@ private: | |||
| 228 | VideoCommon::QueryCacheBase<QueryCacheParams>* query_cache = nullptr; | 238 | VideoCommon::QueryCacheBase<QueryCacheParams>* query_cache = nullptr; |
| 229 | 239 | ||
| 230 | vk::CommandBuffer current_cmdbuf; | 240 | vk::CommandBuffer current_cmdbuf; |
| 241 | vk::CommandBuffer current_upload_cmdbuf; | ||
| 231 | 242 | ||
| 232 | std::unique_ptr<CommandChunk> chunk; | 243 | std::unique_ptr<CommandChunk> chunk; |
| 233 | std::function<void()> on_submit; | 244 | std::function<void()> on_submit; |
diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/vk_smaa.cpp index 5efd7d66e..70644ea82 100644 --- a/src/video_core/renderer_vulkan/vk_smaa.cpp +++ b/src/video_core/renderer_vulkan/vk_smaa.cpp | |||
| @@ -672,7 +672,7 @@ void SMAA::UploadImages(Scheduler& scheduler) { | |||
| 672 | UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, | 672 | UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, |
| 673 | VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); | 673 | VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); |
| 674 | 674 | ||
| 675 | scheduler.Record([&](vk::CommandBuffer& cmdbuf) { | 675 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { |
| 676 | for (auto& images : m_dynamic_images) { | 676 | for (auto& images : m_dynamic_images) { |
| 677 | for (size_t i = 0; i < MaxDynamicImage; i++) { | 677 | for (size_t i = 0; i < MaxDynamicImage; i++) { |
| 678 | ClearColorImage(cmdbuf, *images.images[i]); | 678 | ClearColorImage(cmdbuf, *images.images[i]); |
| @@ -707,7 +707,7 @@ VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_ | |||
| 707 | UpdateDescriptorSets(source_image_view, image_index); | 707 | UpdateDescriptorSets(source_image_view, image_index); |
| 708 | 708 | ||
| 709 | scheduler.RequestOutsideRenderPassOperationContext(); | 709 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 710 | scheduler.Record([=, this](vk::CommandBuffer& cmdbuf) { | 710 | scheduler.Record([=, this](vk::CommandBuffer cmdbuf) { |
| 711 | TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); | 711 | TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); |
| 712 | TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); | 712 | TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); |
| 713 | BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer, | 713 | BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer, |
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index d3deb9072..f63a20327 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h | |||
| @@ -36,6 +36,10 @@ public: | |||
| 36 | StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false); | 36 | StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false); |
| 37 | void FreeDeferred(StagingBufferRef& ref); | 37 | void FreeDeferred(StagingBufferRef& ref); |
| 38 | 38 | ||
| 39 | [[nodiscard]] VkBuffer StreamBuf() const noexcept { | ||
| 40 | return *stream_buffer; | ||
| 41 | } | ||
| 42 | |||
| 39 | void TickFrame(); | 43 | void TickFrame(); |
| 40 | 44 | ||
| 41 | private: | 45 | private: |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index de34f6d49..5dbec2e62 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -1785,8 +1785,22 @@ ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, | |||
| 1785 | : VideoCommon::ImageViewBase{info, view_info, gpu_addr_}, | 1785 | : VideoCommon::ImageViewBase{info, view_info, gpu_addr_}, |
| 1786 | buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {} | 1786 | buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {} |
| 1787 | 1787 | ||
| 1788 | ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams& params) | 1788 | ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params) |
| 1789 | : VideoCommon::ImageViewBase{params} {} | 1789 | : VideoCommon::ImageViewBase{params}, device{&runtime.device} { |
| 1790 | if (device->HasNullDescriptor()) { | ||
| 1791 | return; | ||
| 1792 | } | ||
| 1793 | |||
| 1794 | // Handle fallback for devices without nullDescriptor | ||
| 1795 | ImageInfo info{}; | ||
| 1796 | info.format = PixelFormat::A8B8G8R8_UNORM; | ||
| 1797 | |||
| 1798 | null_image = MakeImage(*device, runtime.memory_allocator, info, {}); | ||
| 1799 | image_handle = *null_image; | ||
| 1800 | for (u32 i = 0; i < Shader::NUM_TEXTURE_TYPES; i++) { | ||
| 1801 | image_views[i] = MakeView(VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_ASPECT_COLOR_BIT); | ||
| 1802 | } | ||
| 1803 | } | ||
| 1790 | 1804 | ||
| 1791 | ImageView::~ImageView() = default; | 1805 | ImageView::~ImageView() = default; |
| 1792 | 1806 | ||
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 7a0807709..edf5d7635 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -267,6 +267,7 @@ private: | |||
| 267 | vk::ImageView depth_view; | 267 | vk::ImageView depth_view; |
| 268 | vk::ImageView stencil_view; | 268 | vk::ImageView stencil_view; |
| 269 | vk::ImageView color_view; | 269 | vk::ImageView color_view; |
| 270 | vk::Image null_image; | ||
| 270 | VkImage image_handle = VK_NULL_HANDLE; | 271 | VkImage image_handle = VK_NULL_HANDLE; |
| 271 | VkImageView render_target = VK_NULL_HANDLE; | 272 | VkImageView render_target = VK_NULL_HANDLE; |
| 272 | VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; | 273 | VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; |
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h index 9df6a2903..3ffa2a661 100644 --- a/src/video_core/texture_cache/slot_vector.h +++ b/src/video_core/texture_cache/slot_vector.h | |||
| @@ -138,6 +138,10 @@ public: | |||
| 138 | return Iterator(this, SlotId{SlotId::INVALID_INDEX}); | 138 | return Iterator(this, SlotId{SlotId::INVALID_INDEX}); |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | [[nodiscard]] size_t size() const noexcept { | ||
| 142 | return values_capacity - free_list.size(); | ||
| 143 | } | ||
| 144 | |||
| 141 | private: | 145 | private: |
| 142 | struct NonTrivialDummy { | 146 | struct NonTrivialDummy { |
| 143 | NonTrivialDummy() noexcept {} | 147 | NonTrivialDummy() noexcept {} |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index e518756d2..fde36a49c 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -635,6 +635,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 635 | has_broken_cube_compatibility = true; | 635 | has_broken_cube_compatibility = true; |
| 636 | } | 636 | } |
| 637 | } | 637 | } |
| 638 | if (is_qualcomm) { | ||
| 639 | const u32 version = (properties.properties.driverVersion << 3) >> 3; | ||
| 640 | if (version < VK_MAKE_API_VERSION(0, 255, 615, 512)) { | ||
| 641 | has_broken_parallel_compiling = true; | ||
| 642 | } | ||
| 643 | } | ||
| 638 | if (extensions.sampler_filter_minmax && is_amd) { | 644 | if (extensions.sampler_filter_minmax && is_amd) { |
| 639 | // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. | 645 | // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. |
| 640 | if (!features.shader_float16_int8.shaderFloat16) { | 646 | if (!features.shader_float16_int8.shaderFloat16) { |
| @@ -863,7 +869,8 @@ bool Device::ShouldBoostClocks() const { | |||
| 863 | driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA || | 869 | driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA || |
| 864 | driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP; | 870 | driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP; |
| 865 | 871 | ||
| 866 | const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; | 872 | const bool is_steam_deck = (vendor_id == 0x1002 && device_id == 0x163F) || |
| 873 | (vendor_id == 0x1002 && device_id == 0x1435); | ||
| 867 | 874 | ||
| 868 | const bool is_debugging = this->HasDebuggingToolAttached(); | 875 | const bool is_debugging = this->HasDebuggingToolAttached(); |
| 869 | 876 | ||
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index b213ed7dd..4f3846345 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h | |||
| @@ -102,6 +102,7 @@ VK_DEFINE_HANDLE(VmaAllocator) | |||
| 102 | EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \ | 102 | EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \ |
| 103 | EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \ | 103 | EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \ |
| 104 | EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \ | 104 | EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \ |
| 105 | EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \ | ||
| 105 | EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \ | 106 | EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \ |
| 106 | EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \ | 107 | EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \ |
| 107 | EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \ | 108 | EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \ |
| @@ -599,6 +600,11 @@ public: | |||
| 599 | return has_broken_cube_compatibility; | 600 | return has_broken_cube_compatibility; |
| 600 | } | 601 | } |
| 601 | 602 | ||
| 603 | /// Returns true if parallel shader compiling has issues with the current driver. | ||
| 604 | bool HasBrokenParallelShaderCompiling() const { | ||
| 605 | return has_broken_parallel_compiling; | ||
| 606 | } | ||
| 607 | |||
| 602 | /// Returns the vendor name reported from Vulkan. | 608 | /// Returns the vendor name reported from Vulkan. |
| 603 | std::string_view GetVendorName() const { | 609 | std::string_view GetVendorName() const { |
| 604 | return properties.driver.driverName; | 610 | return properties.driver.driverName; |
| @@ -663,6 +669,10 @@ public: | |||
| 663 | return supports_conditional_barriers; | 669 | return supports_conditional_barriers; |
| 664 | } | 670 | } |
| 665 | 671 | ||
| 672 | bool SupportsMultiViewport() const { | ||
| 673 | return features2.features.multiViewport; | ||
| 674 | } | ||
| 675 | |||
| 666 | [[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id, | 676 | [[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id, |
| 667 | u32 driver_version) { | 677 | u32 driver_version) { |
| 668 | if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { | 678 | if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { |
| @@ -794,6 +804,7 @@ private: | |||
| 794 | bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. | 804 | bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. |
| 795 | bool has_broken_compute{}; ///< Compute shaders can cause crashes | 805 | bool has_broken_compute{}; ///< Compute shaders can cause crashes |
| 796 | bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit | 806 | bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit |
| 807 | bool has_broken_parallel_compiling{}; ///< Has broken parallel shader compiling. | ||
| 797 | bool has_renderdoc{}; ///< Has RenderDoc attached | 808 | bool has_renderdoc{}; ///< Has RenderDoc attached |
| 798 | bool has_nsight_graphics{}; ///< Has Nsight Graphics attached | 809 | bool has_nsight_graphics{}; ///< Has Nsight Graphics attached |
| 799 | bool supports_d24_depth{}; ///< Supports D24 depth buffers. | 810 | bool supports_d24_depth{}; ///< Supports D24 depth buffers. |
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 0487cd3b6..a0c70797f 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h | |||
| @@ -1101,6 +1101,10 @@ public: | |||
| 1101 | return &handle; | 1101 | return &handle; |
| 1102 | } | 1102 | } |
| 1103 | 1103 | ||
| 1104 | VkCommandBuffer operator*() const noexcept { | ||
| 1105 | return handle; | ||
| 1106 | } | ||
| 1107 | |||
| 1104 | void Begin(const VkCommandBufferBeginInfo& begin_info) const { | 1108 | void Begin(const VkCommandBufferBeginInfo& begin_info) const { |
| 1105 | Check(dld->vkBeginCommandBuffer(handle, &begin_info)); | 1109 | Check(dld->vkBeginCommandBuffer(handle, &begin_info)); |
| 1106 | } | 1110 | } |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 33e1fb663..90278052a 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -38,8 +38,6 @@ add_executable(yuzu | |||
| 38 | compatdb.ui | 38 | compatdb.ui |
| 39 | compatibility_list.cpp | 39 | compatibility_list.cpp |
| 40 | compatibility_list.h | 40 | compatibility_list.h |
| 41 | configuration/config.cpp | ||
| 42 | configuration/config.h | ||
| 43 | configuration/configuration_shared.cpp | 41 | configuration/configuration_shared.cpp |
| 44 | configuration/configuration_shared.h | 42 | configuration/configuration_shared.h |
| 45 | configuration/configure.ui | 43 | configuration/configure.ui |
| @@ -147,6 +145,8 @@ add_executable(yuzu | |||
| 147 | configuration/shared_translation.h | 145 | configuration/shared_translation.h |
| 148 | configuration/shared_widget.cpp | 146 | configuration/shared_widget.cpp |
| 149 | configuration/shared_widget.h | 147 | configuration/shared_widget.h |
| 148 | configuration/qt_config.cpp | ||
| 149 | configuration/qt_config.h | ||
| 150 | debugger/console.cpp | 150 | debugger/console.cpp |
| 151 | debugger/console.h | 151 | debugger/console.h |
| 152 | debugger/controller.cpp | 152 | debugger/controller.cpp |
| @@ -252,6 +252,7 @@ file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) | |||
| 252 | if (ENABLE_QT_TRANSLATION) | 252 | if (ENABLE_QT_TRANSLATION) |
| 253 | set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") | 253 | set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") |
| 254 | option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) | 254 | option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) |
| 255 | option(WORKAROUND_BROKEN_LUPDATE "Run lupdate directly through CMake if Qt's convenience wrappers don't work" OFF) | ||
| 255 | 256 | ||
| 256 | # Update source TS file if enabled | 257 | # Update source TS file if enabled |
| 257 | if (GENERATE_QT_TRANSLATION) | 258 | if (GENERATE_QT_TRANSLATION) |
| @@ -259,19 +260,51 @@ if (ENABLE_QT_TRANSLATION) | |||
| 259 | # these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals | 260 | # these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals |
| 260 | # so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm | 261 | # so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm |
| 261 | set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations") | 262 | set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations") |
| 262 | qt_create_translation(QM_FILES | 263 | if (WORKAROUND_BROKEN_LUPDATE) |
| 263 | ${SRCS} | 264 | add_custom_command(OUTPUT ${YUZU_QT_LANGUAGES}/en.ts |
| 264 | ${UIS} | 265 | COMMAND lupdate |
| 265 | ${YUZU_QT_LANGUAGES}/en.ts | 266 | -source-language en_US |
| 266 | OPTIONS | 267 | -target-language en_US |
| 267 | -source-language en_US | 268 | ${SRCS} |
| 268 | -target-language en_US | 269 | ${UIS} |
| 269 | ) | 270 | -ts ${YUZU_QT_LANGUAGES}/en.ts |
| 271 | DEPENDS | ||
| 272 | ${SRCS} | ||
| 273 | ${UIS} | ||
| 274 | WORKING_DIRECTORY | ||
| 275 | ${CMAKE_CURRENT_SOURCE_DIR} | ||
| 276 | ) | ||
| 277 | else() | ||
| 278 | qt_create_translation(QM_FILES | ||
| 279 | ${SRCS} | ||
| 280 | ${UIS} | ||
| 281 | ${YUZU_QT_LANGUAGES}/en.ts | ||
| 282 | OPTIONS | ||
| 283 | -source-language en_US | ||
| 284 | -target-language en_US | ||
| 285 | ) | ||
| 286 | endif() | ||
| 270 | 287 | ||
| 271 | # Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts | 288 | # Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts |
| 272 | set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts) | 289 | set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts) |
| 273 | set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals") | 290 | set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals") |
| 274 | qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US) | 291 | if (WORKAROUND_BROKEN_LUPDATE) |
| 292 | add_custom_command(OUTPUT ${GENERATED_PLURALS_FILE} | ||
| 293 | COMMAND lupdate | ||
| 294 | -source-language en_US | ||
| 295 | -target-language en_US | ||
| 296 | ${SRCS} | ||
| 297 | ${UIS} | ||
| 298 | -ts ${GENERATED_PLURALS_FILE} | ||
| 299 | DEPENDS | ||
| 300 | ${SRCS} | ||
| 301 | ${UIS} | ||
| 302 | WORKING_DIRECTORY | ||
| 303 | ${CMAKE_CURRENT_SOURCE_DIR} | ||
| 304 | ) | ||
| 305 | else() | ||
| 306 | qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US) | ||
| 307 | endif() | ||
| 275 | 308 | ||
| 276 | add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE}) | 309 | add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE}) |
| 277 | endif() | 310 | endif() |
| @@ -344,7 +377,7 @@ endif() | |||
| 344 | 377 | ||
| 345 | create_target_directory_groups(yuzu) | 378 | create_target_directory_groups(yuzu) |
| 346 | 379 | ||
| 347 | target_link_libraries(yuzu PRIVATE common core input_common network video_core) | 380 | target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core) |
| 348 | target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets) | 381 | target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets) |
| 349 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | 382 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) |
| 350 | 383 | ||
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index 515cb7ce6..9e5319716 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp | |||
| @@ -13,7 +13,6 @@ | |||
| 13 | #include "core/hid/hid_core.h" | 13 | #include "core/hid/hid_core.h" |
| 14 | #include "core/hid/hid_types.h" | 14 | #include "core/hid/hid_types.h" |
| 15 | #include "core/hle/service/hid/controllers/npad.h" | 15 | #include "core/hle/service/hid/controllers/npad.h" |
| 16 | #include "core/hle/service/hid/hid.h" | ||
| 17 | #include "core/hle/service/sm/sm.h" | 16 | #include "core/hle/service/sm/sm.h" |
| 18 | #include "ui_qt_controller.h" | 17 | #include "ui_qt_controller.h" |
| 19 | #include "yuzu/applets/qt_controller.h" | 18 | #include "yuzu/applets/qt_controller.h" |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp deleted file mode 100644 index baa3e55f3..000000000 --- a/src/yuzu/configuration/config.cpp +++ /dev/null | |||
| @@ -1,1306 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <array> | ||
| 6 | #include <QKeySequence> | ||
| 7 | #include <QSettings> | ||
| 8 | #include "common/fs/fs.h" | ||
| 9 | #include "common/fs/path_util.h" | ||
| 10 | #include "common/settings.h" | ||
| 11 | #include "common/settings_common.h" | ||
| 12 | #include "common/settings_enums.h" | ||
| 13 | #include "core/core.h" | ||
| 14 | #include "core/hle/service/acc/profile_manager.h" | ||
| 15 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 16 | #include "input_common/main.h" | ||
| 17 | #include "network/network.h" | ||
| 18 | #include "yuzu/configuration/config.h" | ||
| 19 | |||
| 20 | namespace FS = Common::FS; | ||
| 21 | |||
| 22 | Config::Config(const std::string& config_name, ConfigType config_type) | ||
| 23 | : type(config_type), global{config_type == ConfigType::GlobalConfig} { | ||
| 24 | Initialize(config_name); | ||
| 25 | } | ||
| 26 | |||
| 27 | Config::~Config() { | ||
| 28 | if (global) { | ||
| 29 | Save(); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { | ||
| 34 | Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F, | ||
| 35 | Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T, | ||
| 36 | Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, | ||
| 37 | Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0, | ||
| 38 | }; | ||
| 39 | |||
| 40 | const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = { | ||
| 41 | Qt::Key_7, | ||
| 42 | Qt::Key_8, | ||
| 43 | }; | ||
| 44 | |||
| 45 | const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ | ||
| 46 | { | ||
| 47 | Qt::Key_W, | ||
| 48 | Qt::Key_S, | ||
| 49 | Qt::Key_A, | ||
| 50 | Qt::Key_D, | ||
| 51 | }, | ||
| 52 | { | ||
| 53 | Qt::Key_I, | ||
| 54 | Qt::Key_K, | ||
| 55 | Qt::Key_J, | ||
| 56 | Qt::Key_L, | ||
| 57 | }, | ||
| 58 | }}; | ||
| 59 | |||
| 60 | const std::array<int, 2> Config::default_stick_mod = { | ||
| 61 | Qt::Key_Shift, | ||
| 62 | 0, | ||
| 63 | }; | ||
| 64 | |||
| 65 | const std::array<int, 2> Config::default_ringcon_analogs{{ | ||
| 66 | Qt::Key_A, | ||
| 67 | Qt::Key_D, | ||
| 68 | }}; | ||
| 69 | |||
| 70 | const std::map<Settings::AntiAliasing, QString> Config::anti_aliasing_texts_map = { | ||
| 71 | {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))}, | ||
| 72 | {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))}, | ||
| 73 | {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))}, | ||
| 74 | }; | ||
| 75 | |||
| 76 | const std::map<Settings::ScalingFilter, QString> Config::scaling_filter_texts_map = { | ||
| 77 | {Settings::ScalingFilter::NearestNeighbor, | ||
| 78 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))}, | ||
| 79 | {Settings::ScalingFilter::Bilinear, | ||
| 80 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))}, | ||
| 81 | {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, | ||
| 82 | {Settings::ScalingFilter::Gaussian, | ||
| 83 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, | ||
| 84 | {Settings::ScalingFilter::ScaleForce, | ||
| 85 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, | ||
| 86 | {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, | ||
| 87 | }; | ||
| 88 | |||
| 89 | const std::map<Settings::ConsoleMode, QString> Config::use_docked_mode_texts_map = { | ||
| 90 | {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, | ||
| 91 | {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, | ||
| 92 | }; | ||
| 93 | |||
| 94 | const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = { | ||
| 95 | {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))}, | ||
| 96 | {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))}, | ||
| 97 | {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))}, | ||
| 98 | }; | ||
| 99 | |||
| 100 | const std::map<Settings::RendererBackend, QString> Config::renderer_backend_texts_map = { | ||
| 101 | {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))}, | ||
| 102 | {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))}, | ||
| 103 | {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, | ||
| 104 | }; | ||
| 105 | |||
| 106 | const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = { | ||
| 107 | {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, | ||
| 108 | {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, | ||
| 109 | {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, | ||
| 110 | }; | ||
| 111 | |||
| 112 | // This shouldn't have anything except static initializers (no functions). So | ||
| 113 | // QKeySequence(...).toString() is NOT ALLOWED HERE. | ||
| 114 | // This must be in alphabetical order according to action name as it must have the same order as | ||
| 115 | // UISetting::values.shortcuts, which is alphabetically ordered. | ||
| 116 | // clang-format off | ||
| 117 | const std::array<UISettings::Shortcut, 23> Config::default_hotkeys{{ | ||
| 118 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut, false}}, | ||
| 119 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut, true}}, | ||
| 120 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut, true}}, | ||
| 121 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut, false}}, | ||
| 122 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut, false}}, | ||
| 123 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut, false}}, | ||
| 124 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut, false}}, | ||
| 125 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut, false}}, | ||
| 126 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut, false}}, | ||
| 127 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut, false}}, | ||
| 128 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}}, | ||
| 129 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}}, | ||
| 130 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, | ||
| 131 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}}, | ||
| 132 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}}, | ||
| 133 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | ||
| 134 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | ||
| 135 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | ||
| 136 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut, false}}, | ||
| 137 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut, false}}, | ||
| 138 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | ||
| 139 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | ||
| 140 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut, false}}, | ||
| 141 | }}; | ||
| 142 | // clang-format on | ||
| 143 | |||
| 144 | void Config::Initialize(const std::string& config_name) { | ||
| 145 | const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); | ||
| 146 | const auto config_file = fmt::format("{}.ini", config_name); | ||
| 147 | |||
| 148 | switch (type) { | ||
| 149 | case ConfigType::GlobalConfig: | ||
| 150 | qt_config_loc = FS::PathToUTF8String(fs_config_loc / config_file); | ||
| 151 | void(FS::CreateParentDir(qt_config_loc)); | ||
| 152 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), | ||
| 153 | QSettings::IniFormat); | ||
| 154 | Reload(); | ||
| 155 | break; | ||
| 156 | case ConfigType::PerGameConfig: | ||
| 157 | qt_config_loc = | ||
| 158 | FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); | ||
| 159 | void(FS::CreateParentDir(qt_config_loc)); | ||
| 160 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), | ||
| 161 | QSettings::IniFormat); | ||
| 162 | Reload(); | ||
| 163 | break; | ||
| 164 | case ConfigType::InputProfile: | ||
| 165 | qt_config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); | ||
| 166 | void(FS::CreateParentDir(qt_config_loc)); | ||
| 167 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), | ||
| 168 | QSettings::IniFormat); | ||
| 169 | break; | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | bool Config::IsCustomConfig() { | ||
| 174 | return type == ConfigType::PerGameConfig; | ||
| 175 | } | ||
| 176 | |||
| 177 | void Config::ReadPlayerValue(std::size_t player_index) { | ||
| 178 | const QString player_prefix = [this, player_index] { | ||
| 179 | if (type == ConfigType::InputProfile) { | ||
| 180 | return QString{}; | ||
| 181 | } else { | ||
| 182 | return QStringLiteral("player_%1_").arg(player_index); | ||
| 183 | } | ||
| 184 | }(); | ||
| 185 | |||
| 186 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 187 | if (IsCustomConfig()) { | ||
| 188 | const auto profile_name = | ||
| 189 | qt_config->value(QStringLiteral("%1profile_name").arg(player_prefix), QString{}) | ||
| 190 | .toString() | ||
| 191 | .toStdString(); | ||
| 192 | if (profile_name.empty()) { | ||
| 193 | // Use the global input config | ||
| 194 | player = Settings::values.players.GetValue(true)[player_index]; | ||
| 195 | return; | ||
| 196 | } | ||
| 197 | player.profile_name = profile_name; | ||
| 198 | } | ||
| 199 | |||
| 200 | if (player_prefix.isEmpty() && Settings::IsConfiguringGlobal()) { | ||
| 201 | const auto controller = static_cast<Settings::ControllerType>( | ||
| 202 | qt_config | ||
| 203 | ->value(QStringLiteral("%1type").arg(player_prefix), | ||
| 204 | static_cast<u8>(Settings::ControllerType::ProController)) | ||
| 205 | .toUInt()); | ||
| 206 | |||
| 207 | if (controller == Settings::ControllerType::LeftJoycon || | ||
| 208 | controller == Settings::ControllerType::RightJoycon) { | ||
| 209 | player.controller_type = controller; | ||
| 210 | } | ||
| 211 | } else { | ||
| 212 | player.connected = | ||
| 213 | ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0) | ||
| 214 | .toBool(); | ||
| 215 | |||
| 216 | player.controller_type = static_cast<Settings::ControllerType>( | ||
| 217 | qt_config | ||
| 218 | ->value(QStringLiteral("%1type").arg(player_prefix), | ||
| 219 | static_cast<u8>(Settings::ControllerType::ProController)) | ||
| 220 | .toUInt()); | ||
| 221 | |||
| 222 | player.vibration_enabled = | ||
| 223 | qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true) | ||
| 224 | .toBool(); | ||
| 225 | |||
| 226 | player.vibration_strength = | ||
| 227 | qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100) | ||
| 228 | .toInt(); | ||
| 229 | |||
| 230 | player.body_color_left = qt_config | ||
| 231 | ->value(QStringLiteral("%1body_color_left").arg(player_prefix), | ||
| 232 | Settings::JOYCON_BODY_NEON_BLUE) | ||
| 233 | .toUInt(); | ||
| 234 | player.body_color_right = | ||
| 235 | qt_config | ||
| 236 | ->value(QStringLiteral("%1body_color_right").arg(player_prefix), | ||
| 237 | Settings::JOYCON_BODY_NEON_RED) | ||
| 238 | .toUInt(); | ||
| 239 | player.button_color_left = | ||
| 240 | qt_config | ||
| 241 | ->value(QStringLiteral("%1button_color_left").arg(player_prefix), | ||
| 242 | Settings::JOYCON_BUTTONS_NEON_BLUE) | ||
| 243 | .toUInt(); | ||
| 244 | player.button_color_right = | ||
| 245 | qt_config | ||
| 246 | ->value(QStringLiteral("%1button_color_right").arg(player_prefix), | ||
| 247 | Settings::JOYCON_BUTTONS_NEON_RED) | ||
| 248 | .toUInt(); | ||
| 249 | } | ||
| 250 | |||
| 251 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 252 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 253 | auto& player_buttons = player.buttons[i]; | ||
| 254 | |||
| 255 | player_buttons = qt_config | ||
| 256 | ->value(QStringLiteral("%1").arg(player_prefix) + | ||
| 257 | QString::fromUtf8(Settings::NativeButton::mapping[i]), | ||
| 258 | QString::fromStdString(default_param)) | ||
| 259 | .toString() | ||
| 260 | .toStdString(); | ||
| 261 | if (player_buttons.empty()) { | ||
| 262 | player_buttons = default_param; | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 267 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 268 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 269 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 270 | auto& player_analogs = player.analogs[i]; | ||
| 271 | |||
| 272 | player_analogs = qt_config | ||
| 273 | ->value(QStringLiteral("%1").arg(player_prefix) + | ||
| 274 | QString::fromUtf8(Settings::NativeAnalog::mapping[i]), | ||
| 275 | QString::fromStdString(default_param)) | ||
| 276 | .toString() | ||
| 277 | .toStdString(); | ||
| 278 | if (player_analogs.empty()) { | ||
| 279 | player_analogs = default_param; | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | ||
| 284 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); | ||
| 285 | auto& player_motions = player.motions[i]; | ||
| 286 | |||
| 287 | player_motions = qt_config | ||
| 288 | ->value(QStringLiteral("%1").arg(player_prefix) + | ||
| 289 | QString::fromUtf8(Settings::NativeMotion::mapping[i]), | ||
| 290 | QString::fromStdString(default_param)) | ||
| 291 | .toString() | ||
| 292 | .toStdString(); | ||
| 293 | if (player_motions.empty()) { | ||
| 294 | player_motions = default_param; | ||
| 295 | } | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | void Config::ReadDebugValues() { | ||
| 300 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 301 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 302 | auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; | ||
| 303 | |||
| 304 | debug_pad_buttons = qt_config | ||
| 305 | ->value(QStringLiteral("debug_pad_") + | ||
| 306 | QString::fromUtf8(Settings::NativeButton::mapping[i]), | ||
| 307 | QString::fromStdString(default_param)) | ||
| 308 | .toString() | ||
| 309 | .toStdString(); | ||
| 310 | if (debug_pad_buttons.empty()) { | ||
| 311 | debug_pad_buttons = default_param; | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 315 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 316 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 317 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 318 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 319 | auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; | ||
| 320 | |||
| 321 | debug_pad_analogs = qt_config | ||
| 322 | ->value(QStringLiteral("debug_pad_") + | ||
| 323 | QString::fromUtf8(Settings::NativeAnalog::mapping[i]), | ||
| 324 | QString::fromStdString(default_param)) | ||
| 325 | .toString() | ||
| 326 | .toStdString(); | ||
| 327 | if (debug_pad_analogs.empty()) { | ||
| 328 | debug_pad_analogs = default_param; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | void Config::ReadTouchscreenValues() { | ||
| 334 | Settings::values.touchscreen.enabled = | ||
| 335 | ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool(); | ||
| 336 | |||
| 337 | Settings::values.touchscreen.rotation_angle = | ||
| 338 | ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt(); | ||
| 339 | Settings::values.touchscreen.diameter_x = | ||
| 340 | ReadSetting(QStringLiteral("touchscreen_diameter_x"), 15).toUInt(); | ||
| 341 | Settings::values.touchscreen.diameter_y = | ||
| 342 | ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); | ||
| 343 | } | ||
| 344 | |||
| 345 | void Config::ReadHidbusValues() { | ||
| 346 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 347 | 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); | ||
| 348 | auto& ringcon_analogs = Settings::values.ringcon_analogs; | ||
| 349 | |||
| 350 | ringcon_analogs = | ||
| 351 | qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param)) | ||
| 352 | .toString() | ||
| 353 | .toStdString(); | ||
| 354 | if (ringcon_analogs.empty()) { | ||
| 355 | ringcon_analogs = default_param; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | void Config::ReadAudioValues() { | ||
| 360 | qt_config->beginGroup(QStringLiteral("Audio")); | ||
| 361 | |||
| 362 | ReadCategory(Settings::Category::Audio); | ||
| 363 | |||
| 364 | qt_config->endGroup(); | ||
| 365 | } | ||
| 366 | |||
| 367 | void Config::ReadControlValues() { | ||
| 368 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 369 | |||
| 370 | ReadCategory(Settings::Category::Controls); | ||
| 371 | |||
| 372 | Settings::values.players.SetGlobal(!IsCustomConfig()); | ||
| 373 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 374 | ReadPlayerValue(p); | ||
| 375 | } | ||
| 376 | |||
| 377 | // Disable docked mode if handheld is selected | ||
| 378 | const auto controller_type = Settings::values.players.GetValue()[0].controller_type; | ||
| 379 | if (controller_type == Settings::ControllerType::Handheld) { | ||
| 380 | Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); | ||
| 381 | Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld); | ||
| 382 | } | ||
| 383 | |||
| 384 | if (IsCustomConfig()) { | ||
| 385 | qt_config->endGroup(); | ||
| 386 | return; | ||
| 387 | } | ||
| 388 | ReadDebugValues(); | ||
| 389 | ReadTouchscreenValues(); | ||
| 390 | ReadMotionTouchValues(); | ||
| 391 | ReadHidbusValues(); | ||
| 392 | |||
| 393 | qt_config->endGroup(); | ||
| 394 | } | ||
| 395 | |||
| 396 | void Config::ReadMotionTouchValues() { | ||
| 397 | int num_touch_from_button_maps = | ||
| 398 | qt_config->beginReadArray(QStringLiteral("touch_from_button_maps")); | ||
| 399 | |||
| 400 | if (num_touch_from_button_maps > 0) { | ||
| 401 | const auto append_touch_from_button_map = [this] { | ||
| 402 | Settings::TouchFromButtonMap map; | ||
| 403 | map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default")) | ||
| 404 | .toString() | ||
| 405 | .toStdString(); | ||
| 406 | const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries")); | ||
| 407 | map.buttons.reserve(num_touch_maps); | ||
| 408 | for (int i = 0; i < num_touch_maps; i++) { | ||
| 409 | qt_config->setArrayIndex(i); | ||
| 410 | std::string touch_mapping = | ||
| 411 | ReadSetting(QStringLiteral("bind")).toString().toStdString(); | ||
| 412 | map.buttons.emplace_back(std::move(touch_mapping)); | ||
| 413 | } | ||
| 414 | qt_config->endArray(); // entries | ||
| 415 | Settings::values.touch_from_button_maps.emplace_back(std::move(map)); | ||
| 416 | }; | ||
| 417 | |||
| 418 | for (int i = 0; i < num_touch_from_button_maps; ++i) { | ||
| 419 | qt_config->setArrayIndex(i); | ||
| 420 | append_touch_from_button_map(); | ||
| 421 | } | ||
| 422 | } else { | ||
| 423 | Settings::values.touch_from_button_maps.emplace_back( | ||
| 424 | Settings::TouchFromButtonMap{"default", {}}); | ||
| 425 | num_touch_from_button_maps = 1; | ||
| 426 | } | ||
| 427 | qt_config->endArray(); | ||
| 428 | |||
| 429 | Settings::values.touch_from_button_map_index = std::clamp( | ||
| 430 | Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); | ||
| 431 | } | ||
| 432 | |||
| 433 | void Config::ReadCoreValues() { | ||
| 434 | qt_config->beginGroup(QStringLiteral("Core")); | ||
| 435 | |||
| 436 | ReadCategory(Settings::Category::Core); | ||
| 437 | |||
| 438 | qt_config->endGroup(); | ||
| 439 | } | ||
| 440 | |||
| 441 | void Config::ReadDataStorageValues() { | ||
| 442 | qt_config->beginGroup(QStringLiteral("Data Storage")); | ||
| 443 | |||
| 444 | FS::SetYuzuPath( | ||
| 445 | FS::YuzuPath::NANDDir, | ||
| 446 | qt_config | ||
| 447 | ->value(QStringLiteral("nand_directory"), | ||
| 448 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))) | ||
| 449 | .toString() | ||
| 450 | .toStdString()); | ||
| 451 | FS::SetYuzuPath( | ||
| 452 | FS::YuzuPath::SDMCDir, | ||
| 453 | qt_config | ||
| 454 | ->value(QStringLiteral("sdmc_directory"), | ||
| 455 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))) | ||
| 456 | .toString() | ||
| 457 | .toStdString()); | ||
| 458 | FS::SetYuzuPath( | ||
| 459 | FS::YuzuPath::LoadDir, | ||
| 460 | qt_config | ||
| 461 | ->value(QStringLiteral("load_directory"), | ||
| 462 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))) | ||
| 463 | .toString() | ||
| 464 | .toStdString()); | ||
| 465 | FS::SetYuzuPath( | ||
| 466 | FS::YuzuPath::DumpDir, | ||
| 467 | qt_config | ||
| 468 | ->value(QStringLiteral("dump_directory"), | ||
| 469 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))) | ||
| 470 | .toString() | ||
| 471 | .toStdString()); | ||
| 472 | FS::SetYuzuPath(FS::YuzuPath::TASDir, | ||
| 473 | qt_config | ||
| 474 | ->value(QStringLiteral("tas_directory"), | ||
| 475 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))) | ||
| 476 | .toString() | ||
| 477 | .toStdString()); | ||
| 478 | |||
| 479 | ReadCategory(Settings::Category::DataStorage); | ||
| 480 | |||
| 481 | qt_config->endGroup(); | ||
| 482 | } | ||
| 483 | |||
| 484 | void Config::ReadDebuggingValues() { | ||
| 485 | qt_config->beginGroup(QStringLiteral("Debugging")); | ||
| 486 | |||
| 487 | // Intentionally not using the QT default setting as this is intended to be changed in the ini | ||
| 488 | Settings::values.record_frame_times = | ||
| 489 | qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); | ||
| 490 | |||
| 491 | ReadCategory(Settings::Category::Debugging); | ||
| 492 | ReadCategory(Settings::Category::DebuggingGraphics); | ||
| 493 | |||
| 494 | qt_config->endGroup(); | ||
| 495 | } | ||
| 496 | |||
| 497 | void Config::ReadServiceValues() { | ||
| 498 | qt_config->beginGroup(QStringLiteral("Services")); | ||
| 499 | |||
| 500 | ReadCategory(Settings::Category::Services); | ||
| 501 | |||
| 502 | qt_config->endGroup(); | ||
| 503 | } | ||
| 504 | |||
| 505 | void Config::ReadDisabledAddOnValues() { | ||
| 506 | const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns")); | ||
| 507 | |||
| 508 | for (int i = 0; i < size; ++i) { | ||
| 509 | qt_config->setArrayIndex(i); | ||
| 510 | const auto title_id = ReadSetting(QStringLiteral("title_id"), 0).toULongLong(); | ||
| 511 | std::vector<std::string> out; | ||
| 512 | const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled")); | ||
| 513 | for (int j = 0; j < d_size; ++j) { | ||
| 514 | qt_config->setArrayIndex(j); | ||
| 515 | out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString()); | ||
| 516 | } | ||
| 517 | qt_config->endArray(); | ||
| 518 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | ||
| 519 | } | ||
| 520 | |||
| 521 | qt_config->endArray(); | ||
| 522 | } | ||
| 523 | |||
| 524 | void Config::ReadMiscellaneousValues() { | ||
| 525 | qt_config->beginGroup(QStringLiteral("Miscellaneous")); | ||
| 526 | |||
| 527 | ReadCategory(Settings::Category::Miscellaneous); | ||
| 528 | |||
| 529 | qt_config->endGroup(); | ||
| 530 | } | ||
| 531 | |||
| 532 | void Config::ReadPathValues() { | ||
| 533 | qt_config->beginGroup(QStringLiteral("Paths")); | ||
| 534 | |||
| 535 | UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); | ||
| 536 | UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); | ||
| 537 | UISettings::values.game_dir_deprecated = | ||
| 538 | ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); | ||
| 539 | UISettings::values.game_dir_deprecated_deepscan = | ||
| 540 | ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool(); | ||
| 541 | const int gamedirs_size = qt_config->beginReadArray(QStringLiteral("gamedirs")); | ||
| 542 | for (int i = 0; i < gamedirs_size; ++i) { | ||
| 543 | qt_config->setArrayIndex(i); | ||
| 544 | UISettings::GameDir game_dir; | ||
| 545 | game_dir.path = ReadSetting(QStringLiteral("path")).toString(); | ||
| 546 | game_dir.deep_scan = ReadSetting(QStringLiteral("deep_scan"), false).toBool(); | ||
| 547 | game_dir.expanded = ReadSetting(QStringLiteral("expanded"), true).toBool(); | ||
| 548 | UISettings::values.game_dirs.append(game_dir); | ||
| 549 | } | ||
| 550 | qt_config->endArray(); | ||
| 551 | // create NAND and SD card directories if empty, these are not removable through the UI, | ||
| 552 | // also carries over old game list settings if present | ||
| 553 | if (UISettings::values.game_dirs.isEmpty()) { | ||
| 554 | UISettings::GameDir game_dir; | ||
| 555 | game_dir.path = QStringLiteral("SDMC"); | ||
| 556 | game_dir.expanded = true; | ||
| 557 | UISettings::values.game_dirs.append(game_dir); | ||
| 558 | game_dir.path = QStringLiteral("UserNAND"); | ||
| 559 | UISettings::values.game_dirs.append(game_dir); | ||
| 560 | game_dir.path = QStringLiteral("SysNAND"); | ||
| 561 | UISettings::values.game_dirs.append(game_dir); | ||
| 562 | if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) { | ||
| 563 | game_dir.path = UISettings::values.game_dir_deprecated; | ||
| 564 | game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan; | ||
| 565 | UISettings::values.game_dirs.append(game_dir); | ||
| 566 | } | ||
| 567 | } | ||
| 568 | UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); | ||
| 569 | UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString(); | ||
| 570 | |||
| 571 | qt_config->endGroup(); | ||
| 572 | } | ||
| 573 | |||
| 574 | void Config::ReadCpuValues() { | ||
| 575 | qt_config->beginGroup(QStringLiteral("Cpu")); | ||
| 576 | |||
| 577 | ReadCategory(Settings::Category::Cpu); | ||
| 578 | ReadCategory(Settings::Category::CpuDebug); | ||
| 579 | ReadCategory(Settings::Category::CpuUnsafe); | ||
| 580 | |||
| 581 | qt_config->endGroup(); | ||
| 582 | } | ||
| 583 | |||
| 584 | void Config::ReadRendererValues() { | ||
| 585 | qt_config->beginGroup(QStringLiteral("Renderer")); | ||
| 586 | |||
| 587 | ReadCategory(Settings::Category::Renderer); | ||
| 588 | ReadCategory(Settings::Category::RendererAdvanced); | ||
| 589 | ReadCategory(Settings::Category::RendererDebug); | ||
| 590 | |||
| 591 | qt_config->endGroup(); | ||
| 592 | } | ||
| 593 | |||
| 594 | void Config::ReadScreenshotValues() { | ||
| 595 | qt_config->beginGroup(QStringLiteral("Screenshots")); | ||
| 596 | |||
| 597 | ReadCategory(Settings::Category::Screenshots); | ||
| 598 | FS::SetYuzuPath( | ||
| 599 | FS::YuzuPath::ScreenshotsDir, | ||
| 600 | qt_config | ||
| 601 | ->value(QStringLiteral("screenshot_path"), | ||
| 602 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))) | ||
| 603 | .toString() | ||
| 604 | .toStdString()); | ||
| 605 | |||
| 606 | qt_config->endGroup(); | ||
| 607 | } | ||
| 608 | |||
| 609 | void Config::ReadShortcutValues() { | ||
| 610 | qt_config->beginGroup(QStringLiteral("Shortcuts")); | ||
| 611 | |||
| 612 | for (const auto& [name, group, shortcut] : default_hotkeys) { | ||
| 613 | qt_config->beginGroup(group); | ||
| 614 | qt_config->beginGroup(name); | ||
| 615 | // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1 | ||
| 616 | // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open | ||
| 617 | // a file dialog in windowed mode | ||
| 618 | UISettings::values.shortcuts.push_back( | ||
| 619 | {name, | ||
| 620 | group, | ||
| 621 | {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(), | ||
| 622 | ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq) | ||
| 623 | .toString(), | ||
| 624 | shortcut.context, ReadSetting(QStringLiteral("Repeat"), shortcut.repeat).toBool()}}); | ||
| 625 | qt_config->endGroup(); | ||
| 626 | qt_config->endGroup(); | ||
| 627 | } | ||
| 628 | |||
| 629 | qt_config->endGroup(); | ||
| 630 | } | ||
| 631 | |||
| 632 | void Config::ReadSystemValues() { | ||
| 633 | qt_config->beginGroup(QStringLiteral("System")); | ||
| 634 | |||
| 635 | ReadCategory(Settings::Category::System); | ||
| 636 | ReadCategory(Settings::Category::SystemAudio); | ||
| 637 | |||
| 638 | qt_config->endGroup(); | ||
| 639 | } | ||
| 640 | |||
| 641 | void Config::ReadUIValues() { | ||
| 642 | qt_config->beginGroup(QStringLiteral("UI")); | ||
| 643 | |||
| 644 | UISettings::values.theme = | ||
| 645 | ReadSetting( | ||
| 646 | QStringLiteral("theme"), | ||
| 647 | QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second)) | ||
| 648 | .toString(); | ||
| 649 | |||
| 650 | ReadUIGamelistValues(); | ||
| 651 | ReadUILayoutValues(); | ||
| 652 | ReadPathValues(); | ||
| 653 | ReadScreenshotValues(); | ||
| 654 | ReadShortcutValues(); | ||
| 655 | ReadMultiplayerValues(); | ||
| 656 | |||
| 657 | ReadCategory(Settings::Category::Ui); | ||
| 658 | ReadCategory(Settings::Category::UiGeneral); | ||
| 659 | |||
| 660 | qt_config->endGroup(); | ||
| 661 | } | ||
| 662 | |||
| 663 | void Config::ReadUIGamelistValues() { | ||
| 664 | qt_config->beginGroup(QStringLiteral("UIGameList")); | ||
| 665 | |||
| 666 | ReadCategory(Settings::Category::UiGameList); | ||
| 667 | |||
| 668 | const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); | ||
| 669 | for (int i = 0; i < favorites_size; i++) { | ||
| 670 | qt_config->setArrayIndex(i); | ||
| 671 | UISettings::values.favorited_ids.append( | ||
| 672 | ReadSetting(QStringLiteral("program_id")).toULongLong()); | ||
| 673 | } | ||
| 674 | qt_config->endArray(); | ||
| 675 | |||
| 676 | qt_config->endGroup(); | ||
| 677 | } | ||
| 678 | |||
| 679 | void Config::ReadUILayoutValues() { | ||
| 680 | qt_config->beginGroup(QStringLiteral("UILayout")); | ||
| 681 | |||
| 682 | UISettings::values.geometry = ReadSetting(QStringLiteral("geometry")).toByteArray(); | ||
| 683 | UISettings::values.state = ReadSetting(QStringLiteral("state")).toByteArray(); | ||
| 684 | UISettings::values.renderwindow_geometry = | ||
| 685 | ReadSetting(QStringLiteral("geometryRenderWindow")).toByteArray(); | ||
| 686 | UISettings::values.gamelist_header_state = | ||
| 687 | ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray(); | ||
| 688 | UISettings::values.microprofile_geometry = | ||
| 689 | ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray(); | ||
| 690 | |||
| 691 | ReadCategory(Settings::Category::UiLayout); | ||
| 692 | |||
| 693 | qt_config->endGroup(); | ||
| 694 | } | ||
| 695 | |||
| 696 | void Config::ReadWebServiceValues() { | ||
| 697 | qt_config->beginGroup(QStringLiteral("WebService")); | ||
| 698 | |||
| 699 | ReadCategory(Settings::Category::WebService); | ||
| 700 | |||
| 701 | qt_config->endGroup(); | ||
| 702 | } | ||
| 703 | |||
| 704 | void Config::ReadMultiplayerValues() { | ||
| 705 | qt_config->beginGroup(QStringLiteral("Multiplayer")); | ||
| 706 | |||
| 707 | ReadCategory(Settings::Category::Multiplayer); | ||
| 708 | |||
| 709 | // Read ban list back | ||
| 710 | int size = qt_config->beginReadArray(QStringLiteral("username_ban_list")); | ||
| 711 | UISettings::values.multiplayer_ban_list.first.resize(size); | ||
| 712 | for (int i = 0; i < size; ++i) { | ||
| 713 | qt_config->setArrayIndex(i); | ||
| 714 | UISettings::values.multiplayer_ban_list.first[i] = | ||
| 715 | ReadSetting(QStringLiteral("username")).toString().toStdString(); | ||
| 716 | } | ||
| 717 | qt_config->endArray(); | ||
| 718 | size = qt_config->beginReadArray(QStringLiteral("ip_ban_list")); | ||
| 719 | UISettings::values.multiplayer_ban_list.second.resize(size); | ||
| 720 | for (int i = 0; i < size; ++i) { | ||
| 721 | qt_config->setArrayIndex(i); | ||
| 722 | UISettings::values.multiplayer_ban_list.second[i] = | ||
| 723 | ReadSetting(QStringLiteral("ip")).toString().toStdString(); | ||
| 724 | } | ||
| 725 | qt_config->endArray(); | ||
| 726 | |||
| 727 | qt_config->endGroup(); | ||
| 728 | } | ||
| 729 | |||
| 730 | void Config::ReadNetworkValues() { | ||
| 731 | qt_config->beginGroup(QString::fromStdString("Services")); | ||
| 732 | |||
| 733 | ReadCategory(Settings::Category::Network); | ||
| 734 | |||
| 735 | qt_config->endGroup(); | ||
| 736 | } | ||
| 737 | |||
| 738 | void Config::ReadValues() { | ||
| 739 | if (global) { | ||
| 740 | ReadDataStorageValues(); | ||
| 741 | ReadDebuggingValues(); | ||
| 742 | ReadDisabledAddOnValues(); | ||
| 743 | ReadNetworkValues(); | ||
| 744 | ReadServiceValues(); | ||
| 745 | ReadUIValues(); | ||
| 746 | ReadWebServiceValues(); | ||
| 747 | ReadMiscellaneousValues(); | ||
| 748 | } | ||
| 749 | ReadControlValues(); | ||
| 750 | ReadCoreValues(); | ||
| 751 | ReadCpuValues(); | ||
| 752 | ReadRendererValues(); | ||
| 753 | ReadAudioValues(); | ||
| 754 | ReadSystemValues(); | ||
| 755 | } | ||
| 756 | |||
| 757 | void Config::SavePlayerValue(std::size_t player_index) { | ||
| 758 | const QString player_prefix = [this, player_index] { | ||
| 759 | if (type == ConfigType::InputProfile) { | ||
| 760 | return QString{}; | ||
| 761 | } else { | ||
| 762 | return QStringLiteral("player_%1_").arg(player_index); | ||
| 763 | } | ||
| 764 | }(); | ||
| 765 | |||
| 766 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 767 | if (IsCustomConfig()) { | ||
| 768 | if (player.profile_name.empty()) { | ||
| 769 | // No custom profile selected | ||
| 770 | return; | ||
| 771 | } | ||
| 772 | WriteSetting(QStringLiteral("%1profile_name").arg(player_prefix), | ||
| 773 | QString::fromStdString(player.profile_name), QString{}); | ||
| 774 | } | ||
| 775 | |||
| 776 | WriteSetting(QStringLiteral("%1type").arg(player_prefix), | ||
| 777 | static_cast<u8>(player.controller_type), | ||
| 778 | static_cast<u8>(Settings::ControllerType::ProController)); | ||
| 779 | |||
| 780 | if (!player_prefix.isEmpty() || !Settings::IsConfiguringGlobal()) { | ||
| 781 | WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, | ||
| 782 | player_index == 0); | ||
| 783 | WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix), | ||
| 784 | player.vibration_enabled, true); | ||
| 785 | WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix), | ||
| 786 | player.vibration_strength, 100); | ||
| 787 | WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left, | ||
| 788 | Settings::JOYCON_BODY_NEON_BLUE); | ||
| 789 | WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix), | ||
| 790 | player.body_color_right, Settings::JOYCON_BODY_NEON_RED); | ||
| 791 | WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix), | ||
| 792 | player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE); | ||
| 793 | WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix), | ||
| 794 | player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED); | ||
| 795 | } | ||
| 796 | |||
| 797 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 798 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 799 | WriteSetting(QStringLiteral("%1").arg(player_prefix) + | ||
| 800 | QString::fromStdString(Settings::NativeButton::mapping[i]), | ||
| 801 | QString::fromStdString(player.buttons[i]), | ||
| 802 | QString::fromStdString(default_param)); | ||
| 803 | } | ||
| 804 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 805 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 806 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 807 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 808 | WriteSetting(QStringLiteral("%1").arg(player_prefix) + | ||
| 809 | QString::fromStdString(Settings::NativeAnalog::mapping[i]), | ||
| 810 | QString::fromStdString(player.analogs[i]), | ||
| 811 | QString::fromStdString(default_param)); | ||
| 812 | } | ||
| 813 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | ||
| 814 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); | ||
| 815 | WriteSetting(QStringLiteral("%1").arg(player_prefix) + | ||
| 816 | QString::fromStdString(Settings::NativeMotion::mapping[i]), | ||
| 817 | QString::fromStdString(player.motions[i]), | ||
| 818 | QString::fromStdString(default_param)); | ||
| 819 | } | ||
| 820 | } | ||
| 821 | |||
| 822 | void Config::SaveDebugValues() { | ||
| 823 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 824 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 825 | WriteSetting(QStringLiteral("debug_pad_") + | ||
| 826 | QString::fromStdString(Settings::NativeButton::mapping[i]), | ||
| 827 | QString::fromStdString(Settings::values.debug_pad_buttons[i]), | ||
| 828 | QString::fromStdString(default_param)); | ||
| 829 | } | ||
| 830 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 831 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 832 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 833 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 834 | WriteSetting(QStringLiteral("debug_pad_") + | ||
| 835 | QString::fromStdString(Settings::NativeAnalog::mapping[i]), | ||
| 836 | QString::fromStdString(Settings::values.debug_pad_analogs[i]), | ||
| 837 | QString::fromStdString(default_param)); | ||
| 838 | } | ||
| 839 | } | ||
| 840 | |||
| 841 | void Config::SaveTouchscreenValues() { | ||
| 842 | const auto& touchscreen = Settings::values.touchscreen; | ||
| 843 | |||
| 844 | WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true); | ||
| 845 | |||
| 846 | WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0); | ||
| 847 | WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15); | ||
| 848 | WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); | ||
| 849 | } | ||
| 850 | |||
| 851 | void Config::SaveMotionTouchValues() { | ||
| 852 | qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); | ||
| 853 | for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { | ||
| 854 | qt_config->setArrayIndex(static_cast<int>(p)); | ||
| 855 | WriteSetting(QStringLiteral("name"), | ||
| 856 | QString::fromStdString(Settings::values.touch_from_button_maps[p].name), | ||
| 857 | QStringLiteral("default")); | ||
| 858 | qt_config->beginWriteArray(QStringLiteral("entries")); | ||
| 859 | for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); | ||
| 860 | ++q) { | ||
| 861 | qt_config->setArrayIndex(static_cast<int>(q)); | ||
| 862 | WriteSetting( | ||
| 863 | QStringLiteral("bind"), | ||
| 864 | QString::fromStdString(Settings::values.touch_from_button_maps[p].buttons[q])); | ||
| 865 | } | ||
| 866 | qt_config->endArray(); | ||
| 867 | } | ||
| 868 | qt_config->endArray(); | ||
| 869 | } | ||
| 870 | |||
| 871 | void Config::SaveHidbusValues() { | ||
| 872 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 873 | 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); | ||
| 874 | WriteSetting(QStringLiteral("ring_controller"), | ||
| 875 | QString::fromStdString(Settings::values.ringcon_analogs), | ||
| 876 | QString::fromStdString(default_param)); | ||
| 877 | } | ||
| 878 | |||
| 879 | void Config::SaveValues() { | ||
| 880 | if (global) { | ||
| 881 | SaveDataStorageValues(); | ||
| 882 | SaveDebuggingValues(); | ||
| 883 | SaveDisabledAddOnValues(); | ||
| 884 | SaveNetworkValues(); | ||
| 885 | SaveUIValues(); | ||
| 886 | SaveWebServiceValues(); | ||
| 887 | SaveMiscellaneousValues(); | ||
| 888 | } | ||
| 889 | SaveControlValues(); | ||
| 890 | SaveCoreValues(); | ||
| 891 | SaveCpuValues(); | ||
| 892 | SaveRendererValues(); | ||
| 893 | SaveAudioValues(); | ||
| 894 | SaveSystemValues(); | ||
| 895 | |||
| 896 | qt_config->sync(); | ||
| 897 | } | ||
| 898 | |||
| 899 | void Config::SaveAudioValues() { | ||
| 900 | qt_config->beginGroup(QStringLiteral("Audio")); | ||
| 901 | |||
| 902 | WriteCategory(Settings::Category::Audio); | ||
| 903 | |||
| 904 | qt_config->endGroup(); | ||
| 905 | } | ||
| 906 | |||
| 907 | void Config::SaveControlValues() { | ||
| 908 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 909 | |||
| 910 | WriteCategory(Settings::Category::Controls); | ||
| 911 | |||
| 912 | Settings::values.players.SetGlobal(!IsCustomConfig()); | ||
| 913 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 914 | SavePlayerValue(p); | ||
| 915 | } | ||
| 916 | if (IsCustomConfig()) { | ||
| 917 | qt_config->endGroup(); | ||
| 918 | return; | ||
| 919 | } | ||
| 920 | SaveDebugValues(); | ||
| 921 | SaveTouchscreenValues(); | ||
| 922 | SaveMotionTouchValues(); | ||
| 923 | SaveHidbusValues(); | ||
| 924 | |||
| 925 | qt_config->endGroup(); | ||
| 926 | } | ||
| 927 | |||
| 928 | void Config::SaveCoreValues() { | ||
| 929 | qt_config->beginGroup(QStringLiteral("Core")); | ||
| 930 | |||
| 931 | WriteCategory(Settings::Category::Core); | ||
| 932 | |||
| 933 | qt_config->endGroup(); | ||
| 934 | } | ||
| 935 | |||
| 936 | void Config::SaveDataStorageValues() { | ||
| 937 | qt_config->beginGroup(QStringLiteral("Data Storage")); | ||
| 938 | |||
| 939 | WriteSetting(QStringLiteral("nand_directory"), | ||
| 940 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)), | ||
| 941 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); | ||
| 942 | WriteSetting(QStringLiteral("sdmc_directory"), | ||
| 943 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)), | ||
| 944 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); | ||
| 945 | WriteSetting(QStringLiteral("load_directory"), | ||
| 946 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)), | ||
| 947 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); | ||
| 948 | WriteSetting(QStringLiteral("dump_directory"), | ||
| 949 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)), | ||
| 950 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); | ||
| 951 | WriteSetting(QStringLiteral("tas_directory"), | ||
| 952 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)), | ||
| 953 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); | ||
| 954 | |||
| 955 | WriteCategory(Settings::Category::DataStorage); | ||
| 956 | |||
| 957 | qt_config->endGroup(); | ||
| 958 | } | ||
| 959 | |||
| 960 | void Config::SaveDebuggingValues() { | ||
| 961 | qt_config->beginGroup(QStringLiteral("Debugging")); | ||
| 962 | |||
| 963 | // Intentionally not using the QT default setting as this is intended to be changed in the ini | ||
| 964 | qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); | ||
| 965 | |||
| 966 | WriteCategory(Settings::Category::Debugging); | ||
| 967 | WriteCategory(Settings::Category::DebuggingGraphics); | ||
| 968 | |||
| 969 | qt_config->endGroup(); | ||
| 970 | } | ||
| 971 | |||
| 972 | void Config::SaveNetworkValues() { | ||
| 973 | qt_config->beginGroup(QStringLiteral("Services")); | ||
| 974 | |||
| 975 | WriteCategory(Settings::Category::Network); | ||
| 976 | |||
| 977 | qt_config->endGroup(); | ||
| 978 | } | ||
| 979 | |||
| 980 | void Config::SaveDisabledAddOnValues() { | ||
| 981 | qt_config->beginWriteArray(QStringLiteral("DisabledAddOns")); | ||
| 982 | |||
| 983 | int i = 0; | ||
| 984 | for (const auto& elem : Settings::values.disabled_addons) { | ||
| 985 | qt_config->setArrayIndex(i); | ||
| 986 | WriteSetting(QStringLiteral("title_id"), QVariant::fromValue<u64>(elem.first), 0); | ||
| 987 | qt_config->beginWriteArray(QStringLiteral("disabled")); | ||
| 988 | for (std::size_t j = 0; j < elem.second.size(); ++j) { | ||
| 989 | qt_config->setArrayIndex(static_cast<int>(j)); | ||
| 990 | WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{}); | ||
| 991 | } | ||
| 992 | qt_config->endArray(); | ||
| 993 | ++i; | ||
| 994 | } | ||
| 995 | |||
| 996 | qt_config->endArray(); | ||
| 997 | } | ||
| 998 | |||
| 999 | void Config::SaveMiscellaneousValues() { | ||
| 1000 | qt_config->beginGroup(QStringLiteral("Miscellaneous")); | ||
| 1001 | |||
| 1002 | WriteCategory(Settings::Category::Miscellaneous); | ||
| 1003 | |||
| 1004 | qt_config->endGroup(); | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | void Config::SavePathValues() { | ||
| 1008 | qt_config->beginGroup(QStringLiteral("Paths")); | ||
| 1009 | |||
| 1010 | WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); | ||
| 1011 | WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); | ||
| 1012 | qt_config->beginWriteArray(QStringLiteral("gamedirs")); | ||
| 1013 | for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { | ||
| 1014 | qt_config->setArrayIndex(i); | ||
| 1015 | const auto& game_dir = UISettings::values.game_dirs[i]; | ||
| 1016 | WriteSetting(QStringLiteral("path"), game_dir.path); | ||
| 1017 | WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false); | ||
| 1018 | WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true); | ||
| 1019 | } | ||
| 1020 | qt_config->endArray(); | ||
| 1021 | WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); | ||
| 1022 | WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{}); | ||
| 1023 | |||
| 1024 | qt_config->endGroup(); | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | void Config::SaveCpuValues() { | ||
| 1028 | qt_config->beginGroup(QStringLiteral("Cpu")); | ||
| 1029 | |||
| 1030 | WriteCategory(Settings::Category::Cpu); | ||
| 1031 | WriteCategory(Settings::Category::CpuDebug); | ||
| 1032 | WriteCategory(Settings::Category::CpuUnsafe); | ||
| 1033 | |||
| 1034 | qt_config->endGroup(); | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | void Config::SaveRendererValues() { | ||
| 1038 | qt_config->beginGroup(QStringLiteral("Renderer")); | ||
| 1039 | |||
| 1040 | WriteCategory(Settings::Category::Renderer); | ||
| 1041 | WriteCategory(Settings::Category::RendererAdvanced); | ||
| 1042 | WriteCategory(Settings::Category::RendererDebug); | ||
| 1043 | |||
| 1044 | qt_config->endGroup(); | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | void Config::SaveScreenshotValues() { | ||
| 1048 | qt_config->beginGroup(QStringLiteral("Screenshots")); | ||
| 1049 | |||
| 1050 | WriteSetting(QStringLiteral("screenshot_path"), | ||
| 1051 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))); | ||
| 1052 | WriteCategory(Settings::Category::Screenshots); | ||
| 1053 | |||
| 1054 | qt_config->endGroup(); | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | void Config::SaveShortcutValues() { | ||
| 1058 | qt_config->beginGroup(QStringLiteral("Shortcuts")); | ||
| 1059 | |||
| 1060 | // Lengths of UISettings::values.shortcuts & default_hotkeys are same. | ||
| 1061 | // However, their ordering must also be the same. | ||
| 1062 | for (std::size_t i = 0; i < default_hotkeys.size(); i++) { | ||
| 1063 | const auto& [name, group, shortcut] = UISettings::values.shortcuts[i]; | ||
| 1064 | const auto& default_hotkey = default_hotkeys[i].shortcut; | ||
| 1065 | |||
| 1066 | qt_config->beginGroup(group); | ||
| 1067 | qt_config->beginGroup(name); | ||
| 1068 | WriteSetting(QStringLiteral("KeySeq"), shortcut.keyseq, default_hotkey.keyseq); | ||
| 1069 | WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq, | ||
| 1070 | default_hotkey.controller_keyseq); | ||
| 1071 | WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context); | ||
| 1072 | WriteSetting(QStringLiteral("Repeat"), shortcut.repeat, default_hotkey.repeat); | ||
| 1073 | qt_config->endGroup(); | ||
| 1074 | qt_config->endGroup(); | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | qt_config->endGroup(); | ||
| 1078 | } | ||
| 1079 | |||
| 1080 | void Config::SaveSystemValues() { | ||
| 1081 | qt_config->beginGroup(QStringLiteral("System")); | ||
| 1082 | |||
| 1083 | WriteCategory(Settings::Category::System); | ||
| 1084 | WriteCategory(Settings::Category::SystemAudio); | ||
| 1085 | |||
| 1086 | qt_config->endGroup(); | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | void Config::SaveUIValues() { | ||
| 1090 | qt_config->beginGroup(QStringLiteral("UI")); | ||
| 1091 | |||
| 1092 | WriteCategory(Settings::Category::Ui); | ||
| 1093 | WriteCategory(Settings::Category::UiGeneral); | ||
| 1094 | |||
| 1095 | WriteSetting(QStringLiteral("theme"), UISettings::values.theme, | ||
| 1096 | QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second)); | ||
| 1097 | |||
| 1098 | SaveUIGamelistValues(); | ||
| 1099 | SaveUILayoutValues(); | ||
| 1100 | SavePathValues(); | ||
| 1101 | SaveScreenshotValues(); | ||
| 1102 | SaveShortcutValues(); | ||
| 1103 | SaveMultiplayerValues(); | ||
| 1104 | |||
| 1105 | qt_config->endGroup(); | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | void Config::SaveUIGamelistValues() { | ||
| 1109 | qt_config->beginGroup(QStringLiteral("UIGameList")); | ||
| 1110 | |||
| 1111 | WriteCategory(Settings::Category::UiGameList); | ||
| 1112 | |||
| 1113 | qt_config->beginWriteArray(QStringLiteral("favorites")); | ||
| 1114 | for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { | ||
| 1115 | qt_config->setArrayIndex(i); | ||
| 1116 | WriteSetting(QStringLiteral("program_id"), | ||
| 1117 | QVariant::fromValue(UISettings::values.favorited_ids[i])); | ||
| 1118 | } | ||
| 1119 | qt_config->endArray(); | ||
| 1120 | |||
| 1121 | qt_config->endGroup(); | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | void Config::SaveUILayoutValues() { | ||
| 1125 | qt_config->beginGroup(QStringLiteral("UILayout")); | ||
| 1126 | |||
| 1127 | WriteSetting(QStringLiteral("geometry"), UISettings::values.geometry); | ||
| 1128 | WriteSetting(QStringLiteral("state"), UISettings::values.state); | ||
| 1129 | WriteSetting(QStringLiteral("geometryRenderWindow"), UISettings::values.renderwindow_geometry); | ||
| 1130 | WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state); | ||
| 1131 | WriteSetting(QStringLiteral("microProfileDialogGeometry"), | ||
| 1132 | UISettings::values.microprofile_geometry); | ||
| 1133 | |||
| 1134 | WriteCategory(Settings::Category::UiLayout); | ||
| 1135 | |||
| 1136 | qt_config->endGroup(); | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | void Config::SaveWebServiceValues() { | ||
| 1140 | qt_config->beginGroup(QStringLiteral("WebService")); | ||
| 1141 | |||
| 1142 | WriteCategory(Settings::Category::WebService); | ||
| 1143 | |||
| 1144 | qt_config->endGroup(); | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | void Config::SaveMultiplayerValues() { | ||
| 1148 | qt_config->beginGroup(QStringLiteral("Multiplayer")); | ||
| 1149 | |||
| 1150 | WriteCategory(Settings::Category::Multiplayer); | ||
| 1151 | |||
| 1152 | // Write ban list | ||
| 1153 | qt_config->beginWriteArray(QStringLiteral("username_ban_list")); | ||
| 1154 | for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) { | ||
| 1155 | qt_config->setArrayIndex(static_cast<int>(i)); | ||
| 1156 | WriteSetting(QStringLiteral("username"), | ||
| 1157 | QString::fromStdString(UISettings::values.multiplayer_ban_list.first[i])); | ||
| 1158 | } | ||
| 1159 | qt_config->endArray(); | ||
| 1160 | qt_config->beginWriteArray(QStringLiteral("ip_ban_list")); | ||
| 1161 | for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) { | ||
| 1162 | qt_config->setArrayIndex(static_cast<int>(i)); | ||
| 1163 | WriteSetting(QStringLiteral("ip"), | ||
| 1164 | QString::fromStdString(UISettings::values.multiplayer_ban_list.second[i])); | ||
| 1165 | } | ||
| 1166 | qt_config->endArray(); | ||
| 1167 | |||
| 1168 | qt_config->endGroup(); | ||
| 1169 | } | ||
| 1170 | |||
| 1171 | QVariant Config::ReadSetting(const QString& name) const { | ||
| 1172 | return qt_config->value(name); | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const { | ||
| 1176 | QVariant result; | ||
| 1177 | if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { | ||
| 1178 | result = default_value; | ||
| 1179 | } else { | ||
| 1180 | result = qt_config->value(name, default_value); | ||
| 1181 | } | ||
| 1182 | return result; | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | void Config::WriteSetting(const QString& name, const QVariant& value) { | ||
| 1186 | qt_config->setValue(name, value); | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | void Config::WriteSetting(const QString& name, const QVariant& value, | ||
| 1190 | const QVariant& default_value) { | ||
| 1191 | qt_config->setValue(name + QStringLiteral("/default"), value == default_value); | ||
| 1192 | qt_config->setValue(name, value); | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, | ||
| 1196 | bool use_global) { | ||
| 1197 | if (!global) { | ||
| 1198 | qt_config->setValue(name + QStringLiteral("/use_global"), use_global); | ||
| 1199 | } | ||
| 1200 | if (global || !use_global) { | ||
| 1201 | qt_config->setValue(name + QStringLiteral("/default"), value == default_value); | ||
| 1202 | qt_config->setValue(name, value); | ||
| 1203 | } | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | void Config::Reload() { | ||
| 1207 | ReadValues(); | ||
| 1208 | // To apply default value changes | ||
| 1209 | SaveValues(); | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | void Config::Save() { | ||
| 1213 | SaveValues(); | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | void Config::ReadControlPlayerValue(std::size_t player_index) { | ||
| 1217 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 1218 | ReadPlayerValue(player_index); | ||
| 1219 | qt_config->endGroup(); | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | void Config::SaveControlPlayerValue(std::size_t player_index) { | ||
| 1223 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 1224 | SavePlayerValue(player_index); | ||
| 1225 | qt_config->endGroup(); | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | void Config::ClearControlPlayerValues() { | ||
| 1229 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 1230 | // If key is an empty string, all keys in the current group() are removed. | ||
| 1231 | qt_config->remove(QString{}); | ||
| 1232 | qt_config->endGroup(); | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | const std::string& Config::GetConfigFilePath() const { | ||
| 1236 | return qt_config_loc; | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | static auto FindRelevantList(Settings::Category category) { | ||
| 1240 | auto& map = Settings::values.linkage.by_category; | ||
| 1241 | if (map.contains(category)) { | ||
| 1242 | return Settings::values.linkage.by_category[category]; | ||
| 1243 | } | ||
| 1244 | return UISettings::values.linkage.by_category[category]; | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | void Config::ReadCategory(Settings::Category category) { | ||
| 1248 | const auto& settings = FindRelevantList(category); | ||
| 1249 | std::for_each(settings.begin(), settings.end(), | ||
| 1250 | [&](const auto& setting) { ReadSettingGeneric(setting); }); | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | void Config::WriteCategory(Settings::Category category) { | ||
| 1254 | const auto& settings = FindRelevantList(category); | ||
| 1255 | std::for_each(settings.begin(), settings.end(), | ||
| 1256 | [&](const auto& setting) { WriteSettingGeneric(setting); }); | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) { | ||
| 1260 | if (!setting->Save() || (!setting->Switchable() && !global)) { | ||
| 1261 | return; | ||
| 1262 | } | ||
| 1263 | const QString name = QString::fromStdString(setting->GetLabel()); | ||
| 1264 | const auto default_value = | ||
| 1265 | QVariant::fromValue<QString>(QString::fromStdString(setting->DefaultToString())); | ||
| 1266 | |||
| 1267 | bool use_global = true; | ||
| 1268 | if (setting->Switchable() && !global) { | ||
| 1269 | use_global = qt_config->value(name + QStringLiteral("/use_global"), true).value<bool>(); | ||
| 1270 | setting->SetGlobal(use_global); | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | if (global || !use_global) { | ||
| 1274 | const bool is_default = | ||
| 1275 | qt_config->value(name + QStringLiteral("/default"), true).value<bool>(); | ||
| 1276 | if (!is_default) { | ||
| 1277 | setting->LoadString( | ||
| 1278 | qt_config->value(name, default_value).value<QString>().toStdString()); | ||
| 1279 | } else { | ||
| 1280 | // Empty string resets the Setting to default | ||
| 1281 | setting->LoadString(""); | ||
| 1282 | } | ||
| 1283 | } | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | void Config::WriteSettingGeneric(Settings::BasicSetting* const setting) const { | ||
| 1287 | if (!setting->Save()) { | ||
| 1288 | return; | ||
| 1289 | } | ||
| 1290 | const QVariant value = QVariant::fromValue(QString::fromStdString(setting->ToString())); | ||
| 1291 | const QVariant default_value = | ||
| 1292 | QVariant::fromValue(QString::fromStdString(setting->DefaultToString())); | ||
| 1293 | const QString label = QString::fromStdString(setting->GetLabel()); | ||
| 1294 | if (setting->Switchable()) { | ||
| 1295 | if (!global) { | ||
| 1296 | qt_config->setValue(label + QStringLiteral("/use_global"), setting->UsingGlobal()); | ||
| 1297 | } | ||
| 1298 | if (global || !setting->UsingGlobal()) { | ||
| 1299 | qt_config->setValue(label + QStringLiteral("/default"), value == default_value); | ||
| 1300 | qt_config->setValue(label, value); | ||
| 1301 | } | ||
| 1302 | } else if (global) { | ||
| 1303 | qt_config->setValue(label + QStringLiteral("/default"), value == default_value); | ||
| 1304 | qt_config->setValue(label, value); | ||
| 1305 | } | ||
| 1306 | } | ||
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h deleted file mode 100644 index 74ec4f771..000000000 --- a/src/yuzu/configuration/config.h +++ /dev/null | |||
| @@ -1,179 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include <QMetaType> | ||
| 10 | #include <QVariant> | ||
| 11 | #include "common/settings.h" | ||
| 12 | #include "common/settings_enums.h" | ||
| 13 | #include "yuzu/uisettings.h" | ||
| 14 | |||
| 15 | class QSettings; | ||
| 16 | |||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } | ||
| 20 | |||
| 21 | class Config { | ||
| 22 | public: | ||
| 23 | enum class ConfigType { | ||
| 24 | GlobalConfig, | ||
| 25 | PerGameConfig, | ||
| 26 | InputProfile, | ||
| 27 | }; | ||
| 28 | |||
| 29 | explicit Config(const std::string& config_name = "qt-config", | ||
| 30 | ConfigType config_type = ConfigType::GlobalConfig); | ||
| 31 | ~Config(); | ||
| 32 | |||
| 33 | void Reload(); | ||
| 34 | void Save(); | ||
| 35 | |||
| 36 | void ReadControlPlayerValue(std::size_t player_index); | ||
| 37 | void SaveControlPlayerValue(std::size_t player_index); | ||
| 38 | void ClearControlPlayerValues(); | ||
| 39 | |||
| 40 | const std::string& GetConfigFilePath() const; | ||
| 41 | |||
| 42 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; | ||
| 43 | static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; | ||
| 44 | static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; | ||
| 45 | static const std::array<int, 2> default_stick_mod; | ||
| 46 | static const std::array<int, 2> default_ringcon_analogs; | ||
| 47 | static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> | ||
| 48 | default_mouse_buttons; | ||
| 49 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; | ||
| 50 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; | ||
| 51 | static const std::array<UISettings::Shortcut, 23> default_hotkeys; | ||
| 52 | |||
| 53 | static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map; | ||
| 54 | static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map; | ||
| 55 | static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map; | ||
| 56 | static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map; | ||
| 57 | static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map; | ||
| 58 | static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map; | ||
| 59 | |||
| 60 | static constexpr UISettings::Theme default_theme{ | ||
| 61 | #ifdef _WIN32 | ||
| 62 | UISettings::Theme::DarkColorful | ||
| 63 | #else | ||
| 64 | UISettings::Theme::DefaultColorful | ||
| 65 | #endif | ||
| 66 | }; | ||
| 67 | |||
| 68 | private: | ||
| 69 | void Initialize(const std::string& config_name); | ||
| 70 | bool IsCustomConfig(); | ||
| 71 | |||
| 72 | void ReadValues(); | ||
| 73 | void ReadPlayerValue(std::size_t player_index); | ||
| 74 | void ReadDebugValues(); | ||
| 75 | void ReadKeyboardValues(); | ||
| 76 | void ReadMouseValues(); | ||
| 77 | void ReadTouchscreenValues(); | ||
| 78 | void ReadMotionTouchValues(); | ||
| 79 | void ReadHidbusValues(); | ||
| 80 | void ReadIrCameraValues(); | ||
| 81 | |||
| 82 | // Read functions bases off the respective config section names. | ||
| 83 | void ReadAudioValues(); | ||
| 84 | void ReadControlValues(); | ||
| 85 | void ReadCoreValues(); | ||
| 86 | void ReadDataStorageValues(); | ||
| 87 | void ReadDebuggingValues(); | ||
| 88 | void ReadServiceValues(); | ||
| 89 | void ReadDisabledAddOnValues(); | ||
| 90 | void ReadMiscellaneousValues(); | ||
| 91 | void ReadPathValues(); | ||
| 92 | void ReadCpuValues(); | ||
| 93 | void ReadRendererValues(); | ||
| 94 | void ReadScreenshotValues(); | ||
| 95 | void ReadShortcutValues(); | ||
| 96 | void ReadSystemValues(); | ||
| 97 | void ReadUIValues(); | ||
| 98 | void ReadUIGamelistValues(); | ||
| 99 | void ReadUILayoutValues(); | ||
| 100 | void ReadWebServiceValues(); | ||
| 101 | void ReadMultiplayerValues(); | ||
| 102 | void ReadNetworkValues(); | ||
| 103 | |||
| 104 | void SaveValues(); | ||
| 105 | void SavePlayerValue(std::size_t player_index); | ||
| 106 | void SaveDebugValues(); | ||
| 107 | void SaveMouseValues(); | ||
| 108 | void SaveTouchscreenValues(); | ||
| 109 | void SaveMotionTouchValues(); | ||
| 110 | void SaveHidbusValues(); | ||
| 111 | void SaveIrCameraValues(); | ||
| 112 | |||
| 113 | // Save functions based off the respective config section names. | ||
| 114 | void SaveAudioValues(); | ||
| 115 | void SaveControlValues(); | ||
| 116 | void SaveCoreValues(); | ||
| 117 | void SaveDataStorageValues(); | ||
| 118 | void SaveDebuggingValues(); | ||
| 119 | void SaveNetworkValues(); | ||
| 120 | void SaveDisabledAddOnValues(); | ||
| 121 | void SaveMiscellaneousValues(); | ||
| 122 | void SavePathValues(); | ||
| 123 | void SaveCpuValues(); | ||
| 124 | void SaveRendererValues(); | ||
| 125 | void SaveScreenshotValues(); | ||
| 126 | void SaveShortcutValues(); | ||
| 127 | void SaveSystemValues(); | ||
| 128 | void SaveUIValues(); | ||
| 129 | void SaveUIGamelistValues(); | ||
| 130 | void SaveUILayoutValues(); | ||
| 131 | void SaveWebServiceValues(); | ||
| 132 | void SaveMultiplayerValues(); | ||
| 133 | |||
| 134 | /** | ||
| 135 | * Reads a setting from the qt_config. | ||
| 136 | * | ||
| 137 | * @param name The setting's identifier | ||
| 138 | * @param default_value The value to use when the setting is not already present in the config | ||
| 139 | */ | ||
| 140 | QVariant ReadSetting(const QString& name) const; | ||
| 141 | QVariant ReadSetting(const QString& name, const QVariant& default_value) const; | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Writes a setting to the qt_config. | ||
| 145 | * | ||
| 146 | * @param name The setting's idetentifier | ||
| 147 | * @param value Value of the setting | ||
| 148 | * @param default_value Default of the setting if not present in qt_config | ||
| 149 | * @param use_global Specifies if the custom or global config should be in use, for custom | ||
| 150 | * configs | ||
| 151 | */ | ||
| 152 | void WriteSetting(const QString& name, const QVariant& value); | ||
| 153 | void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); | ||
| 154 | void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, | ||
| 155 | bool use_global); | ||
| 156 | |||
| 157 | void ReadCategory(Settings::Category category); | ||
| 158 | void WriteCategory(Settings::Category category); | ||
| 159 | void ReadSettingGeneric(Settings::BasicSetting* const setting); | ||
| 160 | void WriteSettingGeneric(Settings::BasicSetting* const setting) const; | ||
| 161 | |||
| 162 | const ConfigType type; | ||
| 163 | std::unique_ptr<QSettings> qt_config; | ||
| 164 | std::string qt_config_loc; | ||
| 165 | const bool global; | ||
| 166 | }; | ||
| 167 | |||
| 168 | // These metatype declarations cannot be in common/settings.h because core is devoid of QT | ||
| 169 | Q_DECLARE_METATYPE(Settings::CpuAccuracy); | ||
| 170 | Q_DECLARE_METATYPE(Settings::GpuAccuracy); | ||
| 171 | Q_DECLARE_METATYPE(Settings::FullscreenMode); | ||
| 172 | Q_DECLARE_METATYPE(Settings::NvdecEmulation); | ||
| 173 | Q_DECLARE_METATYPE(Settings::ResolutionSetup); | ||
| 174 | Q_DECLARE_METATYPE(Settings::ScalingFilter); | ||
| 175 | Q_DECLARE_METATYPE(Settings::AntiAliasing); | ||
| 176 | Q_DECLARE_METATYPE(Settings::RendererBackend); | ||
| 177 | Q_DECLARE_METATYPE(Settings::ShaderBackend); | ||
| 178 | Q_DECLARE_METATYPE(Settings::AstcRecompression); | ||
| 179 | Q_DECLARE_METATYPE(Settings::AstcDecodeMode); | ||
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index 81dd51ad3..9b6ef47a7 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp | |||
| @@ -38,17 +38,21 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) { | |||
| 38 | 38 | ||
| 39 | std::map<u32, QWidget*> hold; | 39 | std::map<u32, QWidget*> hold; |
| 40 | 40 | ||
| 41 | auto push = [&](Settings::Category category) { | 41 | auto push_settings = [&](Settings::Category category) { |
| 42 | for (auto* setting : Settings::values.linkage.by_category[category]) { | 42 | for (auto* setting : Settings::values.linkage.by_category[category]) { |
| 43 | settings.push_back(setting); | 43 | settings.push_back(setting); |
| 44 | } | 44 | } |
| 45 | }; | ||
| 46 | |||
| 47 | auto push_ui_settings = [&](Settings::Category category) { | ||
| 45 | for (auto* setting : UISettings::values.linkage.by_category[category]) { | 48 | for (auto* setting : UISettings::values.linkage.by_category[category]) { |
| 46 | settings.push_back(setting); | 49 | settings.push_back(setting); |
| 47 | } | 50 | } |
| 48 | }; | 51 | }; |
| 49 | 52 | ||
| 50 | push(Settings::Category::Audio); | 53 | push_settings(Settings::Category::Audio); |
| 51 | push(Settings::Category::SystemAudio); | 54 | push_settings(Settings::Category::SystemAudio); |
| 55 | push_ui_settings(Settings::Category::UiAudio); | ||
| 52 | 56 | ||
| 53 | for (auto* setting : settings) { | 57 | for (auto* setting : settings) { |
| 54 | auto* widget = builder.BuildWidget(setting, apply_funcs); | 58 | auto* widget = builder.BuildWidget(setting, apply_funcs); |
diff --git a/src/yuzu/configuration/configure_camera.cpp b/src/yuzu/configuration/configure_camera.cpp index d95e96696..3368f53f3 100644 --- a/src/yuzu/configuration/configure_camera.cpp +++ b/src/yuzu/configuration/configure_camera.cpp | |||
| @@ -10,10 +10,10 @@ | |||
| 10 | #include <QStandardItemModel> | 10 | #include <QStandardItemModel> |
| 11 | #include <QTimer> | 11 | #include <QTimer> |
| 12 | 12 | ||
| 13 | #include "common/settings.h" | ||
| 13 | #include "input_common/drivers/camera.h" | 14 | #include "input_common/drivers/camera.h" |
| 14 | #include "input_common/main.h" | 15 | #include "input_common/main.h" |
| 15 | #include "ui_configure_camera.h" | 16 | #include "ui_configure_camera.h" |
| 16 | #include "yuzu/configuration/config.h" | ||
| 17 | #include "yuzu/configuration/configure_camera.h" | 17 | #include "yuzu/configuration/configure_camera.h" |
| 18 | 18 | ||
| 19 | ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) | 19 | ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) |
diff --git a/src/yuzu/configuration/configure_camera.h b/src/yuzu/configuration/configure_camera.h index 9a90512b3..3d822da7b 100644 --- a/src/yuzu/configuration/configure_camera.h +++ b/src/yuzu/configuration/configure_camera.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Text : Copyright 2022 yuzu Emulator Project | 1 | // Text : Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index ef421c754..1010038b7 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp | |||
| @@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() { | |||
| 51 | ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue()); | 51 | ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue()); |
| 52 | ui->enable_renderdoc_hotkey->setEnabled(runtime_lock); | 52 | ui->enable_renderdoc_hotkey->setEnabled(runtime_lock); |
| 53 | ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue()); | 53 | ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue()); |
| 54 | ui->disable_buffer_reorder->setEnabled(runtime_lock); | ||
| 55 | ui->disable_buffer_reorder->setChecked(Settings::values.disable_buffer_reorder.GetValue()); | ||
| 54 | ui->enable_graphics_debugging->setEnabled(runtime_lock); | 56 | ui->enable_graphics_debugging->setEnabled(runtime_lock); |
| 55 | ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); | 57 | ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); |
| 56 | ui->enable_shader_feedback->setEnabled(runtime_lock); | 58 | ui->enable_shader_feedback->setEnabled(runtime_lock); |
| @@ -96,6 +98,7 @@ void ConfigureDebug::ApplyConfiguration() { | |||
| 96 | Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked(); | 98 | Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked(); |
| 97 | Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); | 99 | Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); |
| 98 | Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked(); | 100 | Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked(); |
| 101 | Settings::values.disable_buffer_reorder = ui->disable_buffer_reorder->isChecked(); | ||
| 99 | Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); | 102 | Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); |
| 100 | Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); | 103 | Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); |
| 101 | Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); | 104 | Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 76fe98924..22b51f39c 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -271,19 +271,6 @@ | |||
| 271 | </widget> | 271 | </widget> |
| 272 | </item> | 272 | </item> |
| 273 | <item row="8" column="0"> | 273 | <item row="8" column="0"> |
| 274 | <widget class="QCheckBox" name="disable_macro_hle"> | ||
| 275 | <property name="enabled"> | ||
| 276 | <bool>true</bool> | ||
| 277 | </property> | ||
| 278 | <property name="toolTip"> | ||
| 279 | <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string> | ||
| 280 | </property> | ||
| 281 | <property name="text"> | ||
| 282 | <string>Disable Macro HLE</string> | ||
| 283 | </property> | ||
| 284 | </widget> | ||
| 285 | </item> | ||
| 286 | <item row="7" column="0"> | ||
| 287 | <widget class="QCheckBox" name="dump_macros"> | 274 | <widget class="QCheckBox" name="dump_macros"> |
| 288 | <property name="enabled"> | 275 | <property name="enabled"> |
| 289 | <bool>true</bool> | 276 | <bool>true</bool> |
| @@ -306,17 +293,27 @@ | |||
| 306 | </property> | 293 | </property> |
| 307 | </widget> | 294 | </widget> |
| 308 | </item> | 295 | </item> |
| 309 | <item row="2" column="0"> | 296 | <item row="6" column="0"> |
| 310 | <widget class="QCheckBox" name="enable_shader_feedback"> | 297 | <widget class="QCheckBox" name="dump_shaders"> |
| 298 | <property name="enabled"> | ||
| 299 | <bool>true</bool> | ||
| 300 | </property> | ||
| 311 | <property name="toolTip"> | 301 | <property name="toolTip"> |
| 312 | <string>When checked, yuzu will log statistics about the compiled pipeline cache</string> | 302 | <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string> |
| 313 | </property> | 303 | </property> |
| 314 | <property name="text"> | 304 | <property name="text"> |
| 315 | <string>Enable Shader Feedback</string> | 305 | <string>Dump Game Shaders</string> |
| 316 | </property> | 306 | </property> |
| 317 | </widget> | 307 | </widget> |
| 318 | </item> | 308 | </item> |
| 319 | <item row="6" column="0"> | 309 | <item row="1" column="0"> |
| 310 | <widget class="QCheckBox" name="enable_renderdoc_hotkey"> | ||
| 311 | <property name="text"> | ||
| 312 | <string>Enable Renderdoc Hotkey</string> | ||
| 313 | </property> | ||
| 314 | </widget> | ||
| 315 | </item> | ||
| 316 | <item row="7" column="0"> | ||
| 320 | <widget class="QCheckBox" name="disable_macro_jit"> | 317 | <widget class="QCheckBox" name="disable_macro_jit"> |
| 321 | <property name="enabled"> | 318 | <property name="enabled"> |
| 322 | <bool>true</bool> | 319 | <bool>true</bool> |
| @@ -330,20 +327,17 @@ | |||
| 330 | </widget> | 327 | </widget> |
| 331 | </item> | 328 | </item> |
| 332 | <item row="9" column="0"> | 329 | <item row="9" column="0"> |
| 333 | <spacer name="verticalSpacer_5"> | 330 | <widget class="QCheckBox" name="disable_macro_hle"> |
| 334 | <property name="orientation"> | 331 | <property name="enabled"> |
| 335 | <enum>Qt::Vertical</enum> | 332 | <bool>true</bool> |
| 336 | </property> | 333 | </property> |
| 337 | <property name="sizeType"> | 334 | <property name="toolTip"> |
| 338 | <enum>QSizePolicy::Preferred</enum> | 335 | <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string> |
| 339 | </property> | 336 | </property> |
| 340 | <property name="sizeHint" stdset="0"> | 337 | <property name="text"> |
| 341 | <size> | 338 | <string>Disable Macro HLE</string> |
| 342 | <width>20</width> | ||
| 343 | <height>0</height> | ||
| 344 | </size> | ||
| 345 | </property> | 339 | </property> |
| 346 | </spacer> | 340 | </widget> |
| 347 | </item> | 341 | </item> |
| 348 | <item row="0" column="0"> | 342 | <item row="0" column="0"> |
| 349 | <widget class="QCheckBox" name="enable_graphics_debugging"> | 343 | <widget class="QCheckBox" name="enable_graphics_debugging"> |
| @@ -358,23 +352,39 @@ | |||
| 358 | </property> | 352 | </property> |
| 359 | </widget> | 353 | </widget> |
| 360 | </item> | 354 | </item> |
| 361 | <item row="5" column="0"> | 355 | <item row="10" column="0"> |
| 362 | <widget class="QCheckBox" name="dump_shaders"> | 356 | <spacer name="verticalSpacer_5"> |
| 363 | <property name="enabled"> | 357 | <property name="orientation"> |
| 364 | <bool>true</bool> | 358 | <enum>Qt::Vertical</enum> |
| 359 | </property> | ||
| 360 | <property name="sizeType"> | ||
| 361 | <enum>QSizePolicy::Preferred</enum> | ||
| 365 | </property> | 362 | </property> |
| 363 | <property name="sizeHint" stdset="0"> | ||
| 364 | <size> | ||
| 365 | <width>20</width> | ||
| 366 | <height>0</height> | ||
| 367 | </size> | ||
| 368 | </property> | ||
| 369 | </spacer> | ||
| 370 | </item> | ||
| 371 | <item row="2" column="0"> | ||
| 372 | <widget class="QCheckBox" name="enable_shader_feedback"> | ||
| 366 | <property name="toolTip"> | 373 | <property name="toolTip"> |
| 367 | <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string> | 374 | <string>When checked, yuzu will log statistics about the compiled pipeline cache</string> |
| 368 | </property> | 375 | </property> |
| 369 | <property name="text"> | 376 | <property name="text"> |
| 370 | <string>Dump Game Shaders</string> | 377 | <string>Enable Shader Feedback</string> |
| 371 | </property> | 378 | </property> |
| 372 | </widget> | 379 | </widget> |
| 373 | </item> | 380 | </item> |
| 374 | <item row="1" column="0"> | 381 | <item row="5" column="0"> |
| 375 | <widget class="QCheckBox" name="enable_renderdoc_hotkey"> | 382 | <widget class="QCheckBox" name="disable_buffer_reorder"> |
| 383 | <property name="toolTip"> | ||
| 384 | <string><html><head/><body><p>When checked, disables reording of mapped memory uploads which allows to associate uploads with specific draws. May reduce performance in some cases.</p></body></html></string> | ||
| 385 | </property> | ||
| 376 | <property name="text"> | 386 | <property name="text"> |
| 377 | <string>Enable Renderdoc Hotkey</string> | 387 | <string>Disable Buffer Reorder</string> |
| 378 | </property> | 388 | </property> |
| 379 | </widget> | 389 | </widget> |
| 380 | </item> | 390 | </item> |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 0ad95cc02..aab54a1cc 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "ui_configure.h" | 9 | #include "ui_configure.h" |
| 10 | #include "vk_device_info.h" | 10 | #include "vk_device_info.h" |
| 11 | #include "yuzu/configuration/config.h" | ||
| 12 | #include "yuzu/configuration/configure_audio.h" | 11 | #include "yuzu/configuration/configure_audio.h" |
| 13 | #include "yuzu/configuration/configure_cpu.h" | 12 | #include "yuzu/configuration/configure_cpu.h" |
| 14 | #include "yuzu/configuration/configure_debug_tab.h" | 13 | #include "yuzu/configuration/configure_debug_tab.h" |
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index 68e21cd84..76fc33e49 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp | |||
| @@ -9,10 +9,11 @@ | |||
| 9 | #include "core/hid/emulated_controller.h" | 9 | #include "core/hid/emulated_controller.h" |
| 10 | #include "core/hid/hid_core.h" | 10 | #include "core/hid/hid_core.h" |
| 11 | 11 | ||
| 12 | #include "frontend_common/config.h" | ||
| 12 | #include "ui_configure_hotkeys.h" | 13 | #include "ui_configure_hotkeys.h" |
| 13 | #include "yuzu/configuration/config.h" | ||
| 14 | #include "yuzu/configuration/configure_hotkeys.h" | 14 | #include "yuzu/configuration/configure_hotkeys.h" |
| 15 | #include "yuzu/hotkeys.h" | 15 | #include "yuzu/hotkeys.h" |
| 16 | #include "yuzu/uisettings.h" | ||
| 16 | #include "yuzu/util/sequence_dialog/sequence_dialog.h" | 17 | #include "yuzu/util/sequence_dialog/sequence_dialog.h" |
| 17 | 18 | ||
| 18 | constexpr int name_column = 0; | 19 | constexpr int name_column = 0; |
| @@ -62,18 +63,21 @@ ConfigureHotkeys::~ConfigureHotkeys() = default; | |||
| 62 | 63 | ||
| 63 | void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { | 64 | void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { |
| 64 | for (const auto& group : registry.hotkey_groups) { | 65 | for (const auto& group : registry.hotkey_groups) { |
| 66 | QString parent_item_data = QString::fromStdString(group.first); | ||
| 65 | auto* parent_item = | 67 | auto* parent_item = |
| 66 | new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first))); | 68 | new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(parent_item_data))); |
| 67 | parent_item->setEditable(false); | 69 | parent_item->setEditable(false); |
| 68 | parent_item->setData(group.first); | 70 | parent_item->setData(parent_item_data); |
| 69 | for (const auto& hotkey : group.second) { | 71 | for (const auto& hotkey : group.second) { |
| 70 | auto* action = | 72 | QString hotkey_action_data = QString::fromStdString(hotkey.first); |
| 71 | new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first))); | 73 | auto* action = new QStandardItem( |
| 74 | QCoreApplication::translate("Hotkeys", qPrintable(hotkey_action_data))); | ||
| 72 | auto* keyseq = | 75 | auto* keyseq = |
| 73 | new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); | 76 | new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); |
| 74 | auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); | 77 | auto* controller_keyseq = |
| 78 | new QStandardItem(QString::fromStdString(hotkey.second.controller_keyseq)); | ||
| 75 | action->setEditable(false); | 79 | action->setEditable(false); |
| 76 | action->setData(hotkey.first); | 80 | action->setData(hotkey_action_data); |
| 77 | keyseq->setEditable(false); | 81 | keyseq->setEditable(false); |
| 78 | controller_keyseq->setEditable(false); | 82 | controller_keyseq->setEditable(false); |
| 79 | parent_item->appendRow({action, keyseq, controller_keyseq}); | 83 | parent_item->appendRow({action, keyseq, controller_keyseq}); |
| @@ -301,13 +305,13 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { | |||
| 301 | const QStandardItem* controller_keyseq = | 305 | const QStandardItem* controller_keyseq = |
| 302 | parent->child(key_column_id, controller_column); | 306 | parent->child(key_column_id, controller_column); |
| 303 | for (auto& [group, sub_actions] : registry.hotkey_groups) { | 307 | for (auto& [group, sub_actions] : registry.hotkey_groups) { |
| 304 | if (group != parent->data()) | 308 | if (group != parent->data().toString().toStdString()) |
| 305 | continue; | 309 | continue; |
| 306 | for (auto& [action_name, hotkey] : sub_actions) { | 310 | for (auto& [action_name, hotkey] : sub_actions) { |
| 307 | if (action_name != action->data()) | 311 | if (action_name != action->data().toString().toStdString()) |
| 308 | continue; | 312 | continue; |
| 309 | hotkey.keyseq = QKeySequence(keyseq->text()); | 313 | hotkey.keyseq = QKeySequence(keyseq->text()); |
| 310 | hotkey.controller_keyseq = controller_keyseq->text(); | 314 | hotkey.controller_keyseq = controller_keyseq->text().toStdString(); |
| 311 | } | 315 | } |
| 312 | } | 316 | } |
| 313 | } | 317 | } |
| @@ -319,7 +323,7 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { | |||
| 319 | void ConfigureHotkeys::RestoreDefaults() { | 323 | void ConfigureHotkeys::RestoreDefaults() { |
| 320 | for (int r = 0; r < model->rowCount(); ++r) { | 324 | for (int r = 0; r < model->rowCount(); ++r) { |
| 321 | const QStandardItem* parent = model->item(r, 0); | 325 | const QStandardItem* parent = model->item(r, 0); |
| 322 | const int hotkey_size = static_cast<int>(Config::default_hotkeys.size()); | 326 | const int hotkey_size = static_cast<int>(UISettings::default_hotkeys.size()); |
| 323 | 327 | ||
| 324 | if (hotkey_size != parent->rowCount()) { | 328 | if (hotkey_size != parent->rowCount()) { |
| 325 | QMessageBox::warning(this, tr("Invalid hotkey settings"), | 329 | QMessageBox::warning(this, tr("Invalid hotkey settings"), |
| @@ -330,10 +334,11 @@ void ConfigureHotkeys::RestoreDefaults() { | |||
| 330 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { | 334 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { |
| 331 | model->item(r, 0) | 335 | model->item(r, 0) |
| 332 | ->child(r2, hotkey_column) | 336 | ->child(r2, hotkey_column) |
| 333 | ->setText(Config::default_hotkeys[r2].shortcut.keyseq); | 337 | ->setText(QString::fromStdString(UISettings::default_hotkeys[r2].shortcut.keyseq)); |
| 334 | model->item(r, 0) | 338 | model->item(r, 0) |
| 335 | ->child(r2, controller_column) | 339 | ->child(r2, controller_column) |
| 336 | ->setText(Config::default_hotkeys[r2].shortcut.controller_keyseq); | 340 | ->setText(QString::fromStdString( |
| 341 | UISettings::default_hotkeys[r2].shortcut.controller_keyseq)); | ||
| 337 | } | 342 | } |
| 338 | } | 343 | } |
| 339 | } | 344 | } |
| @@ -379,7 +384,7 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) { | |||
| 379 | 384 | ||
| 380 | void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { | 385 | void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { |
| 381 | const QString& default_key_sequence = | 386 | const QString& default_key_sequence = |
| 382 | Config::default_hotkeys[index.row()].shortcut.controller_keyseq; | 387 | QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.controller_keyseq); |
| 383 | const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence); | 388 | const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence); |
| 384 | 389 | ||
| 385 | if (key_sequence_used && default_key_sequence != model->data(index).toString()) { | 390 | if (key_sequence_used && default_key_sequence != model->data(index).toString()) { |
| @@ -393,7 +398,8 @@ void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { | |||
| 393 | 398 | ||
| 394 | void ConfigureHotkeys::RestoreHotkey(QModelIndex index) { | 399 | void ConfigureHotkeys::RestoreHotkey(QModelIndex index) { |
| 395 | const QKeySequence& default_key_sequence = QKeySequence::fromString( | 400 | const QKeySequence& default_key_sequence = QKeySequence::fromString( |
| 396 | Config::default_hotkeys[index.row()].shortcut.keyseq, QKeySequence::NativeText); | 401 | QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.keyseq), |
| 402 | QKeySequence::NativeText); | ||
| 397 | const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence); | 403 | const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence); |
| 398 | 404 | ||
| 399 | if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) { | 405 | if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) { |
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 3dcad2701..02e23cce6 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp | |||
| @@ -152,7 +152,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, | |||
| 152 | connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged, | 152 | connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged, |
| 153 | [this](bool is_handheld) { UpdateDockedState(is_handheld); }); | 153 | [this](bool is_handheld) { UpdateDockedState(is_handheld); }); |
| 154 | 154 | ||
| 155 | advanced = new ConfigureInputAdvanced(this); | 155 | advanced = new ConfigureInputAdvanced(hid_core, this); |
| 156 | ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); | 156 | ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); |
| 157 | ui->tabAdvanced->layout()->addWidget(advanced); | 157 | ui->tabAdvanced->layout()->addWidget(advanced); |
| 158 | 158 | ||
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index 136cd3a0a..beb503dae 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 3cfd5d439..441cea3f6 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp | |||
| @@ -4,11 +4,13 @@ | |||
| 4 | #include <QColorDialog> | 4 | #include <QColorDialog> |
| 5 | #include "common/settings.h" | 5 | #include "common/settings.h" |
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "core/hid/emulated_controller.h" | ||
| 8 | #include "core/hid/hid_core.h" | ||
| 7 | #include "ui_configure_input_advanced.h" | 9 | #include "ui_configure_input_advanced.h" |
| 8 | #include "yuzu/configuration/configure_input_advanced.h" | 10 | #include "yuzu/configuration/configure_input_advanced.h" |
| 9 | 11 | ||
| 10 | ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) | 12 | ConfigureInputAdvanced::ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent) |
| 11 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) { | 13 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()), hid_core{hid_core_} { |
| 12 | ui->setupUi(this); | 14 | ui->setupUi(this); |
| 13 | 15 | ||
| 14 | controllers_color_buttons = {{ | 16 | controllers_color_buttons = {{ |
| @@ -123,6 +125,8 @@ void ConfigureInputAdvanced::ApplyConfiguration() { | |||
| 123 | player.button_color_left = colors[1]; | 125 | player.button_color_left = colors[1]; |
| 124 | player.body_color_right = colors[2]; | 126 | player.body_color_right = colors[2]; |
| 125 | player.button_color_right = colors[3]; | 127 | player.button_color_right = colors[3]; |
| 128 | |||
| 129 | hid_core.GetEmulatedControllerByIndex(player_idx)->ReloadColorsFromSettings(); | ||
| 126 | } | 130 | } |
| 127 | 131 | ||
| 128 | Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked(); | 132 | Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked(); |
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h index fc1230284..41f822c4a 100644 --- a/src/yuzu/configuration/configure_input_advanced.h +++ b/src/yuzu/configuration/configure_input_advanced.h | |||
| @@ -14,11 +14,15 @@ namespace Ui { | |||
| 14 | class ConfigureInputAdvanced; | 14 | class ConfigureInputAdvanced; |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | namespace Core::HID { | ||
| 18 | class HIDCore; | ||
| 19 | } // namespace Core::HID | ||
| 20 | |||
| 17 | class ConfigureInputAdvanced : public QWidget { | 21 | class ConfigureInputAdvanced : public QWidget { |
| 18 | Q_OBJECT | 22 | Q_OBJECT |
| 19 | 23 | ||
| 20 | public: | 24 | public: |
| 21 | explicit ConfigureInputAdvanced(QWidget* parent = nullptr); | 25 | explicit ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent = nullptr); |
| 22 | ~ConfigureInputAdvanced() override; | 26 | ~ConfigureInputAdvanced() override; |
| 23 | 27 | ||
| 24 | void ApplyConfiguration(); | 28 | void ApplyConfiguration(); |
| @@ -44,4 +48,6 @@ private: | |||
| 44 | 48 | ||
| 45 | std::array<std::array<QColor, 4>, 8> controllers_colors; | 49 | std::array<std::array<QColor, 4>, 8> controllers_colors; |
| 46 | std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons; | 50 | std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons; |
| 51 | |||
| 52 | Core::HID::HIDCore& hid_core; | ||
| 47 | }; | 53 | }; |
diff --git a/src/yuzu/configuration/configure_input_per_game.cpp b/src/yuzu/configuration/configure_input_per_game.cpp index 78e65d468..8d9f65a05 100644 --- a/src/yuzu/configuration/configure_input_per_game.cpp +++ b/src/yuzu/configuration/configure_input_per_game.cpp | |||
| @@ -5,12 +5,12 @@ | |||
| 5 | #include "core/core.h" | 5 | #include "core/core.h" |
| 6 | #include "core/hid/emulated_controller.h" | 6 | #include "core/hid/emulated_controller.h" |
| 7 | #include "core/hid/hid_core.h" | 7 | #include "core/hid/hid_core.h" |
| 8 | #include "frontend_common/config.h" | ||
| 8 | #include "ui_configure_input_per_game.h" | 9 | #include "ui_configure_input_per_game.h" |
| 9 | #include "yuzu/configuration/config.h" | ||
| 10 | #include "yuzu/configuration/configure_input_per_game.h" | 10 | #include "yuzu/configuration/configure_input_per_game.h" |
| 11 | #include "yuzu/configuration/input_profiles.h" | 11 | #include "yuzu/configuration/input_profiles.h" |
| 12 | 12 | ||
| 13 | ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, Config* config_, | 13 | ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, QtConfig* config_, |
| 14 | QWidget* parent) | 14 | QWidget* parent) |
| 15 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPerGame>()), | 15 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPerGame>()), |
| 16 | profiles(std::make_unique<InputProfiles>()), system{system_}, config{config_} { | 16 | profiles(std::make_unique<InputProfiles>()), system{system_}, config{config_} { |
| @@ -110,6 +110,6 @@ void ConfigureInputPerGame::SaveConfiguration() { | |||
| 110 | // Clear all controls from the config in case the user reverted back to globals | 110 | // Clear all controls from the config in case the user reverted back to globals |
| 111 | config->ClearControlPlayerValues(); | 111 | config->ClearControlPlayerValues(); |
| 112 | for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) { | 112 | for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) { |
| 113 | config->SaveControlPlayerValue(index); | 113 | config->SaveQtControlPlayerValues(index); |
| 114 | } | 114 | } |
| 115 | } | 115 | } |
diff --git a/src/yuzu/configuration/configure_input_per_game.h b/src/yuzu/configuration/configure_input_per_game.h index 660faf574..4420e856c 100644 --- a/src/yuzu/configuration/configure_input_per_game.h +++ b/src/yuzu/configuration/configure_input_per_game.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include "ui_configure_input_per_game.h" | 10 | #include "ui_configure_input_per_game.h" |
| 11 | #include "yuzu/configuration/input_profiles.h" | 11 | #include "yuzu/configuration/input_profiles.h" |
| 12 | #include "yuzu/configuration/qt_config.h" | ||
| 12 | 13 | ||
| 13 | class QComboBox; | 14 | class QComboBox; |
| 14 | 15 | ||
| @@ -22,7 +23,7 @@ class ConfigureInputPerGame : public QWidget { | |||
| 22 | Q_OBJECT | 23 | Q_OBJECT |
| 23 | 24 | ||
| 24 | public: | 25 | public: |
| 25 | explicit ConfigureInputPerGame(Core::System& system_, Config* config_, | 26 | explicit ConfigureInputPerGame(Core::System& system_, QtConfig* config_, |
| 26 | QWidget* parent = nullptr); | 27 | QWidget* parent = nullptr); |
| 27 | 28 | ||
| 28 | /// Load and Save configurations to settings file. | 29 | /// Load and Save configurations to settings file. |
| @@ -41,5 +42,5 @@ private: | |||
| 41 | std::array<QComboBox*, 8> profile_comboboxes; | 42 | std::array<QComboBox*, 8> profile_comboboxes; |
| 42 | 43 | ||
| 43 | Core::System& system; | 44 | Core::System& system; |
| 44 | Config* config; | 45 | QtConfig* config; |
| 45 | }; | 46 | }; |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 576f5b571..0f7b3714e 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -12,15 +12,16 @@ | |||
| 12 | #include <QTimer> | 12 | #include <QTimer> |
| 13 | #include "common/assert.h" | 13 | #include "common/assert.h" |
| 14 | #include "common/param_package.h" | 14 | #include "common/param_package.h" |
| 15 | #include "configuration/qt_config.h" | ||
| 15 | #include "core/hid/emulated_controller.h" | 16 | #include "core/hid/emulated_controller.h" |
| 16 | #include "core/hid/hid_core.h" | 17 | #include "core/hid/hid_core.h" |
| 17 | #include "core/hid/hid_types.h" | 18 | #include "core/hid/hid_types.h" |
| 19 | #include "frontend_common/config.h" | ||
| 18 | #include "input_common/drivers/keyboard.h" | 20 | #include "input_common/drivers/keyboard.h" |
| 19 | #include "input_common/drivers/mouse.h" | 21 | #include "input_common/drivers/mouse.h" |
| 20 | #include "input_common/main.h" | 22 | #include "input_common/main.h" |
| 21 | #include "ui_configure_input_player.h" | 23 | #include "ui_configure_input_player.h" |
| 22 | #include "yuzu/bootmanager.h" | 24 | #include "yuzu/bootmanager.h" |
| 23 | #include "yuzu/configuration/config.h" | ||
| 24 | #include "yuzu/configuration/configure_input_player.h" | 25 | #include "yuzu/configuration/configure_input_player.h" |
| 25 | #include "yuzu/configuration/configure_input_player_widget.h" | 26 | #include "yuzu/configuration/configure_input_player_widget.h" |
| 26 | #include "yuzu/configuration/configure_mouse_panning.h" | 27 | #include "yuzu/configuration/configure_mouse_panning.h" |
| @@ -322,11 +323,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 322 | setFocusPolicy(Qt::ClickFocus); | 323 | setFocusPolicy(Qt::ClickFocus); |
| 323 | 324 | ||
| 324 | button_map = { | 325 | button_map = { |
| 325 | ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, | 326 | ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, |
| 326 | ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, | 327 | ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, |
| 327 | ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, | 328 | ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, |
| 328 | ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, | 329 | ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, |
| 329 | ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot, | 330 | ui->buttonSLLeft, ui->buttonSRLeft, ui->buttonHome, ui->buttonScreenshot, |
| 331 | ui->buttonSLRight, ui->buttonSRRight, | ||
| 330 | }; | 332 | }; |
| 331 | 333 | ||
| 332 | analog_map_buttons = {{ | 334 | analog_map_buttons = {{ |
| @@ -1181,10 +1183,13 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { | |||
| 1181 | 1183 | ||
| 1182 | // List of all the widgets that will be hidden by any of the following layouts that need | 1184 | // List of all the widgets that will be hidden by any of the following layouts that need |
| 1183 | // "unhidden" after the controller type changes | 1185 | // "unhidden" after the controller type changes |
| 1184 | const std::array<QWidget*, 11> layout_show = { | 1186 | const std::array<QWidget*, 14> layout_show = { |
| 1185 | ui->buttonShoulderButtonsSLSR, | 1187 | ui->buttonShoulderButtonsSLSRLeft, |
| 1188 | ui->buttonShoulderButtonsSLSRRight, | ||
| 1186 | ui->horizontalSpacerShoulderButtonsWidget, | 1189 | ui->horizontalSpacerShoulderButtonsWidget, |
| 1187 | ui->horizontalSpacerShoulderButtonsWidget2, | 1190 | ui->horizontalSpacerShoulderButtonsWidget2, |
| 1191 | ui->horizontalSpacerShoulderButtonsWidget3, | ||
| 1192 | ui->horizontalSpacerShoulderButtonsWidget4, | ||
| 1188 | ui->buttonShoulderButtonsLeft, | 1193 | ui->buttonShoulderButtonsLeft, |
| 1189 | ui->buttonMiscButtonsMinusScreenshot, | 1194 | ui->buttonMiscButtonsMinusScreenshot, |
| 1190 | ui->bottomLeft, | 1195 | ui->bottomLeft, |
| @@ -1202,16 +1207,19 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { | |||
| 1202 | std::vector<QWidget*> layout_hidden; | 1207 | std::vector<QWidget*> layout_hidden; |
| 1203 | switch (layout) { | 1208 | switch (layout) { |
| 1204 | case Core::HID::NpadStyleIndex::ProController: | 1209 | case Core::HID::NpadStyleIndex::ProController: |
| 1205 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 1206 | case Core::HID::NpadStyleIndex::Handheld: | 1210 | case Core::HID::NpadStyleIndex::Handheld: |
| 1207 | layout_hidden = { | 1211 | layout_hidden = { |
| 1208 | ui->buttonShoulderButtonsSLSR, | 1212 | ui->buttonShoulderButtonsSLSRLeft, |
| 1213 | ui->buttonShoulderButtonsSLSRRight, | ||
| 1209 | ui->horizontalSpacerShoulderButtonsWidget2, | 1214 | ui->horizontalSpacerShoulderButtonsWidget2, |
| 1215 | ui->horizontalSpacerShoulderButtonsWidget4, | ||
| 1210 | }; | 1216 | }; |
| 1211 | break; | 1217 | break; |
| 1212 | case Core::HID::NpadStyleIndex::JoyconLeft: | 1218 | case Core::HID::NpadStyleIndex::JoyconLeft: |
| 1213 | layout_hidden = { | 1219 | layout_hidden = { |
| 1220 | ui->buttonShoulderButtonsSLSRRight, | ||
| 1214 | ui->horizontalSpacerShoulderButtonsWidget2, | 1221 | ui->horizontalSpacerShoulderButtonsWidget2, |
| 1222 | ui->horizontalSpacerShoulderButtonsWidget3, | ||
| 1215 | ui->buttonShoulderButtonsRight, | 1223 | ui->buttonShoulderButtonsRight, |
| 1216 | ui->buttonMiscButtonsPlusHome, | 1224 | ui->buttonMiscButtonsPlusHome, |
| 1217 | ui->bottomRight, | 1225 | ui->bottomRight, |
| @@ -1219,16 +1227,17 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { | |||
| 1219 | break; | 1227 | break; |
| 1220 | case Core::HID::NpadStyleIndex::JoyconRight: | 1228 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 1221 | layout_hidden = { | 1229 | layout_hidden = { |
| 1222 | ui->horizontalSpacerShoulderButtonsWidget, | 1230 | ui->buttonShoulderButtonsSLSRLeft, ui->horizontalSpacerShoulderButtonsWidget, |
| 1223 | ui->buttonShoulderButtonsLeft, | 1231 | ui->horizontalSpacerShoulderButtonsWidget4, ui->buttonShoulderButtonsLeft, |
| 1224 | ui->buttonMiscButtonsMinusScreenshot, | 1232 | ui->buttonMiscButtonsMinusScreenshot, ui->bottomLeft, |
| 1225 | ui->bottomLeft, | ||
| 1226 | }; | 1233 | }; |
| 1227 | break; | 1234 | break; |
| 1228 | case Core::HID::NpadStyleIndex::GameCube: | 1235 | case Core::HID::NpadStyleIndex::GameCube: |
| 1229 | layout_hidden = { | 1236 | layout_hidden = { |
| 1230 | ui->buttonShoulderButtonsSLSR, | 1237 | ui->buttonShoulderButtonsSLSRLeft, |
| 1238 | ui->buttonShoulderButtonsSLSRRight, | ||
| 1231 | ui->horizontalSpacerShoulderButtonsWidget2, | 1239 | ui->horizontalSpacerShoulderButtonsWidget2, |
| 1240 | ui->horizontalSpacerShoulderButtonsWidget4, | ||
| 1232 | ui->buttonMiscButtonsMinusGroup, | 1241 | ui->buttonMiscButtonsMinusGroup, |
| 1233 | ui->buttonMiscButtonsScreenshotGroup, | 1242 | ui->buttonMiscButtonsScreenshotGroup, |
| 1234 | }; | 1243 | }; |
| @@ -1389,25 +1398,25 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() { | |||
| 1389 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { | 1398 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { |
| 1390 | emulated_controller->SetButtonParam( | 1399 | emulated_controller->SetButtonParam( |
| 1391 | button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( | 1400 | button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( |
| 1392 | Config::default_buttons[button_id])}); | 1401 | QtConfig::default_buttons[button_id])}); |
| 1393 | } | 1402 | } |
| 1394 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { | 1403 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { |
| 1395 | Common::ParamPackage analog_param{}; | 1404 | Common::ParamPackage analog_param{}; |
| 1396 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { | 1405 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { |
| 1397 | Common::ParamPackage params{InputCommon::GenerateKeyboardParam( | 1406 | Common::ParamPackage params{InputCommon::GenerateKeyboardParam( |
| 1398 | Config::default_analogs[analog_id][sub_button_id])}; | 1407 | QtConfig::default_analogs[analog_id][sub_button_id])}; |
| 1399 | SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]); | 1408 | SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]); |
| 1400 | } | 1409 | } |
| 1401 | 1410 | ||
| 1402 | analog_param.Set("modifier", InputCommon::GenerateKeyboardParam( | 1411 | analog_param.Set("modifier", InputCommon::GenerateKeyboardParam( |
| 1403 | Config::default_stick_mod[analog_id])); | 1412 | QtConfig::default_stick_mod[analog_id])); |
| 1404 | emulated_controller->SetStickParam(analog_id, analog_param); | 1413 | emulated_controller->SetStickParam(analog_id, analog_param); |
| 1405 | } | 1414 | } |
| 1406 | 1415 | ||
| 1407 | for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { | 1416 | for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { |
| 1408 | emulated_controller->SetMotionParam( | 1417 | emulated_controller->SetMotionParam( |
| 1409 | motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( | 1418 | motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( |
| 1410 | Config::default_motions[motion_id])}); | 1419 | QtConfig::default_motions[motion_id])}); |
| 1411 | } | 1420 | } |
| 1412 | 1421 | ||
| 1413 | // If mouse is selected we want to override with mappings from the driver | 1422 | // If mouse is selected we want to override with mappings from the driver |
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index d3255d2b4..fda09e925 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 611a79477..5518cccd1 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui | |||
| @@ -1208,6 +1208,159 @@ | |||
| 1208 | <property name="spacing"> | 1208 | <property name="spacing"> |
| 1209 | <number>3</number> | 1209 | <number>3</number> |
| 1210 | </property> | 1210 | </property> |
| 1211 | <item> | ||
| 1212 | <widget class="QWidget" name="buttonShoulderButtonsSLSRLeft" native="true"> | ||
| 1213 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRLeftVerticalLayout"> | ||
| 1214 | <property name="spacing"> | ||
| 1215 | <number>0</number> | ||
| 1216 | </property> | ||
| 1217 | <property name="leftMargin"> | ||
| 1218 | <number>0</number> | ||
| 1219 | </property> | ||
| 1220 | <property name="topMargin"> | ||
| 1221 | <number>0</number> | ||
| 1222 | </property> | ||
| 1223 | <property name="rightMargin"> | ||
| 1224 | <number>0</number> | ||
| 1225 | </property> | ||
| 1226 | <property name="bottomMargin"> | ||
| 1227 | <number>0</number> | ||
| 1228 | </property> | ||
| 1229 | <item alignment="Qt::AlignHCenter"> | ||
| 1230 | <widget class="QGroupBox" name="buttonShoulderButtonsSLLeftGroup"> | ||
| 1231 | <property name="title"> | ||
| 1232 | <string>SL</string> | ||
| 1233 | </property> | ||
| 1234 | <property name="alignment"> | ||
| 1235 | <set>Qt::AlignCenter</set> | ||
| 1236 | </property> | ||
| 1237 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSLLeftVerticalLayout"> | ||
| 1238 | <property name="spacing"> | ||
| 1239 | <number>3</number> | ||
| 1240 | </property> | ||
| 1241 | <property name="leftMargin"> | ||
| 1242 | <number>3</number> | ||
| 1243 | </property> | ||
| 1244 | <property name="topMargin"> | ||
| 1245 | <number>3</number> | ||
| 1246 | </property> | ||
| 1247 | <property name="rightMargin"> | ||
| 1248 | <number>3</number> | ||
| 1249 | </property> | ||
| 1250 | <property name="bottomMargin"> | ||
| 1251 | <number>3</number> | ||
| 1252 | </property> | ||
| 1253 | <item> | ||
| 1254 | <widget class="QPushButton" name="buttonSLLeft"> | ||
| 1255 | <property name="minimumSize"> | ||
| 1256 | <size> | ||
| 1257 | <width>68</width> | ||
| 1258 | <height>0</height> | ||
| 1259 | </size> | ||
| 1260 | </property> | ||
| 1261 | <property name="maximumSize"> | ||
| 1262 | <size> | ||
| 1263 | <width>68</width> | ||
| 1264 | <height>16777215</height> | ||
| 1265 | </size> | ||
| 1266 | </property> | ||
| 1267 | <property name="styleSheet"> | ||
| 1268 | <string notr="true">min-width: 68px;</string> | ||
| 1269 | </property> | ||
| 1270 | <property name="text"> | ||
| 1271 | <string>SL</string> | ||
| 1272 | </property> | ||
| 1273 | </widget> | ||
| 1274 | </item> | ||
| 1275 | </layout> | ||
| 1276 | </widget> | ||
| 1277 | </item> | ||
| 1278 | <item alignment="Qt::AlignHCenter"> | ||
| 1279 | <widget class="QGroupBox" name="buttonShoulderButtonsSRLeftGroup"> | ||
| 1280 | <property name="title"> | ||
| 1281 | <string>SR</string> | ||
| 1282 | </property> | ||
| 1283 | <property name="alignment"> | ||
| 1284 | <set>Qt::AlignCenter</set> | ||
| 1285 | </property> | ||
| 1286 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSRLeftVerticalLayout"> | ||
| 1287 | <property name="spacing"> | ||
| 1288 | <number>3</number> | ||
| 1289 | </property> | ||
| 1290 | <property name="leftMargin"> | ||
| 1291 | <number>3</number> | ||
| 1292 | </property> | ||
| 1293 | <property name="topMargin"> | ||
| 1294 | <number>3</number> | ||
| 1295 | </property> | ||
| 1296 | <property name="rightMargin"> | ||
| 1297 | <number>3</number> | ||
| 1298 | </property> | ||
| 1299 | <property name="bottomMargin"> | ||
| 1300 | <number>3</number> | ||
| 1301 | </property> | ||
| 1302 | <item> | ||
| 1303 | <widget class="QPushButton" name="buttonSRLeft"> | ||
| 1304 | <property name="minimumSize"> | ||
| 1305 | <size> | ||
| 1306 | <width>68</width> | ||
| 1307 | <height>0</height> | ||
| 1308 | </size> | ||
| 1309 | </property> | ||
| 1310 | <property name="maximumSize"> | ||
| 1311 | <size> | ||
| 1312 | <width>68</width> | ||
| 1313 | <height>16777215</height> | ||
| 1314 | </size> | ||
| 1315 | </property> | ||
| 1316 | <property name="styleSheet"> | ||
| 1317 | <string notr="true">min-width: 68px;</string> | ||
| 1318 | </property> | ||
| 1319 | <property name="text"> | ||
| 1320 | <string>SR</string> | ||
| 1321 | </property> | ||
| 1322 | </widget> | ||
| 1323 | </item> | ||
| 1324 | </layout> | ||
| 1325 | </widget> | ||
| 1326 | </item> | ||
| 1327 | </layout> | ||
| 1328 | </widget> | ||
| 1329 | </item> | ||
| 1330 | <item> | ||
| 1331 | <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget4" native="true"> | ||
| 1332 | <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget4Layout"> | ||
| 1333 | <property name="spacing"> | ||
| 1334 | <number>0</number> | ||
| 1335 | </property> | ||
| 1336 | <property name="leftMargin"> | ||
| 1337 | <number>0</number> | ||
| 1338 | </property> | ||
| 1339 | <property name="topMargin"> | ||
| 1340 | <number>0</number> | ||
| 1341 | </property> | ||
| 1342 | <property name="rightMargin"> | ||
| 1343 | <number>0</number> | ||
| 1344 | </property> | ||
| 1345 | <property name="bottomMargin"> | ||
| 1346 | <number>0</number> | ||
| 1347 | </property> | ||
| 1348 | <item> | ||
| 1349 | <spacer name="horizontalSpacerShoulderButtons5"> | ||
| 1350 | <property name="orientation"> | ||
| 1351 | <enum>Qt::Horizontal</enum> | ||
| 1352 | </property> | ||
| 1353 | <property name="sizeHint" stdset="0"> | ||
| 1354 | <size> | ||
| 1355 | <width>0</width> | ||
| 1356 | <height>20</height> | ||
| 1357 | </size> | ||
| 1358 | </property> | ||
| 1359 | </spacer> | ||
| 1360 | </item> | ||
| 1361 | </layout> | ||
| 1362 | </widget> | ||
| 1363 | </item> | ||
| 1211 | <item> | 1364 | <item> |
| 1212 | <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true"> | 1365 | <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true"> |
| 1213 | <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout"> | 1366 | <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout"> |
| @@ -1830,125 +1983,125 @@ | |||
| 1830 | </layout> | 1983 | </layout> |
| 1831 | </widget> | 1984 | </widget> |
| 1832 | </item> | 1985 | </item> |
| 1833 | <item> | 1986 | <item> |
| 1834 | <widget class="QWidget" name="buttonShoulderButtonsSLSR" native="true"> | 1987 | <widget class="QWidget" name="buttonShoulderButtonsSLSRRight" native="true"> |
| 1835 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRVerticalLayout"> | 1988 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRRightVerticalLayout"> |
| 1836 | <property name="spacing"> | 1989 | <property name="spacing"> |
| 1837 | <number>0</number> | 1990 | <number>0</number> |
| 1838 | </property> | 1991 | </property> |
| 1839 | <property name="leftMargin"> | 1992 | <property name="leftMargin"> |
| 1840 | <number>0</number> | 1993 | <number>0</number> |
| 1841 | </property> | 1994 | </property> |
| 1842 | <property name="topMargin"> | 1995 | <property name="topMargin"> |
| 1843 | <number>0</number> | 1996 | <number>0</number> |
| 1844 | </property> | 1997 | </property> |
| 1845 | <property name="rightMargin"> | 1998 | <property name="rightMargin"> |
| 1846 | <number>0</number> | 1999 | <number>0</number> |
| 1847 | </property> | 2000 | </property> |
| 1848 | <property name="bottomMargin"> | 2001 | <property name="bottomMargin"> |
| 1849 | <number>0</number> | 2002 | <number>0</number> |
| 1850 | </property> | 2003 | </property> |
| 1851 | <item alignment="Qt::AlignHCenter"> | 2004 | <item alignment="Qt::AlignHCenter"> |
| 1852 | <widget class="QGroupBox" name="buttonShoulderButtonsSLGroup"> | 2005 | <widget class="QGroupBox" name="buttonShoulderButtonsSLRightGroup"> |
| 1853 | <property name="title"> | 2006 | <property name="title"> |
| 1854 | <string>SL</string> | 2007 | <string>SL</string> |
| 1855 | </property> | 2008 | </property> |
| 1856 | <property name="alignment"> | 2009 | <property name="alignment"> |
| 1857 | <set>Qt::AlignCenter</set> | 2010 | <set>Qt::AlignCenter</set> |
| 1858 | </property> | 2011 | </property> |
| 1859 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> | 2012 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSLRightVerticalLayout"> |
| 1860 | <property name="spacing"> | 2013 | <property name="spacing"> |
| 1861 | <number>3</number> | 2014 | <number>3</number> |
| 1862 | </property> | 2015 | </property> |
| 1863 | <property name="leftMargin"> | 2016 | <property name="leftMargin"> |
| 1864 | <number>3</number> | 2017 | <number>3</number> |
| 1865 | </property> | 2018 | </property> |
| 1866 | <property name="topMargin"> | 2019 | <property name="topMargin"> |
| 1867 | <number>3</number> | 2020 | <number>3</number> |
| 1868 | </property> | 2021 | </property> |
| 1869 | <property name="rightMargin"> | 2022 | <property name="rightMargin"> |
| 1870 | <number>3</number> | 2023 | <number>3</number> |
| 1871 | </property> | 2024 | </property> |
| 1872 | <property name="bottomMargin"> | 2025 | <property name="bottomMargin"> |
| 1873 | <number>3</number> | 2026 | <number>3</number> |
| 1874 | </property> | 2027 | </property> |
| 1875 | <item> | 2028 | <item> |
| 1876 | <widget class="QPushButton" name="buttonSL"> | 2029 | <widget class="QPushButton" name="buttonSLRight"> |
| 1877 | <property name="minimumSize"> | 2030 | <property name="minimumSize"> |
| 1878 | <size> | 2031 | <size> |
| 1879 | <width>68</width> | 2032 | <width>68</width> |
| 1880 | <height>0</height> | 2033 | <height>0</height> |
| 1881 | </size> | 2034 | </size> |
| 1882 | </property> | 2035 | </property> |
| 1883 | <property name="maximumSize"> | 2036 | <property name="maximumSize"> |
| 1884 | <size> | 2037 | <size> |
| 1885 | <width>68</width> | 2038 | <width>68</width> |
| 1886 | <height>16777215</height> | 2039 | <height>16777215</height> |
| 1887 | </size> | 2040 | </size> |
| 1888 | </property> | 2041 | </property> |
| 1889 | <property name="styleSheet"> | 2042 | <property name="styleSheet"> |
| 1890 | <string notr="true">min-width: 68px;</string> | 2043 | <string notr="true">min-width: 68px;</string> |
| 1891 | </property> | 2044 | </property> |
| 1892 | <property name="text"> | 2045 | <property name="text"> |
| 1893 | <string>SL</string> | 2046 | <string>SL</string> |
| 1894 | </property> | 2047 | </property> |
| 1895 | </widget> | 2048 | </widget> |
| 1896 | </item> | 2049 | </item> |
| 1897 | </layout> | 2050 | </layout> |
| 1898 | </widget> | 2051 | </widget> |
| 1899 | </item> | 2052 | </item> |
| 1900 | <item alignment="Qt::AlignHCenter"> | 2053 | <item alignment="Qt::AlignHCenter"> |
| 1901 | <widget class="QGroupBox" name="buttonShoulderButtonsSRGroup"> | 2054 | <widget class="QGroupBox" name="buttonShoulderButtonsSRRightGroup"> |
| 1902 | <property name="title"> | 2055 | <property name="title"> |
| 1903 | <string>SR</string> | 2056 | <string>SR</string> |
| 1904 | </property> | 2057 | </property> |
| 1905 | <property name="alignment"> | 2058 | <property name="alignment"> |
| 1906 | <set>Qt::AlignCenter</set> | 2059 | <set>Qt::AlignCenter</set> |
| 1907 | </property> | 2060 | </property> |
| 1908 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> | 2061 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSRRightVerticalLayout"> |
| 1909 | <property name="spacing"> | 2062 | <property name="spacing"> |
| 1910 | <number>3</number> | 2063 | <number>3</number> |
| 1911 | </property> | 2064 | </property> |
| 1912 | <property name="leftMargin"> | 2065 | <property name="leftMargin"> |
| 1913 | <number>3</number> | 2066 | <number>3</number> |
| 1914 | </property> | 2067 | </property> |
| 1915 | <property name="topMargin"> | 2068 | <property name="topMargin"> |
| 1916 | <number>3</number> | 2069 | <number>3</number> |
| 1917 | </property> | 2070 | </property> |
| 1918 | <property name="rightMargin"> | 2071 | <property name="rightMargin"> |
| 1919 | <number>3</number> | 2072 | <number>3</number> |
| 1920 | </property> | 2073 | </property> |
| 1921 | <property name="bottomMargin"> | 2074 | <property name="bottomMargin"> |
| 1922 | <number>3</number> | 2075 | <number>3</number> |
| 1923 | </property> | 2076 | </property> |
| 1924 | <item> | 2077 | <item> |
| 1925 | <widget class="QPushButton" name="buttonSR"> | 2078 | <widget class="QPushButton" name="buttonSRRight"> |
| 1926 | <property name="minimumSize"> | 2079 | <property name="minimumSize"> |
| 1927 | <size> | 2080 | <size> |
| 1928 | <width>68</width> | 2081 | <width>68</width> |
| 1929 | <height>0</height> | 2082 | <height>0</height> |
| 1930 | </size> | 2083 | </size> |
| 1931 | </property> | 2084 | </property> |
| 1932 | <property name="maximumSize"> | 2085 | <property name="maximumSize"> |
| 1933 | <size> | 2086 | <size> |
| 1934 | <width>68</width> | 2087 | <width>68</width> |
| 1935 | <height>16777215</height> | 2088 | <height>16777215</height> |
| 1936 | </size> | 2089 | </size> |
| 1937 | </property> | 2090 | </property> |
| 1938 | <property name="styleSheet"> | 2091 | <property name="styleSheet"> |
| 1939 | <string notr="true">min-width: 68px;</string> | 2092 | <string notr="true">min-width: 68px;</string> |
| 1940 | </property> | 2093 | </property> |
| 1941 | <property name="text"> | 2094 | <property name="text"> |
| 1942 | <string>SR</string> | 2095 | <string>SR</string> |
| 1943 | </property> | 2096 | </property> |
| 1944 | </widget> | 2097 | </widget> |
| 1945 | </item> | 2098 | </item> |
| 2099 | </layout> | ||
| 2100 | </widget> | ||
| 2101 | </item> | ||
| 1946 | </layout> | 2102 | </layout> |
| 1947 | </widget> | 2103 | </widget> |
| 1948 | </item> | 2104 | </item> |
| 1949 | </layout> | ||
| 1950 | </widget> | ||
| 1951 | </item> | ||
| 1952 | </layout> | 2105 | </layout> |
| 1953 | </item> | 2106 | </item> |
| 1954 | <item> | 2107 | <item> |
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index a188eef92..550cff9a0 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp | |||
| @@ -297,8 +297,8 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) | |||
| 297 | 297 | ||
| 298 | // Sideview SL and SR buttons | 298 | // Sideview SL and SR buttons |
| 299 | button_color = colors.slider_button; | 299 | button_color = colors.slider_button; |
| 300 | DrawRoundButton(p, center + QPoint(59, 52), button_values[SR], 5, 12, Direction::Left); | 300 | DrawRoundButton(p, center + QPoint(59, 52), button_values[SRLeft], 5, 12, Direction::Left); |
| 301 | DrawRoundButton(p, center + QPoint(59, -69), button_values[SL], 5, 12, Direction::Left); | 301 | DrawRoundButton(p, center + QPoint(59, -69), button_values[SLLeft], 5, 12, Direction::Left); |
| 302 | 302 | ||
| 303 | DrawLeftBody(p, center); | 303 | DrawLeftBody(p, center); |
| 304 | 304 | ||
| @@ -353,8 +353,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) | |||
| 353 | // SR and SL buttons | 353 | // SR and SL buttons |
| 354 | p.setPen(colors.outline); | 354 | p.setPen(colors.outline); |
| 355 | button_color = colors.slider_button; | 355 | button_color = colors.slider_button; |
| 356 | DrawRoundButton(p, center + QPoint(155, 52), button_values[SR], 5.2f, 12, Direction::None, 4); | 356 | DrawRoundButton(p, center + QPoint(155, 52), button_values[SRLeft], 5.2f, 12, Direction::None, |
| 357 | DrawRoundButton(p, center + QPoint(155, -69), button_values[SL], 5.2f, 12, Direction::None, 4); | 357 | 4); |
| 358 | DrawRoundButton(p, center + QPoint(155, -69), button_values[SLLeft], 5.2f, 12, Direction::None, | ||
| 359 | 4); | ||
| 358 | 360 | ||
| 359 | // SR and SL text | 361 | // SR and SL text |
| 360 | p.setPen(colors.transparent); | 362 | p.setPen(colors.transparent); |
| @@ -428,8 +430,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center | |||
| 428 | 430 | ||
| 429 | // Sideview SL and SR buttons | 431 | // Sideview SL and SR buttons |
| 430 | button_color = colors.slider_button; | 432 | button_color = colors.slider_button; |
| 431 | DrawRoundButton(p, center + QPoint(-59, 52), button_values[SL], 5, 11, Direction::Right); | 433 | DrawRoundButton(p, center + QPoint(-59, 52), button_values[SLRight], 5, 11, |
| 432 | DrawRoundButton(p, center + QPoint(-59, -69), button_values[SR], 5, 11, Direction::Right); | 434 | Direction::Right); |
| 435 | DrawRoundButton(p, center + QPoint(-59, -69), button_values[SRRight], 5, 11, | ||
| 436 | Direction::Right); | ||
| 433 | 437 | ||
| 434 | DrawRightBody(p, center); | 438 | DrawRightBody(p, center); |
| 435 | 439 | ||
| @@ -484,8 +488,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center | |||
| 484 | // SR and SL buttons | 488 | // SR and SL buttons |
| 485 | p.setPen(colors.outline); | 489 | p.setPen(colors.outline); |
| 486 | button_color = colors.slider_button; | 490 | button_color = colors.slider_button; |
| 487 | DrawRoundButton(p, center + QPoint(-155, 52), button_values[SL], 5, 12, Direction::None, 4.0f); | 491 | DrawRoundButton(p, center + QPoint(-155, 52), button_values[SLRight], 5, 12, Direction::None, |
| 488 | DrawRoundButton(p, center + QPoint(-155, -69), button_values[SR], 5, 12, Direction::None, 4.0f); | 492 | 4.0f); |
| 493 | DrawRoundButton(p, center + QPoint(-155, -69), button_values[SRRight], 5, 12, Direction::None, | ||
| 494 | 4.0f); | ||
| 489 | 495 | ||
| 490 | // SR and SL text | 496 | // SR and SL text |
| 491 | p.setPen(colors.transparent); | 497 | p.setPen(colors.transparent); |
| @@ -557,6 +563,19 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) | |||
| 557 | DrawRoundButton(p, center + QPoint(-154, -72), button_values[Minus], 7, 4, Direction::Up, | 563 | DrawRoundButton(p, center + QPoint(-154, -72), button_values[Minus], 7, 4, Direction::Up, |
| 558 | 1); | 564 | 1); |
| 559 | 565 | ||
| 566 | // Left SR and SL sideview buttons | ||
| 567 | button_color = colors.slider_button; | ||
| 568 | DrawRoundButton(p, center + QPoint(-20, -62), button_values[SLLeft], 4, 11, | ||
| 569 | Direction::Left); | ||
| 570 | DrawRoundButton(p, center + QPoint(-20, 47), button_values[SRLeft], 4, 11, Direction::Left); | ||
| 571 | |||
| 572 | // Right SR and SL sideview buttons | ||
| 573 | button_color = colors.slider_button; | ||
| 574 | DrawRoundButton(p, center + QPoint(20, 47), button_values[SLRight], 4, 11, | ||
| 575 | Direction::Right); | ||
| 576 | DrawRoundButton(p, center + QPoint(20, -62), button_values[SRRight], 4, 11, | ||
| 577 | Direction::Right); | ||
| 578 | |||
| 560 | DrawDualBody(p, center); | 579 | DrawDualBody(p, center); |
| 561 | 580 | ||
| 562 | // Right trigger top view | 581 | // Right trigger top view |
| @@ -1792,16 +1811,6 @@ void PlayerControlPreview::DrawDualBody(QPainter& p, const QPointF center) { | |||
| 1792 | p.setBrush(colors.right); | 1811 | p.setBrush(colors.right); |
| 1793 | DrawPolygon(p, qright_joycon_topview); | 1812 | DrawPolygon(p, qright_joycon_topview); |
| 1794 | 1813 | ||
| 1795 | // Right SR and SL sideview buttons | ||
| 1796 | p.setPen(colors.outline); | ||
| 1797 | p.setBrush(colors.slider_button); | ||
| 1798 | DrawRoundRectangle(p, center + QPoint(19, 47), 7, 22, 1); | ||
| 1799 | DrawRoundRectangle(p, center + QPoint(19, -62), 7, 22, 1); | ||
| 1800 | |||
| 1801 | // Left SR and SL sideview buttons | ||
| 1802 | DrawRoundRectangle(p, center + QPoint(-19, 47), 7, 22, 1); | ||
| 1803 | DrawRoundRectangle(p, center + QPoint(-19, -62), 7, 22, 1); | ||
| 1804 | |||
| 1805 | // Right Sideview body | 1814 | // Right Sideview body |
| 1806 | p.setBrush(colors.slider); | 1815 | p.setBrush(colors.slider); |
| 1807 | DrawPolygon(p, qright_joycon_slider); | 1816 | DrawPolygon(p, qright_joycon_slider); |
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index b91d6ad4a..b274a3321 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp | |||
| @@ -25,8 +25,8 @@ | |||
| 25 | #include "core/file_sys/patch_manager.h" | 25 | #include "core/file_sys/patch_manager.h" |
| 26 | #include "core/file_sys/xts_archive.h" | 26 | #include "core/file_sys/xts_archive.h" |
| 27 | #include "core/loader/loader.h" | 27 | #include "core/loader/loader.h" |
| 28 | #include "frontend_common/config.h" | ||
| 28 | #include "ui_configure_per_game.h" | 29 | #include "ui_configure_per_game.h" |
| 29 | #include "yuzu/configuration/config.h" | ||
| 30 | #include "yuzu/configuration/configuration_shared.h" | 30 | #include "yuzu/configuration/configuration_shared.h" |
| 31 | #include "yuzu/configuration/configure_audio.h" | 31 | #include "yuzu/configuration/configure_audio.h" |
| 32 | #include "yuzu/configuration/configure_cpu.h" | 32 | #include "yuzu/configuration/configure_cpu.h" |
| @@ -50,8 +50,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st | |||
| 50 | const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); | 50 | const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); |
| 51 | const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) | 51 | const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) |
| 52 | : fmt::format("{:016X}", title_id); | 52 | : fmt::format("{:016X}", title_id); |
| 53 | game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); | 53 | game_config = std::make_unique<QtConfig>(config_file_name, Config::ConfigType::PerGameConfig); |
| 54 | |||
| 55 | addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); | 54 | addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); |
| 56 | audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this); | 55 | audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this); |
| 57 | cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this); | 56 | cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this); |
| @@ -108,7 +107,7 @@ void ConfigurePerGame::ApplyConfiguration() { | |||
| 108 | system.ApplySettings(); | 107 | system.ApplySettings(); |
| 109 | Settings::LogSettings(); | 108 | Settings::LogSettings(); |
| 110 | 109 | ||
| 111 | game_config->Save(); | 110 | game_config->SaveAllValues(); |
| 112 | } | 111 | } |
| 113 | 112 | ||
| 114 | void ConfigurePerGame::changeEvent(QEvent* event) { | 113 | void ConfigurePerGame::changeEvent(QEvent* event) { |
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index 1a727f32c..c8ee46c04 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| @@ -12,9 +12,10 @@ | |||
| 12 | 12 | ||
| 13 | #include "configuration/shared_widget.h" | 13 | #include "configuration/shared_widget.h" |
| 14 | #include "core/file_sys/vfs_types.h" | 14 | #include "core/file_sys/vfs_types.h" |
| 15 | #include "frontend_common/config.h" | ||
| 15 | #include "vk_device_info.h" | 16 | #include "vk_device_info.h" |
| 16 | #include "yuzu/configuration/config.h" | ||
| 17 | #include "yuzu/configuration/configuration_shared.h" | 17 | #include "yuzu/configuration/configuration_shared.h" |
| 18 | #include "yuzu/configuration/qt_config.h" | ||
| 18 | #include "yuzu/configuration/shared_translation.h" | 19 | #include "yuzu/configuration/shared_translation.h" |
| 19 | 20 | ||
| 20 | namespace Core { | 21 | namespace Core { |
| @@ -72,7 +73,7 @@ private: | |||
| 72 | 73 | ||
| 73 | QGraphicsScene* scene; | 74 | QGraphicsScene* scene; |
| 74 | 75 | ||
| 75 | std::unique_ptr<Config> game_config; | 76 | std::unique_ptr<QtConfig> game_config; |
| 76 | 77 | ||
| 77 | Core::System& system; | 78 | Core::System& system; |
| 78 | std::unique_ptr<ConfigurationShared::Builder> builder; | 79 | std::unique_ptr<ConfigurationShared::Builder> builder; |
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp index 674a75a62..140a7fe5d 100644 --- a/src/yuzu/configuration/configure_per_game_addons.cpp +++ b/src/yuzu/configuration/configure_per_game_addons.cpp | |||
| @@ -19,7 +19,6 @@ | |||
| 19 | #include "core/file_sys/xts_archive.h" | 19 | #include "core/file_sys/xts_archive.h" |
| 20 | #include "core/loader/loader.h" | 20 | #include "core/loader/loader.h" |
| 21 | #include "ui_configure_per_game_addons.h" | 21 | #include "ui_configure_per_game_addons.h" |
| 22 | #include "yuzu/configuration/config.h" | ||
| 23 | #include "yuzu/configuration/configure_input.h" | 22 | #include "yuzu/configuration/configure_input.h" |
| 24 | #include "yuzu/configuration/configure_per_game_addons.h" | 23 | #include "yuzu/configuration/configure_per_game_addons.h" |
| 25 | #include "yuzu/uisettings.h" | 24 | #include "yuzu/uisettings.h" |
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index a47089988..6d2219bf5 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp | |||
| @@ -306,10 +306,10 @@ void ConfigureProfileManager::SetUserImage() { | |||
| 306 | return; | 306 | return; |
| 307 | } | 307 | } |
| 308 | 308 | ||
| 309 | // Some games crash when the profile image is too big. Resize any image bigger than 256x256 | 309 | // Profile image must be 256x256 |
| 310 | QImage image(image_path); | 310 | QImage image(image_path); |
| 311 | if (image.width() > 256 || image.height() > 256) { | 311 | if (image.width() != 256 || image.height() != 256) { |
| 312 | image = image.scaled(256, 256, Qt::KeepAspectRatio); | 312 | image = image.scaled(256, 256, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); |
| 313 | if (!image.save(image_path)) { | 313 | if (!image.save(image_path)) { |
| 314 | QMessageBox::warning(this, tr("Error resizing user image"), | 314 | QMessageBox::warning(this, tr("Error resizing user image"), |
| 315 | tr("Unable to resize image")); | 315 | tr("Unable to resize image")); |
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp index f83705544..9572ff43c 100644 --- a/src/yuzu/configuration/configure_ringcon.cpp +++ b/src/yuzu/configuration/configure_ringcon.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <QTimer> | 8 | #include <QTimer> |
| 9 | #include <fmt/format.h> | 9 | #include <fmt/format.h> |
| 10 | 10 | ||
| 11 | #include "configuration/qt_config.h" | ||
| 11 | #include "core/hid/emulated_controller.h" | 12 | #include "core/hid/emulated_controller.h" |
| 12 | #include "core/hid/hid_core.h" | 13 | #include "core/hid/hid_core.h" |
| 13 | #include "input_common/drivers/keyboard.h" | 14 | #include "input_common/drivers/keyboard.h" |
| @@ -15,7 +16,6 @@ | |||
| 15 | #include "input_common/main.h" | 16 | #include "input_common/main.h" |
| 16 | #include "ui_configure_ringcon.h" | 17 | #include "ui_configure_ringcon.h" |
| 17 | #include "yuzu/bootmanager.h" | 18 | #include "yuzu/bootmanager.h" |
| 18 | #include "yuzu/configuration/config.h" | ||
| 19 | #include "yuzu/configuration/configure_ringcon.h" | 19 | #include "yuzu/configuration/configure_ringcon.h" |
| 20 | 20 | ||
| 21 | const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM> | 21 | const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM> |
| @@ -270,7 +270,7 @@ void ConfigureRingController::LoadConfiguration() { | |||
| 270 | 270 | ||
| 271 | void ConfigureRingController::RestoreDefaults() { | 271 | void ConfigureRingController::RestoreDefaults() { |
| 272 | const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( | 272 | const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( |
| 273 | 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f); | 273 | 0, 0, QtConfig::default_ringcon_analogs[0], QtConfig::default_ringcon_analogs[1], 0, 0.05f); |
| 274 | emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string)); | 274 | emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string)); |
| 275 | UpdateUI(); | 275 | UpdateUI(); |
| 276 | } | 276 | } |
diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h index b23c27906..6fd95e2b8 100644 --- a/src/yuzu/configuration/configure_ringcon.h +++ b/src/yuzu/configuration/configure_ringcon.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 0c8e5c8b4..7cbf43775 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | #include "core/core.h" | 16 | #include "core/core.h" |
| 17 | #include "core/hle/service/time/time_manager.h" | 17 | #include "core/hle/service/time/time_manager.h" |
| 18 | #include "ui_configure_system.h" | 18 | #include "ui_configure_system.h" |
| 19 | #include "yuzu/configuration/config.h" | ||
| 20 | #include "yuzu/configuration/configuration_shared.h" | 19 | #include "yuzu/configuration/configuration_shared.h" |
| 21 | #include "yuzu/configuration/configure_system.h" | 20 | #include "yuzu/configuration/configure_system.h" |
| 22 | #include "yuzu/configuration/shared_widget.h" | 21 | #include "yuzu/configuration/shared_widget.h" |
diff --git a/src/yuzu/configuration/configure_tas.h b/src/yuzu/configuration/configure_tas.h index 4a6b0ba4e..a91891906 100644 --- a/src/yuzu/configuration/configure_tas.h +++ b/src/yuzu/configuration/configure_tas.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp index 5a03e48df..94df6d9d3 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.cpp +++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp | |||
| @@ -2,8 +2,8 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <memory> | 4 | #include <memory> |
| 5 | #include "common/settings.h" | ||
| 5 | #include "ui_configure_touchscreen_advanced.h" | 6 | #include "ui_configure_touchscreen_advanced.h" |
| 6 | #include "yuzu/configuration/config.h" | ||
| 7 | #include "yuzu/configuration/configure_touchscreen_advanced.h" | 7 | #include "yuzu/configuration/configure_touchscreen_advanced.h" |
| 8 | 8 | ||
| 9 | ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent) | 9 | ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent) |
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h index 034dc0d46..b6fdffdc8 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.h +++ b/src/yuzu/configuration/configure_touchscreen_advanced.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 82f3b6e78..dd43f0a0e 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp | |||
| @@ -164,7 +164,7 @@ ConfigureUi::~ConfigureUi() = default; | |||
| 164 | 164 | ||
| 165 | void ConfigureUi::ApplyConfiguration() { | 165 | void ConfigureUi::ApplyConfiguration() { |
| 166 | UISettings::values.theme = | 166 | UISettings::values.theme = |
| 167 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); | 167 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString().toStdString(); |
| 168 | UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); | 168 | UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); |
| 169 | UISettings::values.show_compat = ui->show_compat->isChecked(); | 169 | UISettings::values.show_compat = ui->show_compat->isChecked(); |
| 170 | UISettings::values.show_size = ui->show_size->isChecked(); | 170 | UISettings::values.show_size = ui->show_size->isChecked(); |
| @@ -191,9 +191,10 @@ void ConfigureUi::RequestGameListUpdate() { | |||
| 191 | } | 191 | } |
| 192 | 192 | ||
| 193 | void ConfigureUi::SetConfiguration() { | 193 | void ConfigureUi::SetConfiguration() { |
| 194 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | 194 | ui->theme_combobox->setCurrentIndex( |
| 195 | ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme))); | ||
| 195 | ui->language_combobox->setCurrentIndex( | 196 | ui->language_combobox->setCurrentIndex( |
| 196 | ui->language_combobox->findData(UISettings::values.language)); | 197 | ui->language_combobox->findData(QString::fromStdString(UISettings::values.language))); |
| 197 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); | 198 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); |
| 198 | ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); | 199 | ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); |
| 199 | ui->show_size->setChecked(UISettings::values.show_size.GetValue()); | 200 | ui->show_size->setChecked(UISettings::values.show_size.GetValue()); |
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp index 41ef4250a..716efbccd 100644 --- a/src/yuzu/configuration/input_profiles.cpp +++ b/src/yuzu/configuration/input_profiles.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/fs/fs.h" | 6 | #include "common/fs/fs.h" |
| 7 | #include "common/fs/path_util.h" | 7 | #include "common/fs/path_util.h" |
| 8 | #include "yuzu/configuration/config.h" | 8 | #include "frontend_common/config.h" |
| 9 | #include "yuzu/configuration/input_profiles.h" | 9 | #include "yuzu/configuration/input_profiles.h" |
| 10 | 10 | ||
| 11 | namespace FS = Common::FS; | 11 | namespace FS = Common::FS; |
| @@ -44,7 +44,7 @@ InputProfiles::InputProfiles() { | |||
| 44 | if (IsINI(filename) && IsProfileNameValid(name_without_ext)) { | 44 | if (IsINI(filename) && IsProfileNameValid(name_without_ext)) { |
| 45 | map_profiles.insert_or_assign( | 45 | map_profiles.insert_or_assign( |
| 46 | name_without_ext, | 46 | name_without_ext, |
| 47 | std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile)); | 47 | std::make_unique<QtConfig>(name_without_ext, Config::ConfigType::InputProfile)); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | return true; | 50 | return true; |
| @@ -85,7 +85,7 @@ bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t p | |||
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | map_profiles.insert_or_assign( | 87 | map_profiles.insert_or_assign( |
| 88 | profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile)); | 88 | profile_name, std::make_unique<QtConfig>(profile_name, Config::ConfigType::InputProfile)); |
| 89 | 89 | ||
| 90 | return SaveProfile(profile_name, player_index); | 90 | return SaveProfile(profile_name, player_index); |
| 91 | } | 91 | } |
| @@ -113,7 +113,7 @@ bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t pla | |||
| 113 | return false; | 113 | return false; |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | map_profiles[profile_name]->ReadControlPlayerValue(player_index); | 116 | map_profiles[profile_name]->ReadQtControlPlayerValues(player_index); |
| 117 | return true; | 117 | return true; |
| 118 | } | 118 | } |
| 119 | 119 | ||
| @@ -122,7 +122,7 @@ bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t pla | |||
| 122 | return false; | 122 | return false; |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | map_profiles[profile_name]->SaveControlPlayerValue(player_index); | 125 | map_profiles[profile_name]->SaveQtControlPlayerValues(player_index); |
| 126 | return true; | 126 | return true; |
| 127 | } | 127 | } |
| 128 | 128 | ||
diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h index 2bf3e4250..023ec74a6 100644 --- a/src/yuzu/configuration/input_profiles.h +++ b/src/yuzu/configuration/input_profiles.h | |||
| @@ -6,6 +6,8 @@ | |||
| 6 | #include <string> | 6 | #include <string> |
| 7 | #include <unordered_map> | 7 | #include <unordered_map> |
| 8 | 8 | ||
| 9 | #include "configuration/qt_config.h" | ||
| 10 | |||
| 9 | namespace Core { | 11 | namespace Core { |
| 10 | class System; | 12 | class System; |
| 11 | } | 13 | } |
| @@ -30,5 +32,5 @@ public: | |||
| 30 | private: | 32 | private: |
| 31 | bool ProfileExistsInMap(const std::string& profile_name) const; | 33 | bool ProfileExistsInMap(const std::string& profile_name) const; |
| 32 | 34 | ||
| 33 | std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles; | 35 | std::unordered_map<std::string, std::unique_ptr<QtConfig>> map_profiles; |
| 34 | }; | 36 | }; |
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp new file mode 100644 index 000000000..5a8e69aa9 --- /dev/null +++ b/src/yuzu/configuration/qt_config.cpp | |||
| @@ -0,0 +1,549 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "input_common/main.h" | ||
| 5 | #include "qt_config.h" | ||
| 6 | #include "uisettings.h" | ||
| 7 | |||
| 8 | const std::array<int, Settings::NativeButton::NumButtons> QtConfig::default_buttons = { | ||
| 9 | Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F, | ||
| 10 | Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T, | ||
| 11 | Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, | ||
| 12 | Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0, | ||
| 13 | Qt::Key_Q, Qt::Key_E, | ||
| 14 | }; | ||
| 15 | |||
| 16 | const std::array<int, Settings::NativeMotion::NumMotions> QtConfig::default_motions = { | ||
| 17 | Qt::Key_7, | ||
| 18 | Qt::Key_8, | ||
| 19 | }; | ||
| 20 | |||
| 21 | const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{ | ||
| 22 | { | ||
| 23 | Qt::Key_W, | ||
| 24 | Qt::Key_S, | ||
| 25 | Qt::Key_A, | ||
| 26 | Qt::Key_D, | ||
| 27 | }, | ||
| 28 | { | ||
| 29 | Qt::Key_I, | ||
| 30 | Qt::Key_K, | ||
| 31 | Qt::Key_J, | ||
| 32 | Qt::Key_L, | ||
| 33 | }, | ||
| 34 | }}; | ||
| 35 | |||
| 36 | const std::array<int, 2> QtConfig::default_stick_mod = { | ||
| 37 | Qt::Key_Shift, | ||
| 38 | 0, | ||
| 39 | }; | ||
| 40 | |||
| 41 | const std::array<int, 2> QtConfig::default_ringcon_analogs{{ | ||
| 42 | Qt::Key_A, | ||
| 43 | Qt::Key_D, | ||
| 44 | }}; | ||
| 45 | |||
| 46 | QtConfig::QtConfig(const std::string& config_name, const ConfigType config_type) | ||
| 47 | : Config(config_type) { | ||
| 48 | Initialize(config_name); | ||
| 49 | if (config_type != ConfigType::InputProfile) { | ||
| 50 | ReadQtValues(); | ||
| 51 | SaveQtValues(); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | QtConfig::~QtConfig() { | ||
| 56 | if (global) { | ||
| 57 | QtConfig::SaveAllValues(); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | void QtConfig::ReloadAllValues() { | ||
| 62 | Reload(); | ||
| 63 | ReadQtValues(); | ||
| 64 | SaveQtValues(); | ||
| 65 | } | ||
| 66 | |||
| 67 | void QtConfig::SaveAllValues() { | ||
| 68 | Save(); | ||
| 69 | SaveQtValues(); | ||
| 70 | } | ||
| 71 | |||
| 72 | void QtConfig::ReadQtValues() { | ||
| 73 | if (global) { | ||
| 74 | ReadUIValues(); | ||
| 75 | } | ||
| 76 | ReadQtControlValues(); | ||
| 77 | } | ||
| 78 | |||
| 79 | void QtConfig::ReadQtPlayerValues(const std::size_t player_index) { | ||
| 80 | std::string player_prefix; | ||
| 81 | if (type != ConfigType::InputProfile) { | ||
| 82 | player_prefix.append("player_").append(ToString(player_index)).append("_"); | ||
| 83 | } | ||
| 84 | |||
| 85 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 86 | if (IsCustomConfig()) { | ||
| 87 | const auto profile_name = | ||
| 88 | ReadStringSetting(std::string(player_prefix).append("profile_name")); | ||
| 89 | if (profile_name.empty()) { | ||
| 90 | // Use the global input config | ||
| 91 | player = Settings::values.players.GetValue(true)[player_index]; | ||
| 92 | return; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 97 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 98 | auto& player_buttons = player.buttons[i]; | ||
| 99 | |||
| 100 | player_buttons = ReadStringSetting( | ||
| 101 | std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param); | ||
| 102 | if (player_buttons.empty()) { | ||
| 103 | player_buttons = default_param; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 108 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 109 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 110 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 111 | auto& player_analogs = player.analogs[i]; | ||
| 112 | |||
| 113 | player_analogs = ReadStringSetting( | ||
| 114 | std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param); | ||
| 115 | if (player_analogs.empty()) { | ||
| 116 | player_analogs = default_param; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | ||
| 121 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); | ||
| 122 | auto& player_motions = player.motions[i]; | ||
| 123 | |||
| 124 | player_motions = ReadStringSetting( | ||
| 125 | std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param); | ||
| 126 | if (player_motions.empty()) { | ||
| 127 | player_motions = default_param; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | void QtConfig::ReadHidbusValues() { | ||
| 133 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 134 | 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); | ||
| 135 | auto& ringcon_analogs = Settings::values.ringcon_analogs; | ||
| 136 | |||
| 137 | ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param); | ||
| 138 | if (ringcon_analogs.empty()) { | ||
| 139 | ringcon_analogs = default_param; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | void QtConfig::ReadDebugControlValues() { | ||
| 144 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 145 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 146 | auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; | ||
| 147 | |||
| 148 | debug_pad_buttons = ReadStringSetting( | ||
| 149 | std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param); | ||
| 150 | if (debug_pad_buttons.empty()) { | ||
| 151 | debug_pad_buttons = default_param; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 156 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 157 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 158 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 159 | auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; | ||
| 160 | |||
| 161 | debug_pad_analogs = ReadStringSetting( | ||
| 162 | std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param); | ||
| 163 | if (debug_pad_analogs.empty()) { | ||
| 164 | debug_pad_analogs = default_param; | ||
| 165 | } | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | void QtConfig::ReadQtControlValues() { | ||
| 170 | BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); | ||
| 171 | |||
| 172 | Settings::values.players.SetGlobal(!IsCustomConfig()); | ||
| 173 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 174 | ReadQtPlayerValues(p); | ||
| 175 | } | ||
| 176 | if (IsCustomConfig()) { | ||
| 177 | EndGroup(); | ||
| 178 | return; | ||
| 179 | } | ||
| 180 | ReadDebugControlValues(); | ||
| 181 | ReadHidbusValues(); | ||
| 182 | |||
| 183 | EndGroup(); | ||
| 184 | } | ||
| 185 | |||
| 186 | void QtConfig::ReadPathValues() { | ||
| 187 | BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); | ||
| 188 | |||
| 189 | UISettings::values.roms_path = ReadStringSetting(std::string("romsPath")); | ||
| 190 | UISettings::values.symbols_path = ReadStringSetting(std::string("symbolsPath")); | ||
| 191 | UISettings::values.game_dir_deprecated = | ||
| 192 | ReadStringSetting(std::string("gameListRootDir"), std::string(".")); | ||
| 193 | UISettings::values.game_dir_deprecated_deepscan = | ||
| 194 | ReadBooleanSetting(std::string("gameListDeepScan"), std::make_optional(false)); | ||
| 195 | |||
| 196 | const int gamedirs_size = BeginArray(std::string("gamedirs")); | ||
| 197 | for (int i = 0; i < gamedirs_size; ++i) { | ||
| 198 | SetArrayIndex(i); | ||
| 199 | UISettings::GameDir game_dir; | ||
| 200 | game_dir.path = ReadStringSetting(std::string("path")); | ||
| 201 | game_dir.deep_scan = | ||
| 202 | ReadBooleanSetting(std::string("deep_scan"), std::make_optional(false)); | ||
| 203 | game_dir.expanded = ReadBooleanSetting(std::string("expanded"), std::make_optional(true)); | ||
| 204 | UISettings::values.game_dirs.append(game_dir); | ||
| 205 | } | ||
| 206 | EndArray(); | ||
| 207 | |||
| 208 | // Create NAND and SD card directories if empty, these are not removable through the UI, | ||
| 209 | // also carries over old game list settings if present | ||
| 210 | if (UISettings::values.game_dirs.empty()) { | ||
| 211 | UISettings::GameDir game_dir; | ||
| 212 | game_dir.path = std::string("SDMC"); | ||
| 213 | game_dir.expanded = true; | ||
| 214 | UISettings::values.game_dirs.append(game_dir); | ||
| 215 | game_dir.path = std::string("UserNAND"); | ||
| 216 | UISettings::values.game_dirs.append(game_dir); | ||
| 217 | game_dir.path = std::string("SysNAND"); | ||
| 218 | UISettings::values.game_dirs.append(game_dir); | ||
| 219 | if (UISettings::values.game_dir_deprecated != std::string(".")) { | ||
| 220 | game_dir.path = UISettings::values.game_dir_deprecated; | ||
| 221 | game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan; | ||
| 222 | UISettings::values.game_dirs.append(game_dir); | ||
| 223 | } | ||
| 224 | } | ||
| 225 | UISettings::values.recent_files = | ||
| 226 | QString::fromStdString(ReadStringSetting(std::string("recentFiles"))) | ||
| 227 | .split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive); | ||
| 228 | UISettings::values.language = ReadStringSetting(std::string("language"), std::string("")); | ||
| 229 | |||
| 230 | EndGroup(); | ||
| 231 | } | ||
| 232 | |||
| 233 | void QtConfig::ReadShortcutValues() { | ||
| 234 | BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts)); | ||
| 235 | |||
| 236 | for (const auto& [name, group, shortcut] : UISettings::default_hotkeys) { | ||
| 237 | BeginGroup(group); | ||
| 238 | BeginGroup(name); | ||
| 239 | |||
| 240 | // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1 | ||
| 241 | // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open | ||
| 242 | // a file dialog in windowed mode | ||
| 243 | UISettings::values.shortcuts.push_back( | ||
| 244 | {name, | ||
| 245 | group, | ||
| 246 | {ReadStringSetting(std::string("KeySeq"), shortcut.keyseq), | ||
| 247 | ReadStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq), | ||
| 248 | shortcut.context, | ||
| 249 | ReadBooleanSetting(std::string("Repeat"), std::optional(shortcut.repeat))}}); | ||
| 250 | |||
| 251 | EndGroup(); // name | ||
| 252 | EndGroup(); // group | ||
| 253 | } | ||
| 254 | |||
| 255 | EndGroup(); | ||
| 256 | } | ||
| 257 | |||
| 258 | void QtConfig::ReadUIValues() { | ||
| 259 | BeginGroup(Settings::TranslateCategory(Settings::Category::Ui)); | ||
| 260 | |||
| 261 | UISettings::values.theme = ReadStringSetting( | ||
| 262 | std::string("theme"), | ||
| 263 | std::string(UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)); | ||
| 264 | |||
| 265 | ReadUIGamelistValues(); | ||
| 266 | ReadUILayoutValues(); | ||
| 267 | ReadPathValues(); | ||
| 268 | ReadScreenshotValues(); | ||
| 269 | ReadShortcutValues(); | ||
| 270 | ReadMultiplayerValues(); | ||
| 271 | |||
| 272 | ReadCategory(Settings::Category::Ui); | ||
| 273 | ReadCategory(Settings::Category::UiGeneral); | ||
| 274 | |||
| 275 | EndGroup(); | ||
| 276 | } | ||
| 277 | |||
| 278 | void QtConfig::ReadUIGamelistValues() { | ||
| 279 | BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); | ||
| 280 | |||
| 281 | ReadCategory(Settings::Category::UiGameList); | ||
| 282 | |||
| 283 | const int favorites_size = BeginArray("favorites"); | ||
| 284 | for (int i = 0; i < favorites_size; i++) { | ||
| 285 | SetArrayIndex(i); | ||
| 286 | UISettings::values.favorited_ids.append( | ||
| 287 | ReadUnsignedIntegerSetting(std::string("program_id"))); | ||
| 288 | } | ||
| 289 | EndArray(); | ||
| 290 | |||
| 291 | EndGroup(); | ||
| 292 | } | ||
| 293 | |||
| 294 | void QtConfig::ReadUILayoutValues() { | ||
| 295 | BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); | ||
| 296 | |||
| 297 | ReadCategory(Settings::Category::UiLayout); | ||
| 298 | |||
| 299 | EndGroup(); | ||
| 300 | } | ||
| 301 | |||
| 302 | void QtConfig::ReadMultiplayerValues() { | ||
| 303 | BeginGroup(Settings::TranslateCategory(Settings::Category::Multiplayer)); | ||
| 304 | |||
| 305 | ReadCategory(Settings::Category::Multiplayer); | ||
| 306 | |||
| 307 | // Read ban list back | ||
| 308 | int size = BeginArray(std::string("username_ban_list")); | ||
| 309 | UISettings::values.multiplayer_ban_list.first.resize(size); | ||
| 310 | for (int i = 0; i < size; ++i) { | ||
| 311 | SetArrayIndex(i); | ||
| 312 | UISettings::values.multiplayer_ban_list.first[i] = | ||
| 313 | ReadStringSetting(std::string("username"), std::string("")); | ||
| 314 | } | ||
| 315 | EndArray(); | ||
| 316 | |||
| 317 | size = BeginArray(std::string("ip_ban_list")); | ||
| 318 | UISettings::values.multiplayer_ban_list.second.resize(size); | ||
| 319 | for (int i = 0; i < size; ++i) { | ||
| 320 | UISettings::values.multiplayer_ban_list.second[i] = | ||
| 321 | ReadStringSetting("username", std::string("")); | ||
| 322 | } | ||
| 323 | EndArray(); | ||
| 324 | |||
| 325 | EndGroup(); | ||
| 326 | } | ||
| 327 | |||
| 328 | void QtConfig::SaveQtValues() { | ||
| 329 | if (global) { | ||
| 330 | SaveUIValues(); | ||
| 331 | } | ||
| 332 | SaveQtControlValues(); | ||
| 333 | |||
| 334 | WriteToIni(); | ||
| 335 | } | ||
| 336 | |||
| 337 | void QtConfig::SaveQtPlayerValues(const std::size_t player_index) { | ||
| 338 | std::string player_prefix; | ||
| 339 | if (type != ConfigType::InputProfile) { | ||
| 340 | player_prefix = std::string("player_").append(ToString(player_index)).append("_"); | ||
| 341 | } | ||
| 342 | |||
| 343 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 344 | if (IsCustomConfig() && player.profile_name.empty()) { | ||
| 345 | // No custom profile selected | ||
| 346 | return; | ||
| 347 | } | ||
| 348 | |||
| 349 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 350 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 351 | WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), | ||
| 352 | player.buttons[i], std::make_optional(default_param)); | ||
| 353 | } | ||
| 354 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 355 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 356 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 357 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 358 | WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), | ||
| 359 | player.analogs[i], std::make_optional(default_param)); | ||
| 360 | } | ||
| 361 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | ||
| 362 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); | ||
| 363 | WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), | ||
| 364 | player.motions[i], std::make_optional(default_param)); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | void QtConfig::SaveDebugControlValues() { | ||
| 369 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 370 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 371 | WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), | ||
| 372 | Settings::values.debug_pad_buttons[i], std::make_optional(default_param)); | ||
| 373 | } | ||
| 374 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 375 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 376 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 377 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 378 | WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), | ||
| 379 | Settings::values.debug_pad_analogs[i], std::make_optional(default_param)); | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | void QtConfig::SaveHidbusValues() { | ||
| 384 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 385 | 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); | ||
| 386 | WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs, | ||
| 387 | std::make_optional(default_param)); | ||
| 388 | } | ||
| 389 | |||
| 390 | void QtConfig::SaveQtControlValues() { | ||
| 391 | BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); | ||
| 392 | |||
| 393 | Settings::values.players.SetGlobal(!IsCustomConfig()); | ||
| 394 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 395 | SaveQtPlayerValues(p); | ||
| 396 | } | ||
| 397 | if (IsCustomConfig()) { | ||
| 398 | EndGroup(); | ||
| 399 | return; | ||
| 400 | } | ||
| 401 | SaveDebugControlValues(); | ||
| 402 | SaveHidbusValues(); | ||
| 403 | |||
| 404 | EndGroup(); | ||
| 405 | } | ||
| 406 | |||
| 407 | void QtConfig::SavePathValues() { | ||
| 408 | BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); | ||
| 409 | |||
| 410 | WriteSetting(std::string("romsPath"), UISettings::values.roms_path); | ||
| 411 | WriteSetting(std::string("symbolsPath"), UISettings::values.symbols_path); | ||
| 412 | BeginArray(std::string("gamedirs")); | ||
| 413 | for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { | ||
| 414 | SetArrayIndex(i); | ||
| 415 | const auto& game_dir = UISettings::values.game_dirs[i]; | ||
| 416 | WriteSetting(std::string("path"), game_dir.path); | ||
| 417 | WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false)); | ||
| 418 | WriteSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true)); | ||
| 419 | } | ||
| 420 | EndArray(); | ||
| 421 | |||
| 422 | WriteSetting(std::string("recentFiles"), | ||
| 423 | UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString()); | ||
| 424 | WriteSetting(std::string("language"), UISettings::values.language); | ||
| 425 | |||
| 426 | EndGroup(); | ||
| 427 | } | ||
| 428 | |||
| 429 | void QtConfig::SaveShortcutValues() { | ||
| 430 | BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts)); | ||
| 431 | |||
| 432 | // Lengths of UISettings::values.shortcuts & default_hotkeys are same. | ||
| 433 | // However, their ordering must also be the same. | ||
| 434 | for (std::size_t i = 0; i < UISettings::default_hotkeys.size(); i++) { | ||
| 435 | const auto& [name, group, shortcut] = UISettings::values.shortcuts[i]; | ||
| 436 | const auto& default_hotkey = UISettings::default_hotkeys[i].shortcut; | ||
| 437 | |||
| 438 | BeginGroup(group); | ||
| 439 | BeginGroup(name); | ||
| 440 | |||
| 441 | WriteSetting(std::string("KeySeq"), shortcut.keyseq, | ||
| 442 | std::make_optional(default_hotkey.keyseq)); | ||
| 443 | WriteSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq, | ||
| 444 | std::make_optional(default_hotkey.controller_keyseq)); | ||
| 445 | WriteSetting(std::string("Context"), shortcut.context, | ||
| 446 | std::make_optional(default_hotkey.context)); | ||
| 447 | WriteSetting(std::string("Repeat"), shortcut.repeat, | ||
| 448 | std::make_optional(default_hotkey.repeat)); | ||
| 449 | |||
| 450 | EndGroup(); // name | ||
| 451 | EndGroup(); // group | ||
| 452 | } | ||
| 453 | |||
| 454 | EndGroup(); | ||
| 455 | } | ||
| 456 | |||
| 457 | void QtConfig::SaveUIValues() { | ||
| 458 | BeginGroup(Settings::TranslateCategory(Settings::Category::Ui)); | ||
| 459 | |||
| 460 | WriteCategory(Settings::Category::Ui); | ||
| 461 | WriteCategory(Settings::Category::UiGeneral); | ||
| 462 | |||
| 463 | WriteSetting(std::string("theme"), UISettings::values.theme, | ||
| 464 | std::make_optional(std::string( | ||
| 465 | UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second))); | ||
| 466 | |||
| 467 | SaveUIGamelistValues(); | ||
| 468 | SaveUILayoutValues(); | ||
| 469 | SavePathValues(); | ||
| 470 | SaveScreenshotValues(); | ||
| 471 | SaveShortcutValues(); | ||
| 472 | SaveMultiplayerValues(); | ||
| 473 | |||
| 474 | EndGroup(); | ||
| 475 | } | ||
| 476 | |||
| 477 | void QtConfig::SaveUIGamelistValues() { | ||
| 478 | BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); | ||
| 479 | |||
| 480 | WriteCategory(Settings::Category::UiGameList); | ||
| 481 | |||
| 482 | BeginArray(std::string("favorites")); | ||
| 483 | for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { | ||
| 484 | SetArrayIndex(i); | ||
| 485 | WriteSetting(std::string("program_id"), UISettings::values.favorited_ids[i]); | ||
| 486 | } | ||
| 487 | EndArray(); // favorites | ||
| 488 | |||
| 489 | EndGroup(); | ||
| 490 | } | ||
| 491 | |||
| 492 | void QtConfig::SaveUILayoutValues() { | ||
| 493 | BeginGroup(Settings::TranslateCategory(Settings::Category::UiLayout)); | ||
| 494 | |||
| 495 | WriteCategory(Settings::Category::UiLayout); | ||
| 496 | |||
| 497 | EndGroup(); | ||
| 498 | } | ||
| 499 | |||
| 500 | void QtConfig::SaveMultiplayerValues() { | ||
| 501 | BeginGroup(std::string("Multiplayer")); | ||
| 502 | |||
| 503 | WriteCategory(Settings::Category::Multiplayer); | ||
| 504 | |||
| 505 | // Write ban list | ||
| 506 | BeginArray(std::string("username_ban_list")); | ||
| 507 | for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) { | ||
| 508 | SetArrayIndex(static_cast<int>(i)); | ||
| 509 | WriteSetting(std::string("username"), UISettings::values.multiplayer_ban_list.first[i]); | ||
| 510 | } | ||
| 511 | EndArray(); // username_ban_list | ||
| 512 | |||
| 513 | BeginArray(std::string("ip_ban_list")); | ||
| 514 | for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) { | ||
| 515 | SetArrayIndex(static_cast<int>(i)); | ||
| 516 | WriteSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]); | ||
| 517 | } | ||
| 518 | EndArray(); // ip_ban_list | ||
| 519 | |||
| 520 | EndGroup(); | ||
| 521 | } | ||
| 522 | |||
| 523 | std::vector<Settings::BasicSetting*>& QtConfig::FindRelevantList(Settings::Category category) { | ||
| 524 | auto& map = Settings::values.linkage.by_category; | ||
| 525 | if (map.contains(category)) { | ||
| 526 | return Settings::values.linkage.by_category[category]; | ||
| 527 | } | ||
| 528 | return UISettings::values.linkage.by_category[category]; | ||
| 529 | } | ||
| 530 | |||
| 531 | void QtConfig::ReadQtControlPlayerValues(std::size_t player_index) { | ||
| 532 | BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); | ||
| 533 | |||
| 534 | ReadPlayerValues(player_index); | ||
| 535 | ReadQtPlayerValues(player_index); | ||
| 536 | |||
| 537 | EndGroup(); | ||
| 538 | } | ||
| 539 | |||
| 540 | void QtConfig::SaveQtControlPlayerValues(std::size_t player_index) { | ||
| 541 | BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); | ||
| 542 | |||
| 543 | SavePlayerValues(player_index); | ||
| 544 | SaveQtPlayerValues(player_index); | ||
| 545 | |||
| 546 | EndGroup(); | ||
| 547 | |||
| 548 | WriteToIni(); | ||
| 549 | } | ||
diff --git a/src/yuzu/configuration/qt_config.h b/src/yuzu/configuration/qt_config.h new file mode 100644 index 000000000..dc2dceb4d --- /dev/null +++ b/src/yuzu/configuration/qt_config.h | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <QMetaType> | ||
| 7 | |||
| 8 | #include "frontend_common/config.h" | ||
| 9 | |||
| 10 | class QtConfig final : public Config { | ||
| 11 | public: | ||
| 12 | explicit QtConfig(const std::string& config_name = "qt-config", | ||
| 13 | ConfigType config_type = ConfigType::GlobalConfig); | ||
| 14 | ~QtConfig() override; | ||
| 15 | |||
| 16 | void ReloadAllValues() override; | ||
| 17 | void SaveAllValues() override; | ||
| 18 | |||
| 19 | void ReadQtControlPlayerValues(std::size_t player_index); | ||
| 20 | void SaveQtControlPlayerValues(std::size_t player_index); | ||
| 21 | |||
| 22 | protected: | ||
| 23 | void ReadQtValues(); | ||
| 24 | void ReadQtPlayerValues(std::size_t player_index); | ||
| 25 | void ReadQtControlValues(); | ||
| 26 | void ReadHidbusValues() override; | ||
| 27 | void ReadDebugControlValues() override; | ||
| 28 | void ReadPathValues() override; | ||
| 29 | void ReadShortcutValues() override; | ||
| 30 | void ReadUIValues() override; | ||
| 31 | void ReadUIGamelistValues() override; | ||
| 32 | void ReadUILayoutValues() override; | ||
| 33 | void ReadMultiplayerValues() override; | ||
| 34 | |||
| 35 | void SaveQtValues(); | ||
| 36 | void SaveQtPlayerValues(std::size_t player_index); | ||
| 37 | void SaveQtControlValues(); | ||
| 38 | void SaveHidbusValues() override; | ||
| 39 | void SaveDebugControlValues() override; | ||
| 40 | void SavePathValues() override; | ||
| 41 | void SaveShortcutValues() override; | ||
| 42 | void SaveUIValues() override; | ||
| 43 | void SaveUIGamelistValues() override; | ||
| 44 | void SaveUILayoutValues() override; | ||
| 45 | void SaveMultiplayerValues() override; | ||
| 46 | |||
| 47 | std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override; | ||
| 48 | |||
| 49 | public: | ||
| 50 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; | ||
| 51 | static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; | ||
| 52 | static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; | ||
| 53 | static const std::array<int, 2> default_stick_mod; | ||
| 54 | static const std::array<int, 2> default_ringcon_analogs; | ||
| 55 | }; | ||
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index 3fe448f27..a7b5def32 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp | |||
| @@ -1,17 +1,18 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/time_zone.h" | ||
| 5 | #include "yuzu/configuration/shared_translation.h" | 4 | #include "yuzu/configuration/shared_translation.h" |
| 6 | 5 | ||
| 7 | #include <map> | 6 | #include <map> |
| 8 | #include <memory> | 7 | #include <memory> |
| 9 | #include <tuple> | 8 | #include <tuple> |
| 10 | #include <utility> | 9 | #include <utility> |
| 10 | #include <QCoreApplication> | ||
| 11 | #include <QWidget> | 11 | #include <QWidget> |
| 12 | #include "common/settings.h" | 12 | #include "common/settings.h" |
| 13 | #include "common/settings_enums.h" | 13 | #include "common/settings_enums.h" |
| 14 | #include "common/settings_setting.h" | 14 | #include "common/settings_setting.h" |
| 15 | #include "common/time_zone.h" | ||
| 15 | #include "yuzu/uisettings.h" | 16 | #include "yuzu/uisettings.h" |
| 16 | 17 | ||
| 17 | namespace ConfigurationShared { | 18 | namespace ConfigurationShared { |
| @@ -21,123 +22,135 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { | |||
| 21 | const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); }; | 22 | const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); }; |
| 22 | 23 | ||
| 23 | #define INSERT(SETTINGS, ID, NAME, TOOLTIP) \ | 24 | #define INSERT(SETTINGS, ID, NAME, TOOLTIP) \ |
| 24 | translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{tr((NAME)), tr((TOOLTIP))}}) | 25 | translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}}) |
| 25 | 26 | ||
| 26 | // A setting can be ignored by giving it a blank name | 27 | // A setting can be ignored by giving it a blank name |
| 27 | 28 | ||
| 28 | // Audio | 29 | // Audio |
| 29 | INSERT(Settings, sink_id, "Output Engine:", ""); | 30 | INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral()); |
| 30 | INSERT(Settings, audio_output_device_id, "Output Device:", ""); | 31 | INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral()); |
| 31 | INSERT(Settings, audio_input_device_id, "Input Device:", ""); | 32 | INSERT(Settings, audio_input_device_id, tr("Input Device:"), QStringLiteral()); |
| 32 | INSERT(Settings, audio_muted, "Mute audio", ""); | 33 | INSERT(Settings, audio_muted, tr("Mute audio"), QStringLiteral()); |
| 33 | INSERT(Settings, volume, "Volume:", ""); | 34 | INSERT(Settings, volume, tr("Volume:"), QStringLiteral()); |
| 34 | INSERT(Settings, dump_audio_commands, "", ""); | 35 | INSERT(Settings, dump_audio_commands, QStringLiteral(), QStringLiteral()); |
| 35 | INSERT(UISettings, mute_when_in_background, "Mute audio when in background", ""); | 36 | INSERT(UISettings, mute_when_in_background, tr("Mute audio when in background"), |
| 37 | QStringLiteral()); | ||
| 36 | 38 | ||
| 37 | // Core | 39 | // Core |
| 38 | INSERT(Settings, use_multi_core, "Multicore CPU Emulation", ""); | 40 | INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), QStringLiteral()); |
| 39 | INSERT(Settings, memory_layout_mode, "Memory Layout", ""); | 41 | INSERT(Settings, memory_layout_mode, tr("Memory Layout"), QStringLiteral()); |
| 40 | INSERT(Settings, use_speed_limit, "", ""); | 42 | INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral()); |
| 41 | INSERT(Settings, speed_limit, "Limit Speed Percent", ""); | 43 | INSERT(Settings, speed_limit, tr("Limit Speed Percent"), QStringLiteral()); |
| 42 | 44 | ||
| 43 | // Cpu | 45 | // Cpu |
| 44 | INSERT(Settings, cpu_accuracy, "Accuracy:", ""); | 46 | INSERT(Settings, cpu_accuracy, tr("Accuracy:"), QStringLiteral()); |
| 45 | 47 | ||
| 46 | // Cpu Debug | 48 | // Cpu Debug |
| 47 | 49 | ||
| 48 | // Cpu Unsafe | 50 | // Cpu Unsafe |
| 49 | INSERT(Settings, cpuopt_unsafe_unfuse_fma, | ||
| 50 | "Unfuse FMA (improve performance on CPUs without FMA)", | ||
| 51 | "This option improves speed by reducing accuracy of fused-multiply-add instructions on " | ||
| 52 | "CPUs without native FMA support."); | ||
| 53 | INSERT(Settings, cpuopt_unsafe_reduce_fp_error, "Faster FRSQRTE and FRECPE", | ||
| 54 | "This option improves the speed of some approximate floating-point functions by using " | ||
| 55 | "less accurate native approximations."); | ||
| 56 | INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, "Faster ASIMD instructions (32 bits only)", | ||
| 57 | "This option improves the speed of 32 bits ASIMD floating-point functions by running " | ||
| 58 | "with incorrect rounding modes."); | ||
| 59 | INSERT(Settings, cpuopt_unsafe_inaccurate_nan, "Inaccurate NaN handling", | ||
| 60 | "This option improves speed by removing NaN checking. Please note this also reduces " | ||
| 61 | "accuracy of certain floating-point instructions."); | ||
| 62 | INSERT( | 51 | INSERT( |
| 63 | Settings, cpuopt_unsafe_fastmem_check, "Disable address space checks", | 52 | Settings, cpuopt_unsafe_unfuse_fma, |
| 64 | "This option improves speed by eliminating a safety check before every memory read/write " | 53 | tr("Unfuse FMA (improve performance on CPUs without FMA)"), |
| 65 | "in guest. Disabling it may allow a game to read/write the emulator's memory."); | 54 | tr("This option improves speed by reducing accuracy of fused-multiply-add instructions on " |
| 66 | INSERT(Settings, cpuopt_unsafe_ignore_global_monitor, "Ignore global monitor", | 55 | "CPUs without native FMA support.")); |
| 67 | "This option improves speed by relying only on the semantics of cmpxchg to ensure " | 56 | INSERT( |
| 57 | Settings, cpuopt_unsafe_reduce_fp_error, tr("Faster FRSQRTE and FRECPE"), | ||
| 58 | tr("This option improves the speed of some approximate floating-point functions by using " | ||
| 59 | "less accurate native approximations.")); | ||
| 60 | INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, | ||
| 61 | tr("Faster ASIMD instructions (32 bits only)"), | ||
| 62 | tr("This option improves the speed of 32 bits ASIMD floating-point functions by running " | ||
| 63 | "with incorrect rounding modes.")); | ||
| 64 | INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"), | ||
| 65 | tr("This option improves speed by removing NaN checking. Please note this also reduces " | ||
| 66 | "accuracy of certain floating-point instructions.")); | ||
| 67 | INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"), | ||
| 68 | tr("This option improves speed by eliminating a safety check before every memory " | ||
| 69 | "read/write " | ||
| 70 | "in guest. Disabling it may allow a game to read/write the emulator's memory.")); | ||
| 71 | INSERT( | ||
| 72 | Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"), | ||
| 73 | tr("This option improves speed by relying only on the semantics of cmpxchg to ensure " | ||
| 68 | "safety of exclusive access instructions. Please note this may result in deadlocks and " | 74 | "safety of exclusive access instructions. Please note this may result in deadlocks and " |
| 69 | "other race conditions."); | 75 | "other race conditions.")); |
| 70 | 76 | ||
| 71 | // Renderer | 77 | // Renderer |
| 72 | INSERT(Settings, renderer_backend, "API:", ""); | 78 | INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral()); |
| 73 | INSERT(Settings, vulkan_device, "Device:", ""); | 79 | INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral()); |
| 74 | INSERT(Settings, shader_backend, "Shader Backend:", ""); | 80 | INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral()); |
| 75 | INSERT(Settings, resolution_setup, "Resolution:", ""); | 81 | INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral()); |
| 76 | INSERT(Settings, scaling_filter, "Window Adapting Filter:", ""); | 82 | INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral()); |
| 77 | INSERT(Settings, fsr_sharpening_slider, "FSR Sharpness:", ""); | 83 | INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral()); |
| 78 | INSERT(Settings, anti_aliasing, "Anti-Aliasing Method:", ""); | 84 | INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral()); |
| 79 | INSERT(Settings, fullscreen_mode, "Fullscreen Mode:", ""); | 85 | INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral()); |
| 80 | INSERT(Settings, aspect_ratio, "Aspect Ratio:", ""); | 86 | INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral()); |
| 81 | INSERT(Settings, use_disk_shader_cache, "Use disk pipeline cache", ""); | 87 | INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral()); |
| 82 | INSERT(Settings, use_asynchronous_gpu_emulation, "Use asynchronous GPU emulation", ""); | 88 | INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"), |
| 83 | INSERT(Settings, nvdec_emulation, "NVDEC emulation:", ""); | 89 | QStringLiteral()); |
| 84 | INSERT(Settings, accelerate_astc, "ASTC Decoding Method:", ""); | 90 | INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral()); |
| 85 | INSERT(Settings, astc_recompression, "ASTC Recompression Method:", ""); | 91 | INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral()); |
| 86 | INSERT(Settings, vsync_mode, "VSync Mode:", | 92 | INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), QStringLiteral()); |
| 87 | "FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " | 93 | INSERT( |
| 94 | Settings, vsync_mode, tr("VSync Mode:"), | ||
| 95 | tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " | ||
| 88 | "refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from " | 96 | "refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from " |
| 89 | "a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop " | 97 | "a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop " |
| 90 | "frames.\nImmediate (no synchronization) just presents whatever is available and can " | 98 | "frames.\nImmediate (no synchronization) just presents whatever is available and can " |
| 91 | "exhibit tearing."); | 99 | "exhibit tearing.")); |
| 92 | INSERT(Settings, bg_red, "", ""); | 100 | INSERT(Settings, bg_red, QStringLiteral(), QStringLiteral()); |
| 93 | INSERT(Settings, bg_green, "", ""); | 101 | INSERT(Settings, bg_green, QStringLiteral(), QStringLiteral()); |
| 94 | INSERT(Settings, bg_blue, "", ""); | 102 | INSERT(Settings, bg_blue, QStringLiteral(), QStringLiteral()); |
| 95 | 103 | ||
| 96 | // Renderer (Advanced Graphics) | 104 | // Renderer (Advanced Graphics) |
| 97 | INSERT(Settings, async_presentation, "Enable asynchronous presentation (Vulkan only)", ""); | 105 | INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"), |
| 98 | INSERT(Settings, renderer_force_max_clock, "Force maximum clocks (Vulkan only)", | 106 | QStringLiteral()); |
| 99 | "Runs work in the background while waiting for graphics commands to keep the GPU from " | 107 | INSERT( |
| 100 | "lowering its clock speed."); | 108 | Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"), |
| 101 | INSERT(Settings, max_anisotropy, "Anisotropic Filtering:", ""); | 109 | tr("Runs work in the background while waiting for graphics commands to keep the GPU from " |
| 102 | INSERT(Settings, gpu_accuracy, "Accuracy Level:", ""); | 110 | "lowering its clock speed.")); |
| 103 | INSERT(Settings, use_asynchronous_shaders, "Use asynchronous shader building (Hack)", | 111 | INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral()); |
| 104 | "Enables asynchronous shader compilation, which may reduce shader stutter. This feature " | 112 | INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral()); |
| 105 | "is experimental."); | 113 | INSERT( |
| 106 | INSERT(Settings, use_fast_gpu_time, "Use Fast GPU Time (Hack)", | 114 | Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"), |
| 107 | "Enables Fast GPU Time. This option will force most games to run at their highest " | 115 | tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature " |
| 108 | "native resolution."); | 116 | "is experimental.")); |
| 109 | INSERT(Settings, use_vulkan_driver_pipeline_cache, "Use Vulkan pipeline cache", | 117 | INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"), |
| 110 | "Enables GPU vendor-specific pipeline cache. This option can improve shader loading " | 118 | tr("Enables Fast GPU Time. This option will force most games to run at their highest " |
| 111 | "time significantly in cases where the Vulkan driver does not store pipeline cache " | 119 | "native resolution.")); |
| 112 | "files internally."); | 120 | INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"), |
| 113 | INSERT(Settings, enable_compute_pipelines, "Enable Compute Pipelines (Intel Vulkan Only)", | 121 | tr("Enables GPU vendor-specific pipeline cache. This option can improve shader loading " |
| 114 | "Enable compute pipelines, required by some games.\nThis setting only exists for Intel " | 122 | "time significantly in cases where the Vulkan driver does not store pipeline cache " |
| 123 | "files internally.")); | ||
| 124 | INSERT( | ||
| 125 | Settings, enable_compute_pipelines, tr("Enable Compute Pipelines (Intel Vulkan Only)"), | ||
| 126 | tr("Enable compute pipelines, required by some games.\nThis setting only exists for Intel " | ||
| 115 | "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled " | 127 | "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled " |
| 116 | "on all other drivers."); | 128 | "on all other drivers.")); |
| 117 | INSERT(Settings, use_reactive_flushing, "Enable Reactive Flushing", | 129 | INSERT( |
| 118 | "Uses reactive flushing instead of predictive flushing, allowing more accurate memory " | 130 | Settings, use_reactive_flushing, tr("Enable Reactive Flushing"), |
| 119 | "syncing."); | 131 | tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory " |
| 120 | INSERT(Settings, use_video_framerate, "Sync to framerate of video playback", | 132 | "syncing.")); |
| 121 | "Run the game at normal speed during video playback, even when the framerate is " | 133 | INSERT(Settings, use_video_framerate, tr("Sync to framerate of video playback"), |
| 122 | "unlocked."); | 134 | tr("Run the game at normal speed during video playback, even when the framerate is " |
| 123 | INSERT(Settings, barrier_feedback_loops, "Barrier feedback loops", | 135 | "unlocked.")); |
| 124 | "Improves rendering of transparency effects in specific games."); | 136 | INSERT(Settings, barrier_feedback_loops, tr("Barrier feedback loops"), |
| 137 | tr("Improves rendering of transparency effects in specific games.")); | ||
| 125 | 138 | ||
| 126 | // Renderer (Debug) | 139 | // Renderer (Debug) |
| 127 | 140 | ||
| 128 | // System | 141 | // System |
| 129 | INSERT(Settings, rng_seed, "RNG Seed", ""); | 142 | INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral()); |
| 130 | INSERT(Settings, rng_seed_enabled, "", ""); | 143 | INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral()); |
| 131 | INSERT(Settings, device_name, "Device Name", ""); | 144 | INSERT(Settings, device_name, tr("Device Name"), QStringLiteral()); |
| 132 | INSERT(Settings, custom_rtc, "Custom RTC", ""); | 145 | INSERT(Settings, custom_rtc, tr("Custom RTC"), QStringLiteral()); |
| 133 | INSERT(Settings, custom_rtc_enabled, "", ""); | 146 | INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral()); |
| 134 | INSERT(Settings, language_index, | 147 | INSERT(Settings, language_index, tr("Language:"), |
| 135 | "Language:", "Note: this can be overridden when region setting is auto-select"); | 148 | tr("Note: this can be overridden when region setting is auto-select")); |
| 136 | INSERT(Settings, region_index, "Region:", ""); | 149 | INSERT(Settings, region_index, tr("Region:"), QStringLiteral()); |
| 137 | INSERT(Settings, time_zone_index, "Time Zone:", ""); | 150 | INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral()); |
| 138 | INSERT(Settings, sound_index, "Sound Output Mode:", ""); | 151 | INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral()); |
| 139 | INSERT(Settings, use_docked_mode, "Console Mode:", ""); | 152 | INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral()); |
| 140 | INSERT(Settings, current_user, "", ""); | 153 | INSERT(Settings, current_user, QStringLiteral(), QStringLiteral()); |
| 141 | 154 | ||
| 142 | // Controls | 155 | // Controls |
| 143 | 156 | ||
| @@ -154,12 +167,14 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { | |||
| 154 | // Ui | 167 | // Ui |
| 155 | 168 | ||
| 156 | // Ui General | 169 | // Ui General |
| 157 | INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); | 170 | INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), QStringLiteral()); |
| 158 | INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); | 171 | INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"), |
| 159 | INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", ""); | 172 | QStringLiteral()); |
| 160 | INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); | 173 | INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"), |
| 161 | INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); | 174 | QStringLiteral()); |
| 162 | INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); | 175 | INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral()); |
| 176 | INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), | ||
| 177 | QStringLiteral()); | ||
| 163 | 178 | ||
| 164 | // Ui Debugging | 179 | // Ui Debugging |
| 165 | 180 | ||
| @@ -179,140 +194,141 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) { | |||
| 179 | return parent->tr(text, context); | 194 | return parent->tr(text, context); |
| 180 | }; | 195 | }; |
| 181 | 196 | ||
| 182 | #define PAIR(ENUM, VALUE, TRANSLATION) \ | 197 | #define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)} |
| 183 | { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION) } | ||
| 184 | #define CTX_PAIR(ENUM, VALUE, TRANSLATION, CONTEXT) \ | ||
| 185 | { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION, CONTEXT) } | ||
| 186 | 198 | ||
| 187 | // Intentionally skipping VSyncMode to let the UI fill that one out | 199 | // Intentionally skipping VSyncMode to let the UI fill that one out |
| 188 | 200 | ||
| 189 | translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), | 201 | translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), |
| 190 | { | 202 | { |
| 191 | PAIR(AstcDecodeMode, Cpu, "CPU"), | 203 | PAIR(AstcDecodeMode, Cpu, tr("CPU")), |
| 192 | PAIR(AstcDecodeMode, Gpu, "GPU"), | 204 | PAIR(AstcDecodeMode, Gpu, tr("GPU")), |
| 193 | PAIR(AstcDecodeMode, CpuAsynchronous, "CPU Asynchronous"), | 205 | PAIR(AstcDecodeMode, CpuAsynchronous, tr("CPU Asynchronous")), |
| 194 | }}); | ||
| 195 | translations->insert({Settings::EnumMetadata<Settings::AstcRecompression>::Index(), | ||
| 196 | { | ||
| 197 | PAIR(AstcRecompression, Uncompressed, "Uncompressed (Best quality)"), | ||
| 198 | PAIR(AstcRecompression, Bc1, "BC1 (Low quality)"), | ||
| 199 | PAIR(AstcRecompression, Bc3, "BC3 (Medium quality)"), | ||
| 200 | }}); | 206 | }}); |
| 207 | translations->insert( | ||
| 208 | {Settings::EnumMetadata<Settings::AstcRecompression>::Index(), | ||
| 209 | { | ||
| 210 | PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")), | ||
| 211 | PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")), | ||
| 212 | PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")), | ||
| 213 | }}); | ||
| 201 | translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), | 214 | translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), |
| 202 | { | 215 | { |
| 203 | #ifdef HAS_OPENGL | 216 | #ifdef HAS_OPENGL |
| 204 | PAIR(RendererBackend, OpenGL, "OpenGL"), | 217 | PAIR(RendererBackend, OpenGL, tr("OpenGL")), |
| 205 | #endif | 218 | #endif |
| 206 | PAIR(RendererBackend, Vulkan, "Vulkan"), | 219 | PAIR(RendererBackend, Vulkan, tr("Vulkan")), |
| 207 | PAIR(RendererBackend, Null, "Null"), | 220 | PAIR(RendererBackend, Null, tr("Null")), |
| 208 | }}); | ||
| 209 | translations->insert({Settings::EnumMetadata<Settings::ShaderBackend>::Index(), | ||
| 210 | { | ||
| 211 | PAIR(ShaderBackend, Glsl, "GLSL"), | ||
| 212 | PAIR(ShaderBackend, Glasm, "GLASM (Assembly Shaders, NVIDIA Only)"), | ||
| 213 | PAIR(ShaderBackend, SpirV, "SPIR-V (Experimental, Mesa Only)"), | ||
| 214 | }}); | 221 | }}); |
| 222 | translations->insert( | ||
| 223 | {Settings::EnumMetadata<Settings::ShaderBackend>::Index(), | ||
| 224 | { | ||
| 225 | PAIR(ShaderBackend, Glsl, tr("GLSL")), | ||
| 226 | PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")), | ||
| 227 | PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, Mesa Only)")), | ||
| 228 | }}); | ||
| 215 | translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(), | 229 | translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(), |
| 216 | { | 230 | { |
| 217 | PAIR(GpuAccuracy, Normal, "Normal"), | 231 | PAIR(GpuAccuracy, Normal, tr("Normal")), |
| 218 | PAIR(GpuAccuracy, High, "High"), | 232 | PAIR(GpuAccuracy, High, tr("High")), |
| 219 | PAIR(GpuAccuracy, Extreme, "Extreme"), | 233 | PAIR(GpuAccuracy, Extreme, tr("Extreme")), |
| 220 | }}); | ||
| 221 | translations->insert({Settings::EnumMetadata<Settings::CpuAccuracy>::Index(), | ||
| 222 | { | ||
| 223 | PAIR(CpuAccuracy, Auto, "Auto"), | ||
| 224 | PAIR(CpuAccuracy, Accurate, "Accurate"), | ||
| 225 | PAIR(CpuAccuracy, Unsafe, "Unsafe"), | ||
| 226 | PAIR(CpuAccuracy, Paranoid, "Paranoid (disables most optimizations)"), | ||
| 227 | }}); | 234 | }}); |
| 235 | translations->insert( | ||
| 236 | {Settings::EnumMetadata<Settings::CpuAccuracy>::Index(), | ||
| 237 | { | ||
| 238 | PAIR(CpuAccuracy, Auto, tr("Auto")), | ||
| 239 | PAIR(CpuAccuracy, Accurate, tr("Accurate")), | ||
| 240 | PAIR(CpuAccuracy, Unsafe, tr("Unsafe")), | ||
| 241 | PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")), | ||
| 242 | }}); | ||
| 228 | translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(), | 243 | translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(), |
| 229 | { | 244 | { |
| 230 | PAIR(FullscreenMode, Borderless, "Borderless Windowed"), | 245 | PAIR(FullscreenMode, Borderless, tr("Borderless Windowed")), |
| 231 | PAIR(FullscreenMode, Exclusive, "Exclusive Fullscreen"), | 246 | PAIR(FullscreenMode, Exclusive, tr("Exclusive Fullscreen")), |
| 232 | }}); | 247 | }}); |
| 233 | translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(), | 248 | translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(), |
| 234 | { | 249 | { |
| 235 | PAIR(NvdecEmulation, Off, "No Video Output"), | 250 | PAIR(NvdecEmulation, Off, tr("No Video Output")), |
| 236 | PAIR(NvdecEmulation, Cpu, "CPU Video Decoding"), | 251 | PAIR(NvdecEmulation, Cpu, tr("CPU Video Decoding")), |
| 237 | PAIR(NvdecEmulation, Gpu, "GPU Video Decoding (Default)"), | 252 | PAIR(NvdecEmulation, Gpu, tr("GPU Video Decoding (Default)")), |
| 238 | }}); | ||
| 239 | translations->insert({Settings::EnumMetadata<Settings::ResolutionSetup>::Index(), | ||
| 240 | { | ||
| 241 | PAIR(ResolutionSetup, Res1_2X, "0.5X (360p/540p) [EXPERIMENTAL]"), | ||
| 242 | PAIR(ResolutionSetup, Res3_4X, "0.75X (540p/810p) [EXPERIMENTAL]"), | ||
| 243 | PAIR(ResolutionSetup, Res1X, "1X (720p/1080p)"), | ||
| 244 | PAIR(ResolutionSetup, Res3_2X, "1.5X (1080p/1620p) [EXPERIMENTAL]"), | ||
| 245 | PAIR(ResolutionSetup, Res2X, "2X (1440p/2160p)"), | ||
| 246 | PAIR(ResolutionSetup, Res3X, "3X (2160p/3240p)"), | ||
| 247 | PAIR(ResolutionSetup, Res4X, "4X (2880p/4320p)"), | ||
| 248 | PAIR(ResolutionSetup, Res5X, "5X (3600p/5400p)"), | ||
| 249 | PAIR(ResolutionSetup, Res6X, "6X (4320p/6480p)"), | ||
| 250 | PAIR(ResolutionSetup, Res7X, "7X (5040p/7560p)"), | ||
| 251 | PAIR(ResolutionSetup, Res8X, "8X (5760p/8640p)"), | ||
| 252 | }}); | 253 | }}); |
| 254 | translations->insert( | ||
| 255 | {Settings::EnumMetadata<Settings::ResolutionSetup>::Index(), | ||
| 256 | { | ||
| 257 | PAIR(ResolutionSetup, Res1_2X, tr("0.5X (360p/540p) [EXPERIMENTAL]")), | ||
| 258 | PAIR(ResolutionSetup, Res3_4X, tr("0.75X (540p/810p) [EXPERIMENTAL]")), | ||
| 259 | PAIR(ResolutionSetup, Res1X, tr("1X (720p/1080p)")), | ||
| 260 | PAIR(ResolutionSetup, Res3_2X, tr("1.5X (1080p/1620p) [EXPERIMENTAL]")), | ||
| 261 | PAIR(ResolutionSetup, Res2X, tr("2X (1440p/2160p)")), | ||
| 262 | PAIR(ResolutionSetup, Res3X, tr("3X (2160p/3240p)")), | ||
| 263 | PAIR(ResolutionSetup, Res4X, tr("4X (2880p/4320p)")), | ||
| 264 | PAIR(ResolutionSetup, Res5X, tr("5X (3600p/5400p)")), | ||
| 265 | PAIR(ResolutionSetup, Res6X, tr("6X (4320p/6480p)")), | ||
| 266 | PAIR(ResolutionSetup, Res7X, tr("7X (5040p/7560p)")), | ||
| 267 | PAIR(ResolutionSetup, Res8X, tr("8X (5760p/8640p)")), | ||
| 268 | }}); | ||
| 253 | translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(), | 269 | translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(), |
| 254 | { | 270 | { |
| 255 | PAIR(ScalingFilter, NearestNeighbor, "Nearest Neighbor"), | 271 | PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), |
| 256 | PAIR(ScalingFilter, Bilinear, "Bilinear"), | 272 | PAIR(ScalingFilter, Bilinear, tr("Bilinear")), |
| 257 | PAIR(ScalingFilter, Bicubic, "Bicubic"), | 273 | PAIR(ScalingFilter, Bicubic, tr("Bicubic")), |
| 258 | PAIR(ScalingFilter, Gaussian, "Gaussian"), | 274 | PAIR(ScalingFilter, Gaussian, tr("Gaussian")), |
| 259 | PAIR(ScalingFilter, ScaleForce, "ScaleForce"), | 275 | PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), |
| 260 | PAIR(ScalingFilter, Fsr, "AMD FidelityFXâ„¢ï¸ Super Resolution"), | 276 | PAIR(ScalingFilter, Fsr, tr("AMD FidelityFXâ„¢ï¸ Super Resolution")), |
| 261 | }}); | 277 | }}); |
| 262 | translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(), | 278 | translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(), |
| 263 | { | 279 | { |
| 264 | PAIR(AntiAliasing, None, "None"), | 280 | PAIR(AntiAliasing, None, tr("None")), |
| 265 | PAIR(AntiAliasing, Fxaa, "FXAA"), | 281 | PAIR(AntiAliasing, Fxaa, tr("FXAA")), |
| 266 | PAIR(AntiAliasing, Smaa, "SMAA"), | 282 | PAIR(AntiAliasing, Smaa, tr("SMAA")), |
| 267 | }}); | 283 | }}); |
| 268 | translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(), | 284 | translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(), |
| 269 | { | 285 | { |
| 270 | PAIR(AspectRatio, R16_9, "Default (16:9)"), | 286 | PAIR(AspectRatio, R16_9, tr("Default (16:9)")), |
| 271 | PAIR(AspectRatio, R4_3, "Force 4:3"), | 287 | PAIR(AspectRatio, R4_3, tr("Force 4:3")), |
| 272 | PAIR(AspectRatio, R21_9, "Force 21:9"), | 288 | PAIR(AspectRatio, R21_9, tr("Force 21:9")), |
| 273 | PAIR(AspectRatio, R16_10, "Force 16:10"), | 289 | PAIR(AspectRatio, R16_10, tr("Force 16:10")), |
| 274 | PAIR(AspectRatio, Stretch, "Stretch to Window"), | 290 | PAIR(AspectRatio, Stretch, tr("Stretch to Window")), |
| 275 | }}); | 291 | }}); |
| 276 | translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(), | 292 | translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(), |
| 277 | { | 293 | { |
| 278 | PAIR(AnisotropyMode, Automatic, "Automatic"), | 294 | PAIR(AnisotropyMode, Automatic, tr("Automatic")), |
| 279 | PAIR(AnisotropyMode, Default, "Default"), | 295 | PAIR(AnisotropyMode, Default, tr("Default")), |
| 280 | PAIR(AnisotropyMode, X2, "2x"), | 296 | PAIR(AnisotropyMode, X2, tr("2x")), |
| 281 | PAIR(AnisotropyMode, X4, "4x"), | 297 | PAIR(AnisotropyMode, X4, tr("4x")), |
| 282 | PAIR(AnisotropyMode, X8, "8x"), | 298 | PAIR(AnisotropyMode, X8, tr("8x")), |
| 283 | PAIR(AnisotropyMode, X16, "16x"), | 299 | PAIR(AnisotropyMode, X16, tr("16x")), |
| 284 | }}); | 300 | }}); |
| 285 | translations->insert( | 301 | translations->insert( |
| 286 | {Settings::EnumMetadata<Settings::Language>::Index(), | 302 | {Settings::EnumMetadata<Settings::Language>::Index(), |
| 287 | { | 303 | { |
| 288 | PAIR(Language, Japanese, "Japanese (日本語)"), | 304 | PAIR(Language, Japanese, tr("Japanese (日本語)")), |
| 289 | PAIR(Language, EnglishAmerican, "American English"), | 305 | PAIR(Language, EnglishAmerican, tr("American English")), |
| 290 | PAIR(Language, French, "French (français)"), | 306 | PAIR(Language, French, tr("French (français)")), |
| 291 | PAIR(Language, German, "German (Deutsch)"), | 307 | PAIR(Language, German, tr("German (Deutsch)")), |
| 292 | PAIR(Language, Italian, "Italian (italiano)"), | 308 | PAIR(Language, Italian, tr("Italian (italiano)")), |
| 293 | PAIR(Language, Spanish, "Spanish (español)"), | 309 | PAIR(Language, Spanish, tr("Spanish (español)")), |
| 294 | PAIR(Language, Chinese, "Chinese"), | 310 | PAIR(Language, Chinese, tr("Chinese")), |
| 295 | PAIR(Language, Korean, "Korean (한êµì–´)"), | 311 | PAIR(Language, Korean, tr("Korean (한êµì–´)")), |
| 296 | PAIR(Language, Dutch, "Dutch (Nederlands)"), | 312 | PAIR(Language, Dutch, tr("Dutch (Nederlands)")), |
| 297 | PAIR(Language, Portuguese, "Portuguese (português)"), | 313 | PAIR(Language, Portuguese, tr("Portuguese (português)")), |
| 298 | PAIR(Language, Russian, "Russian (РуÑÑкий)"), | 314 | PAIR(Language, Russian, tr("Russian (РуÑÑкий)")), |
| 299 | PAIR(Language, Taiwanese, "Taiwanese"), | 315 | PAIR(Language, Taiwanese, tr("Taiwanese")), |
| 300 | PAIR(Language, EnglishBritish, "British English"), | 316 | PAIR(Language, EnglishBritish, tr("British English")), |
| 301 | PAIR(Language, FrenchCanadian, "Canadian French"), | 317 | PAIR(Language, FrenchCanadian, tr("Canadian French")), |
| 302 | PAIR(Language, SpanishLatin, "Latin American Spanish"), | 318 | PAIR(Language, SpanishLatin, tr("Latin American Spanish")), |
| 303 | PAIR(Language, ChineseSimplified, "Simplified Chinese"), | 319 | PAIR(Language, ChineseSimplified, tr("Simplified Chinese")), |
| 304 | PAIR(Language, ChineseTraditional, "Traditional Chinese (æ£é«”䏿–‡)"), | 320 | PAIR(Language, ChineseTraditional, tr("Traditional Chinese (æ£é«”䏿–‡)")), |
| 305 | PAIR(Language, PortugueseBrazilian, "Brazilian Portuguese (português do Brasil)"), | 321 | PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")), |
| 306 | }}); | 322 | }}); |
| 307 | translations->insert({Settings::EnumMetadata<Settings::Region>::Index(), | 323 | translations->insert({Settings::EnumMetadata<Settings::Region>::Index(), |
| 308 | { | 324 | { |
| 309 | PAIR(Region, Japan, "Japan"), | 325 | PAIR(Region, Japan, tr("Japan")), |
| 310 | PAIR(Region, Usa, "USA"), | 326 | PAIR(Region, Usa, tr("USA")), |
| 311 | PAIR(Region, Europe, "Europe"), | 327 | PAIR(Region, Europe, tr("Europe")), |
| 312 | PAIR(Region, Australia, "Australia"), | 328 | PAIR(Region, Australia, tr("Australia")), |
| 313 | PAIR(Region, China, "China"), | 329 | PAIR(Region, China, tr("China")), |
| 314 | PAIR(Region, Korea, "Korea"), | 330 | PAIR(Region, Korea, tr("Korea")), |
| 315 | PAIR(Region, Taiwan, "Taiwan"), | 331 | PAIR(Region, Taiwan, tr("Taiwan")), |
| 316 | }}); | 332 | }}); |
| 317 | translations->insert( | 333 | translations->insert( |
| 318 | {Settings::EnumMetadata<Settings::TimeZone>::Index(), | 334 | {Settings::EnumMetadata<Settings::TimeZone>::Index(), |
| @@ -324,72 +340,74 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) { | |||
| 324 | {static_cast<u32>(Settings::TimeZone::Default), | 340 | {static_cast<u32>(Settings::TimeZone::Default), |
| 325 | tr("Default (%1)", "Default time zone") | 341 | tr("Default (%1)", "Default time zone") |
| 326 | .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, | 342 | .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, |
| 327 | PAIR(TimeZone, Cet, "CET"), | 343 | PAIR(TimeZone, Cet, tr("CET")), |
| 328 | PAIR(TimeZone, Cst6Cdt, "CST6CDT"), | 344 | PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")), |
| 329 | PAIR(TimeZone, Cuba, "Cuba"), | 345 | PAIR(TimeZone, Cuba, tr("Cuba")), |
| 330 | PAIR(TimeZone, Eet, "EET"), | 346 | PAIR(TimeZone, Eet, tr("EET")), |
| 331 | PAIR(TimeZone, Egypt, "Egypt"), | 347 | PAIR(TimeZone, Egypt, tr("Egypt")), |
| 332 | PAIR(TimeZone, Eire, "Eire"), | 348 | PAIR(TimeZone, Eire, tr("Eire")), |
| 333 | PAIR(TimeZone, Est, "EST"), | 349 | PAIR(TimeZone, Est, tr("EST")), |
| 334 | PAIR(TimeZone, Est5Edt, "EST5EDT"), | 350 | PAIR(TimeZone, Est5Edt, tr("EST5EDT")), |
| 335 | PAIR(TimeZone, Gb, "GB"), | 351 | PAIR(TimeZone, Gb, tr("GB")), |
| 336 | PAIR(TimeZone, GbEire, "GB-Eire"), | 352 | PAIR(TimeZone, GbEire, tr("GB-Eire")), |
| 337 | PAIR(TimeZone, Gmt, "GMT"), | 353 | PAIR(TimeZone, Gmt, tr("GMT")), |
| 338 | PAIR(TimeZone, GmtPlusZero, "GMT+0"), | 354 | PAIR(TimeZone, GmtPlusZero, tr("GMT+0")), |
| 339 | PAIR(TimeZone, GmtMinusZero, "GMT-0"), | 355 | PAIR(TimeZone, GmtMinusZero, tr("GMT-0")), |
| 340 | PAIR(TimeZone, GmtZero, "GMT0"), | 356 | PAIR(TimeZone, GmtZero, tr("GMT0")), |
| 341 | PAIR(TimeZone, Greenwich, "Greenwich"), | 357 | PAIR(TimeZone, Greenwich, tr("Greenwich")), |
| 342 | PAIR(TimeZone, Hongkong, "Hongkong"), | 358 | PAIR(TimeZone, Hongkong, tr("Hongkong")), |
| 343 | PAIR(TimeZone, Hst, "HST"), | 359 | PAIR(TimeZone, Hst, tr("HST")), |
| 344 | PAIR(TimeZone, Iceland, "Iceland"), | 360 | PAIR(TimeZone, Iceland, tr("Iceland")), |
| 345 | PAIR(TimeZone, Iran, "Iran"), | 361 | PAIR(TimeZone, Iran, tr("Iran")), |
| 346 | PAIR(TimeZone, Israel, "Israel"), | 362 | PAIR(TimeZone, Israel, tr("Israel")), |
| 347 | PAIR(TimeZone, Jamaica, "Jamaica"), | 363 | PAIR(TimeZone, Jamaica, tr("Jamaica")), |
| 348 | PAIR(TimeZone, Japan, "Japan"), | 364 | PAIR(TimeZone, Japan, tr("Japan")), |
| 349 | PAIR(TimeZone, Kwajalein, "Kwajalein"), | 365 | PAIR(TimeZone, Kwajalein, tr("Kwajalein")), |
| 350 | PAIR(TimeZone, Libya, "Libya"), | 366 | PAIR(TimeZone, Libya, tr("Libya")), |
| 351 | PAIR(TimeZone, Met, "MET"), | 367 | PAIR(TimeZone, Met, tr("MET")), |
| 352 | PAIR(TimeZone, Mst, "MST"), | 368 | PAIR(TimeZone, Mst, tr("MST")), |
| 353 | PAIR(TimeZone, Mst7Mdt, "MST7MDT"), | 369 | PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")), |
| 354 | PAIR(TimeZone, Navajo, "Navajo"), | 370 | PAIR(TimeZone, Navajo, tr("Navajo")), |
| 355 | PAIR(TimeZone, Nz, "NZ"), | 371 | PAIR(TimeZone, Nz, tr("NZ")), |
| 356 | PAIR(TimeZone, NzChat, "NZ-CHAT"), | 372 | PAIR(TimeZone, NzChat, tr("NZ-CHAT")), |
| 357 | PAIR(TimeZone, Poland, "Poland"), | 373 | PAIR(TimeZone, Poland, tr("Poland")), |
| 358 | PAIR(TimeZone, Portugal, "Portugal"), | 374 | PAIR(TimeZone, Portugal, tr("Portugal")), |
| 359 | PAIR(TimeZone, Prc, "PRC"), | 375 | PAIR(TimeZone, Prc, tr("PRC")), |
| 360 | PAIR(TimeZone, Pst8Pdt, "PST8PDT"), | 376 | PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")), |
| 361 | PAIR(TimeZone, Roc, "ROC"), | 377 | PAIR(TimeZone, Roc, tr("ROC")), |
| 362 | PAIR(TimeZone, Rok, "ROK"), | 378 | PAIR(TimeZone, Rok, tr("ROK")), |
| 363 | PAIR(TimeZone, Singapore, "Singapore"), | 379 | PAIR(TimeZone, Singapore, tr("Singapore")), |
| 364 | PAIR(TimeZone, Turkey, "Turkey"), | 380 | PAIR(TimeZone, Turkey, tr("Turkey")), |
| 365 | PAIR(TimeZone, Uct, "UCT"), | 381 | PAIR(TimeZone, Uct, tr("UCT")), |
| 366 | PAIR(TimeZone, Universal, "Universal"), | 382 | PAIR(TimeZone, Universal, tr("Universal")), |
| 367 | PAIR(TimeZone, Utc, "UTC"), | 383 | PAIR(TimeZone, Utc, tr("UTC")), |
| 368 | PAIR(TimeZone, WSu, "W-SU"), | 384 | PAIR(TimeZone, WSu, tr("W-SU")), |
| 369 | PAIR(TimeZone, Wet, "WET"), | 385 | PAIR(TimeZone, Wet, tr("WET")), |
| 370 | PAIR(TimeZone, Zulu, "Zulu"), | 386 | PAIR(TimeZone, Zulu, tr("Zulu")), |
| 371 | }}); | 387 | }}); |
| 372 | translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(), | 388 | translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(), |
| 373 | { | 389 | { |
| 374 | PAIR(AudioMode, Mono, "Mono"), | 390 | PAIR(AudioMode, Mono, tr("Mono")), |
| 375 | PAIR(AudioMode, Stereo, "Stereo"), | 391 | PAIR(AudioMode, Stereo, tr("Stereo")), |
| 376 | PAIR(AudioMode, Surround, "Surround"), | 392 | PAIR(AudioMode, Surround, tr("Surround")), |
| 377 | }}); | 393 | }}); |
| 378 | translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(), | 394 | translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(), |
| 379 | { | 395 | { |
| 380 | PAIR(MemoryLayout, Memory_4Gb, "4GB DRAM (Default)"), | 396 | PAIR(MemoryLayout, Memory_4Gb, tr("4GB DRAM (Default)")), |
| 381 | PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), | 397 | PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM (Unsafe)")), |
| 382 | PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), | 398 | PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM (Unsafe)")), |
| 399 | }}); | ||
| 400 | translations->insert({Settings::EnumMetadata<Settings::ConsoleMode>::Index(), | ||
| 401 | { | ||
| 402 | PAIR(ConsoleMode, Docked, tr("Docked")), | ||
| 403 | PAIR(ConsoleMode, Handheld, tr("Handheld")), | ||
| 383 | }}); | 404 | }}); |
| 384 | translations->insert( | ||
| 385 | {Settings::EnumMetadata<Settings::ConsoleMode>::Index(), | ||
| 386 | {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}}); | ||
| 387 | translations->insert( | 405 | translations->insert( |
| 388 | {Settings::EnumMetadata<Settings::ConfirmStop>::Index(), | 406 | {Settings::EnumMetadata<Settings::ConfirmStop>::Index(), |
| 389 | { | 407 | { |
| 390 | PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"), | 408 | PAIR(ConfirmStop, Ask_Always, tr("Always ask (Default)")), |
| 391 | PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"), | 409 | PAIR(ConfirmStop, Ask_Based_On_Game, tr("Only if game specifies not to stop")), |
| 392 | PAIR(ConfirmStop, Ask_Never, "Never ask"), | 410 | PAIR(ConfirmStop, Ask_Never, tr("Never ask")), |
| 393 | }}); | 411 | }}); |
| 394 | 412 | ||
| 395 | #undef PAIR | 413 | #undef PAIR |
diff --git a/src/yuzu/configuration/shared_translation.h b/src/yuzu/configuration/shared_translation.h index 99a0e808c..d5fc3b8de 100644 --- a/src/yuzu/configuration/shared_translation.h +++ b/src/yuzu/configuration/shared_translation.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | #include <QString> | 11 | #include <QString> |
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/settings.h" | ||
| 13 | 14 | ||
| 14 | class QWidget; | 15 | class QWidget; |
| 15 | 16 | ||
| @@ -22,4 +23,46 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent); | |||
| 22 | 23 | ||
| 23 | std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent); | 24 | std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent); |
| 24 | 25 | ||
| 26 | static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = { | ||
| 27 | {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))}, | ||
| 28 | {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))}, | ||
| 29 | {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))}, | ||
| 30 | }; | ||
| 31 | |||
| 32 | static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map = { | ||
| 33 | {Settings::ScalingFilter::NearestNeighbor, | ||
| 34 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))}, | ||
| 35 | {Settings::ScalingFilter::Bilinear, | ||
| 36 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))}, | ||
| 37 | {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, | ||
| 38 | {Settings::ScalingFilter::Gaussian, | ||
| 39 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, | ||
| 40 | {Settings::ScalingFilter::ScaleForce, | ||
| 41 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, | ||
| 42 | {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, | ||
| 43 | }; | ||
| 44 | |||
| 45 | static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = { | ||
| 46 | {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, | ||
| 47 | {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, | ||
| 48 | }; | ||
| 49 | |||
| 50 | static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = { | ||
| 51 | {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))}, | ||
| 52 | {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))}, | ||
| 53 | {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))}, | ||
| 54 | }; | ||
| 55 | |||
| 56 | static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = { | ||
| 57 | {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))}, | ||
| 58 | {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))}, | ||
| 59 | {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, | ||
| 60 | }; | ||
| 61 | |||
| 62 | static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map = { | ||
| 63 | {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, | ||
| 64 | {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, | ||
| 65 | {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, | ||
| 66 | }; | ||
| 67 | |||
| 25 | } // namespace ConfigurationShared | 68 | } // namespace ConfigurationShared |
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp index ea8d7add4..941683a43 100644 --- a/src/yuzu/configuration/shared_widget.cpp +++ b/src/yuzu/configuration/shared_widget.cpp | |||
| @@ -194,7 +194,7 @@ QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer, | |||
| 194 | return group; | 194 | return group; |
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | const auto get_selected = [=]() -> int { | 197 | const auto get_selected = [this]() -> int { |
| 198 | for (const auto& [id, button] : radio_buttons) { | 198 | for (const auto& [id, button] : radio_buttons) { |
| 199 | if (button->isChecked()) { | 199 | if (button->isChecked()) { |
| 200 | return id; | 200 | return id; |
| @@ -203,7 +203,7 @@ QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer, | |||
| 203 | return -1; | 203 | return -1; |
| 204 | }; | 204 | }; |
| 205 | 205 | ||
| 206 | const auto set_index = [=](u32 value) { | 206 | const auto set_index = [this](u32 value) { |
| 207 | for (const auto& [id, button] : radio_buttons) { | 207 | for (const auto& [id, button] : radio_buttons) { |
| 208 | button->setChecked(id == value); | 208 | button->setChecked(id == value); |
| 209 | } | 209 | } |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 7049c57b6..6d227ef8d 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -36,10 +36,8 @@ constexpr std::array<std::array<Qt::GlobalColor, 2>, 10> WaitTreeColors{{ | |||
| 36 | 36 | ||
| 37 | bool IsDarkTheme() { | 37 | bool IsDarkTheme() { |
| 38 | const auto& theme = UISettings::values.theme; | 38 | const auto& theme = UISettings::values.theme; |
| 39 | return theme == QStringLiteral("qdarkstyle") || | 39 | return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") || |
| 40 | theme == QStringLiteral("qdarkstyle_midnight_blue") || | 40 | theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue"); |
| 41 | theme == QStringLiteral("colorful_dark") || | ||
| 42 | theme == QStringLiteral("colorful_midnight_blue"); | ||
| 43 | } | 41 | } |
| 44 | 42 | ||
| 45 | } // namespace | 43 | } // namespace |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 7e7d8e252..59b317135 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -278,7 +278,7 @@ void GameList::OnUpdateThemedIcons() { | |||
| 278 | case GameListItemType::CustomDir: { | 278 | case GameListItemType::CustomDir: { |
| 279 | const UISettings::GameDir& game_dir = | 279 | const UISettings::GameDir& game_dir = |
| 280 | UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()]; | 280 | UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()]; |
| 281 | const QString icon_name = QFileInfo::exists(game_dir.path) | 281 | const QString icon_name = QFileInfo::exists(QString::fromStdString(game_dir.path)) |
| 282 | ? QStringLiteral("folder") | 282 | ? QStringLiteral("folder") |
| 283 | : QStringLiteral("bad_folder"); | 283 | : QStringLiteral("bad_folder"); |
| 284 | child->setData( | 284 | child->setData( |
| @@ -567,9 +567,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 567 | QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); | 567 | QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); |
| 568 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); | 568 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); |
| 569 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); | 569 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); |
| 570 | // TODO: Implement shortcut creation for macOS | ||
| 571 | #if !defined(__APPLE__) | ||
| 570 | QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); | 572 | QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); |
| 571 | QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); | 573 | QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); |
| 572 | #ifndef WIN32 | ||
| 573 | QAction* create_applications_menu_shortcut = | 574 | QAction* create_applications_menu_shortcut = |
| 574 | shortcut_menu->addAction(tr("Add to Applications Menu")); | 575 | shortcut_menu->addAction(tr("Add to Applications Menu")); |
| 575 | #endif | 576 | #endif |
| @@ -647,10 +648,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 647 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { | 648 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { |
| 648 | emit NavigateToGamedbEntryRequested(program_id, compatibility_list); | 649 | emit NavigateToGamedbEntryRequested(program_id, compatibility_list); |
| 649 | }); | 650 | }); |
| 651 | // TODO: Implement shortcut creation for macOS | ||
| 652 | #if !defined(__APPLE__) | ||
| 650 | connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { | 653 | connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { |
| 651 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); | 654 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); |
| 652 | }); | 655 | }); |
| 653 | #ifndef WIN32 | ||
| 654 | connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { | 656 | connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { |
| 655 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); | 657 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); |
| 656 | }); | 658 | }); |
| @@ -725,7 +727,8 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { | |||
| 725 | }); | 727 | }); |
| 726 | 728 | ||
| 727 | connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { | 729 | connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { |
| 728 | emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path); | 730 | emit OpenDirectory( |
| 731 | QString::fromStdString(UISettings::values.game_dirs[game_dir_index].path)); | ||
| 729 | }); | 732 | }); |
| 730 | } | 733 | } |
| 731 | 734 | ||
| @@ -867,7 +870,7 @@ const QStringList GameList::supported_file_extensions = { | |||
| 867 | QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; | 870 | QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; |
| 868 | 871 | ||
| 869 | void GameList::RefreshGameDirectory() { | 872 | void GameList::RefreshGameDirectory() { |
| 870 | if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { | 873 | if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { |
| 871 | LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); | 874 | LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); |
| 872 | PopulateAsync(UISettings::values.game_dirs); | 875 | PopulateAsync(UISettings::values.game_dirs); |
| 873 | } | 876 | } |
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 86a0c41d9..c330b574f 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h | |||
| @@ -286,13 +286,13 @@ public: | |||
| 286 | setData(QObject::tr("System Titles"), Qt::DisplayRole); | 286 | setData(QObject::tr("System Titles"), Qt::DisplayRole); |
| 287 | break; | 287 | break; |
| 288 | case GameListItemType::CustomDir: { | 288 | case GameListItemType::CustomDir: { |
| 289 | const QString icon_name = QFileInfo::exists(game_dir->path) | 289 | const QString path = QString::fromStdString(game_dir->path); |
| 290 | ? QStringLiteral("folder") | 290 | const QString icon_name = |
| 291 | : QStringLiteral("bad_folder"); | 291 | QFileInfo::exists(path) ? QStringLiteral("folder") : QStringLiteral("bad_folder"); |
| 292 | setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( | 292 | setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( |
| 293 | icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | 293 | icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), |
| 294 | Qt::DecorationRole); | 294 | Qt::DecorationRole); |
| 295 | setData(game_dir->path, Qt::DisplayRole); | 295 | setData(path, Qt::DisplayRole); |
| 296 | break; | 296 | break; |
| 297 | } | 297 | } |
| 298 | default: | 298 | default: |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 69be21027..dc006832e 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -456,29 +456,29 @@ void GameListWorker::run() { | |||
| 456 | break; | 456 | break; |
| 457 | } | 457 | } |
| 458 | 458 | ||
| 459 | if (game_dir.path == QStringLiteral("SDMC")) { | 459 | if (game_dir.path == std::string("SDMC")) { |
| 460 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); | 460 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); |
| 461 | DirEntryReady(game_list_dir); | 461 | DirEntryReady(game_list_dir); |
| 462 | AddTitlesToGameList(game_list_dir); | 462 | AddTitlesToGameList(game_list_dir); |
| 463 | } else if (game_dir.path == QStringLiteral("UserNAND")) { | 463 | } else if (game_dir.path == std::string("UserNAND")) { |
| 464 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); | 464 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); |
| 465 | DirEntryReady(game_list_dir); | 465 | DirEntryReady(game_list_dir); |
| 466 | AddTitlesToGameList(game_list_dir); | 466 | AddTitlesToGameList(game_list_dir); |
| 467 | } else if (game_dir.path == QStringLiteral("SysNAND")) { | 467 | } else if (game_dir.path == std::string("SysNAND")) { |
| 468 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); | 468 | auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); |
| 469 | DirEntryReady(game_list_dir); | 469 | DirEntryReady(game_list_dir); |
| 470 | AddTitlesToGameList(game_list_dir); | 470 | AddTitlesToGameList(game_list_dir); |
| 471 | } else { | 471 | } else { |
| 472 | watch_list.append(game_dir.path); | 472 | watch_list.append(QString::fromStdString(game_dir.path)); |
| 473 | auto* const game_list_dir = new GameListDir(game_dir); | 473 | auto* const game_list_dir = new GameListDir(game_dir); |
| 474 | DirEntryReady(game_list_dir); | 474 | DirEntryReady(game_list_dir); |
| 475 | ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), | 475 | ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan, |
| 476 | game_dir.deep_scan, game_list_dir); | 476 | game_list_dir); |
| 477 | ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), | 477 | ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan, |
| 478 | game_dir.deep_scan, game_list_dir); | 478 | game_list_dir); |
| 479 | } | 479 | } |
| 480 | } | 480 | } |
| 481 | 481 | ||
| 482 | RecordEvent([=](GameList* game_list) { game_list->DonePopulating(watch_list); }); | 482 | RecordEvent([this](GameList* game_list) { game_list->DonePopulating(watch_list); }); |
| 483 | processing_completed.Set(); | 483 | processing_completed.Set(); |
| 484 | } | 484 | } |
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp index 6530186c1..eebfbf155 100644 --- a/src/yuzu/hotkeys.cpp +++ b/src/yuzu/hotkeys.cpp | |||
| @@ -19,7 +19,7 @@ void HotkeyRegistry::SaveHotkeys() { | |||
| 19 | for (const auto& hotkey : group.second) { | 19 | for (const auto& hotkey : group.second) { |
| 20 | UISettings::values.shortcuts.push_back( | 20 | UISettings::values.shortcuts.push_back( |
| 21 | {hotkey.first, group.first, | 21 | {hotkey.first, group.first, |
| 22 | UISettings::ContextualShortcut({hotkey.second.keyseq.toString(), | 22 | UISettings::ContextualShortcut({hotkey.second.keyseq.toString().toStdString(), |
| 23 | hotkey.second.controller_keyseq, | 23 | hotkey.second.controller_keyseq, |
| 24 | hotkey.second.context, hotkey.second.repeat})}); | 24 | hotkey.second.context, hotkey.second.repeat})}); |
| 25 | } | 25 | } |
| @@ -31,12 +31,12 @@ void HotkeyRegistry::LoadHotkeys() { | |||
| 31 | // beginGroup() | 31 | // beginGroup() |
| 32 | for (auto shortcut : UISettings::values.shortcuts) { | 32 | for (auto shortcut : UISettings::values.shortcuts) { |
| 33 | Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; | 33 | Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; |
| 34 | if (!shortcut.shortcut.keyseq.isEmpty()) { | 34 | if (!shortcut.shortcut.keyseq.empty()) { |
| 35 | hk.keyseq = | 35 | hk.keyseq = QKeySequence::fromString(QString::fromStdString(shortcut.shortcut.keyseq), |
| 36 | QKeySequence::fromString(shortcut.shortcut.keyseq, QKeySequence::NativeText); | 36 | QKeySequence::NativeText); |
| 37 | hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context); | 37 | hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context); |
| 38 | } | 38 | } |
| 39 | if (!shortcut.shortcut.controller_keyseq.isEmpty()) { | 39 | if (!shortcut.shortcut.controller_keyseq.empty()) { |
| 40 | hk.controller_keyseq = shortcut.shortcut.controller_keyseq; | 40 | hk.controller_keyseq = shortcut.shortcut.controller_keyseq; |
| 41 | } | 41 | } |
| 42 | if (hk.shortcut) { | 42 | if (hk.shortcut) { |
| @@ -51,7 +51,8 @@ void HotkeyRegistry::LoadHotkeys() { | |||
| 51 | } | 51 | } |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { | 54 | QShortcut* HotkeyRegistry::GetHotkey(const std::string& group, const std::string& action, |
| 55 | QWidget* widget) { | ||
| 55 | Hotkey& hk = hotkey_groups[group][action]; | 56 | Hotkey& hk = hotkey_groups[group][action]; |
| 56 | 57 | ||
| 57 | if (!hk.shortcut) { | 58 | if (!hk.shortcut) { |
| @@ -62,7 +63,8 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action | |||
| 62 | return hk.shortcut; | 63 | return hk.shortcut; |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, const QString& action, | 66 | ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const std::string& group, |
| 67 | const std::string& action, | ||
| 66 | Core::HID::EmulatedController* controller) { | 68 | Core::HID::EmulatedController* controller) { |
| 67 | Hotkey& hk = hotkey_groups[group][action]; | 69 | Hotkey& hk = hotkey_groups[group][action]; |
| 68 | 70 | ||
| @@ -74,12 +76,12 @@ ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, co | |||
| 74 | return hk.controller_shortcut; | 76 | return hk.controller_shortcut; |
| 75 | } | 77 | } |
| 76 | 78 | ||
| 77 | QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) { | 79 | QKeySequence HotkeyRegistry::GetKeySequence(const std::string& group, const std::string& action) { |
| 78 | return hotkey_groups[group][action].keyseq; | 80 | return hotkey_groups[group][action].keyseq; |
| 79 | } | 81 | } |
| 80 | 82 | ||
| 81 | Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group, | 83 | Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const std::string& group, |
| 82 | const QString& action) { | 84 | const std::string& action) { |
| 83 | return hotkey_groups[group][action].context; | 85 | return hotkey_groups[group][action].context; |
| 84 | } | 86 | } |
| 85 | 87 | ||
| @@ -101,10 +103,10 @@ void ControllerShortcut::SetKey(const ControllerButtonSequence& buttons) { | |||
| 101 | button_sequence = buttons; | 103 | button_sequence = buttons; |
| 102 | } | 104 | } |
| 103 | 105 | ||
| 104 | void ControllerShortcut::SetKey(const QString& buttons_shortcut) { | 106 | void ControllerShortcut::SetKey(const std::string& buttons_shortcut) { |
| 105 | ControllerButtonSequence sequence{}; | 107 | ControllerButtonSequence sequence{}; |
| 106 | name = buttons_shortcut.toStdString(); | 108 | name = buttons_shortcut; |
| 107 | std::istringstream command_line(buttons_shortcut.toStdString()); | 109 | std::istringstream command_line(buttons_shortcut); |
| 108 | std::string line; | 110 | std::string line; |
| 109 | while (std::getline(command_line, line, '+')) { | 111 | while (std::getline(command_line, line, '+')) { |
| 110 | if (line.empty()) { | 112 | if (line.empty()) { |
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h index 56eee8d82..e11332d2e 100644 --- a/src/yuzu/hotkeys.h +++ b/src/yuzu/hotkeys.h | |||
| @@ -33,7 +33,7 @@ public: | |||
| 33 | ~ControllerShortcut(); | 33 | ~ControllerShortcut(); |
| 34 | 34 | ||
| 35 | void SetKey(const ControllerButtonSequence& buttons); | 35 | void SetKey(const ControllerButtonSequence& buttons); |
| 36 | void SetKey(const QString& buttons_shortcut); | 36 | void SetKey(const std::string& buttons_shortcut); |
| 37 | 37 | ||
| 38 | ControllerButtonSequence ButtonSequence() const; | 38 | ControllerButtonSequence ButtonSequence() const; |
| 39 | 39 | ||
| @@ -88,8 +88,8 @@ public: | |||
| 88 | * will be the same. Thus, you shouldn't rely on the caller really being the | 88 | * will be the same. Thus, you shouldn't rely on the caller really being the |
| 89 | * QShortcut's parent. | 89 | * QShortcut's parent. |
| 90 | */ | 90 | */ |
| 91 | QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); | 91 | QShortcut* GetHotkey(const std::string& group, const std::string& action, QWidget* widget); |
| 92 | ControllerShortcut* GetControllerHotkey(const QString& group, const QString& action, | 92 | ControllerShortcut* GetControllerHotkey(const std::string& group, const std::string& action, |
| 93 | Core::HID::EmulatedController* controller); | 93 | Core::HID::EmulatedController* controller); |
| 94 | 94 | ||
| 95 | /** | 95 | /** |
| @@ -98,7 +98,7 @@ public: | |||
| 98 | * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). | 98 | * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). |
| 99 | * @param action Name of the action (e.g. "Start Emulation", "Load Image"). | 99 | * @param action Name of the action (e.g. "Start Emulation", "Load Image"). |
| 100 | */ | 100 | */ |
| 101 | QKeySequence GetKeySequence(const QString& group, const QString& action); | 101 | QKeySequence GetKeySequence(const std::string& group, const std::string& action); |
| 102 | 102 | ||
| 103 | /** | 103 | /** |
| 104 | * Returns a Qt::ShortcutContext object who can be connected to other | 104 | * Returns a Qt::ShortcutContext object who can be connected to other |
| @@ -108,20 +108,20 @@ public: | |||
| 108 | * "Debugger"). | 108 | * "Debugger"). |
| 109 | * @param action Name of the action (e.g. "Start Emulation", "Load Image"). | 109 | * @param action Name of the action (e.g. "Start Emulation", "Load Image"). |
| 110 | */ | 110 | */ |
| 111 | Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action); | 111 | Qt::ShortcutContext GetShortcutContext(const std::string& group, const std::string& action); |
| 112 | 112 | ||
| 113 | private: | 113 | private: |
| 114 | struct Hotkey { | 114 | struct Hotkey { |
| 115 | QKeySequence keyseq; | 115 | QKeySequence keyseq; |
| 116 | QString controller_keyseq; | 116 | std::string controller_keyseq; |
| 117 | QShortcut* shortcut = nullptr; | 117 | QShortcut* shortcut = nullptr; |
| 118 | ControllerShortcut* controller_shortcut = nullptr; | 118 | ControllerShortcut* controller_shortcut = nullptr; |
| 119 | Qt::ShortcutContext context = Qt::WindowShortcut; | 119 | Qt::ShortcutContext context = Qt::WindowShortcut; |
| 120 | bool repeat; | 120 | bool repeat; |
| 121 | }; | 121 | }; |
| 122 | 122 | ||
| 123 | using HotkeyMap = std::map<QString, Hotkey>; | 123 | using HotkeyMap = std::map<std::string, Hotkey>; |
| 124 | using HotkeyGroupMap = std::map<QString, HotkeyMap>; | 124 | using HotkeyGroupMap = std::map<std::string, HotkeyMap>; |
| 125 | 125 | ||
| 126 | HotkeyGroupMap hotkey_groups; | 126 | HotkeyGroupMap hotkey_groups; |
| 127 | }; | 127 | }; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 0df163029..dcf68460a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <thread> | 10 | #include <thread> |
| 11 | #include "core/loader/nca.h" | 11 | #include "core/loader/nca.h" |
| 12 | #include "core/tools/renderdoc.h" | 12 | #include "core/tools/renderdoc.h" |
| 13 | |||
| 13 | #ifdef __APPLE__ | 14 | #ifdef __APPLE__ |
| 14 | #include <unistd.h> // for chdir | 15 | #include <unistd.h> // for chdir |
| 15 | #endif | 16 | #endif |
| @@ -46,6 +47,7 @@ | |||
| 46 | #include "core/hle/service/am/applet_ae.h" | 47 | #include "core/hle/service/am/applet_ae.h" |
| 47 | #include "core/hle/service/am/applet_oe.h" | 48 | #include "core/hle/service/am/applet_oe.h" |
| 48 | #include "core/hle/service/am/applets/applets.h" | 49 | #include "core/hle/service/am/applets/applets.h" |
| 50 | #include "core/hle/service/set/set_sys.h" | ||
| 49 | #include "yuzu/multiplayer/state.h" | 51 | #include "yuzu/multiplayer/state.h" |
| 50 | #include "yuzu/util/controller_navigation.h" | 52 | #include "yuzu/util/controller_navigation.h" |
| 51 | 53 | ||
| @@ -127,6 +129,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 127 | #include "core/loader/loader.h" | 129 | #include "core/loader/loader.h" |
| 128 | #include "core/perf_stats.h" | 130 | #include "core/perf_stats.h" |
| 129 | #include "core/telemetry_session.h" | 131 | #include "core/telemetry_session.h" |
| 132 | #include "frontend_common/config.h" | ||
| 130 | #include "input_common/drivers/tas_input.h" | 133 | #include "input_common/drivers/tas_input.h" |
| 131 | #include "input_common/drivers/virtual_amiibo.h" | 134 | #include "input_common/drivers/virtual_amiibo.h" |
| 132 | #include "input_common/main.h" | 135 | #include "input_common/main.h" |
| @@ -139,9 +142,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 139 | #include "yuzu/bootmanager.h" | 142 | #include "yuzu/bootmanager.h" |
| 140 | #include "yuzu/compatdb.h" | 143 | #include "yuzu/compatdb.h" |
| 141 | #include "yuzu/compatibility_list.h" | 144 | #include "yuzu/compatibility_list.h" |
| 142 | #include "yuzu/configuration/config.h" | ||
| 143 | #include "yuzu/configuration/configure_dialog.h" | 145 | #include "yuzu/configuration/configure_dialog.h" |
| 144 | #include "yuzu/configuration/configure_input_per_game.h" | 146 | #include "yuzu/configuration/configure_input_per_game.h" |
| 147 | #include "yuzu/configuration/qt_config.h" | ||
| 145 | #include "yuzu/debugger/console.h" | 148 | #include "yuzu/debugger/console.h" |
| 146 | #include "yuzu/debugger/controller.h" | 149 | #include "yuzu/debugger/controller.h" |
| 147 | #include "yuzu/debugger/profiler.h" | 150 | #include "yuzu/debugger/profiler.h" |
| @@ -310,7 +313,7 @@ bool GMainWindow::CheckDarkMode() { | |||
| 310 | #endif // __unix__ | 313 | #endif // __unix__ |
| 311 | } | 314 | } |
| 312 | 315 | ||
| 313 | GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan) | 316 | GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan) |
| 314 | : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, | 317 | : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, |
| 315 | input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, | 318 | input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, |
| 316 | vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, | 319 | vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, |
| @@ -675,7 +678,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers( | |||
| 675 | // Don't forget to apply settings. | 678 | // Don't forget to apply settings. |
| 676 | system->HIDCore().DisableAllControllerConfiguration(); | 679 | system->HIDCore().DisableAllControllerConfiguration(); |
| 677 | system->ApplySettings(); | 680 | system->ApplySettings(); |
| 678 | config->Save(); | 681 | config->SaveAllValues(); |
| 679 | 682 | ||
| 680 | UpdateStatusButtons(); | 683 | UpdateStatusButtons(); |
| 681 | 684 | ||
| @@ -1046,7 +1049,12 @@ void GMainWindow::InitializeWidgets() { | |||
| 1046 | statusBar()->addPermanentWidget(label); | 1049 | statusBar()->addPermanentWidget(label); |
| 1047 | } | 1050 | } |
| 1048 | 1051 | ||
| 1049 | // TODO (flTobi): Add the widget when multiplayer is fully implemented | 1052 | firmware_label = new QLabel(); |
| 1053 | firmware_label->setObjectName(QStringLiteral("FirmwareLabel")); | ||
| 1054 | firmware_label->setVisible(false); | ||
| 1055 | firmware_label->setFocusPolicy(Qt::NoFocus); | ||
| 1056 | statusBar()->addPermanentWidget(firmware_label); | ||
| 1057 | |||
| 1050 | statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); | 1058 | statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); |
| 1051 | statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); | 1059 | statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); |
| 1052 | 1060 | ||
| @@ -1064,12 +1072,6 @@ void GMainWindow::InitializeWidgets() { | |||
| 1064 | volume_slider->setObjectName(QStringLiteral("volume_slider")); | 1072 | volume_slider->setObjectName(QStringLiteral("volume_slider")); |
| 1065 | volume_slider->setMaximum(200); | 1073 | volume_slider->setMaximum(200); |
| 1066 | volume_slider->setPageStep(5); | 1074 | volume_slider->setPageStep(5); |
| 1067 | connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) { | ||
| 1068 | Settings::values.audio_muted = false; | ||
| 1069 | const auto volume = static_cast<u8>(percentage); | ||
| 1070 | Settings::values.volume.SetValue(volume); | ||
| 1071 | UpdateVolumeUI(); | ||
| 1072 | }); | ||
| 1073 | volume_popup->layout()->addWidget(volume_slider); | 1075 | volume_popup->layout()->addWidget(volume_slider); |
| 1074 | 1076 | ||
| 1075 | volume_button = new VolumeButton(); | 1077 | volume_button = new VolumeButton(); |
| @@ -1077,6 +1079,12 @@ void GMainWindow::InitializeWidgets() { | |||
| 1077 | volume_button->setFocusPolicy(Qt::NoFocus); | 1079 | volume_button->setFocusPolicy(Qt::NoFocus); |
| 1078 | volume_button->setCheckable(true); | 1080 | volume_button->setCheckable(true); |
| 1079 | UpdateVolumeUI(); | 1081 | UpdateVolumeUI(); |
| 1082 | connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) { | ||
| 1083 | Settings::values.audio_muted = false; | ||
| 1084 | const auto volume = static_cast<u8>(percentage); | ||
| 1085 | Settings::values.volume.SetValue(volume); | ||
| 1086 | UpdateVolumeUI(); | ||
| 1087 | }); | ||
| 1080 | connect(volume_button, &QPushButton::clicked, this, [&] { | 1088 | connect(volume_button, &QPushButton::clicked, this, [&] { |
| 1081 | UpdateVolumeUI(); | 1089 | UpdateVolumeUI(); |
| 1082 | volume_popup->setVisible(!volume_popup->isVisible()); | 1090 | volume_popup->setVisible(!volume_popup->isVisible()); |
| @@ -1128,7 +1136,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 1128 | connect(aa_status_button, &QPushButton::customContextMenuRequested, | 1136 | connect(aa_status_button, &QPushButton::customContextMenuRequested, |
| 1129 | [this](const QPoint& menu_location) { | 1137 | [this](const QPoint& menu_location) { |
| 1130 | QMenu context_menu; | 1138 | QMenu context_menu; |
| 1131 | for (auto const& aa_text_pair : Config::anti_aliasing_texts_map) { | 1139 | for (auto const& aa_text_pair : ConfigurationShared::anti_aliasing_texts_map) { |
| 1132 | context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] { | 1140 | context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] { |
| 1133 | Settings::values.anti_aliasing.SetValue(aa_text_pair.first); | 1141 | Settings::values.anti_aliasing.SetValue(aa_text_pair.first); |
| 1134 | UpdateAAText(); | 1142 | UpdateAAText(); |
| @@ -1152,7 +1160,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 1152 | connect(filter_status_button, &QPushButton::customContextMenuRequested, | 1160 | connect(filter_status_button, &QPushButton::customContextMenuRequested, |
| 1153 | [this](const QPoint& menu_location) { | 1161 | [this](const QPoint& menu_location) { |
| 1154 | QMenu context_menu; | 1162 | QMenu context_menu; |
| 1155 | for (auto const& filter_text_pair : Config::scaling_filter_texts_map) { | 1163 | for (auto const& filter_text_pair : ConfigurationShared::scaling_filter_texts_map) { |
| 1156 | context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] { | 1164 | context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] { |
| 1157 | Settings::values.scaling_filter.SetValue(filter_text_pair.first); | 1165 | Settings::values.scaling_filter.SetValue(filter_text_pair.first); |
| 1158 | UpdateFilterText(); | 1166 | UpdateFilterText(); |
| @@ -1175,7 +1183,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 1175 | [this](const QPoint& menu_location) { | 1183 | [this](const QPoint& menu_location) { |
| 1176 | QMenu context_menu; | 1184 | QMenu context_menu; |
| 1177 | 1185 | ||
| 1178 | for (auto const& pair : Config::use_docked_mode_texts_map) { | 1186 | for (auto const& pair : ConfigurationShared::use_docked_mode_texts_map) { |
| 1179 | context_menu.addAction(pair.second, [this, &pair] { | 1187 | context_menu.addAction(pair.second, [this, &pair] { |
| 1180 | if (pair.first != Settings::values.use_docked_mode.GetValue()) { | 1188 | if (pair.first != Settings::values.use_docked_mode.GetValue()) { |
| 1181 | OnToggleDockedMode(); | 1189 | OnToggleDockedMode(); |
| @@ -1199,7 +1207,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 1199 | [this](const QPoint& menu_location) { | 1207 | [this](const QPoint& menu_location) { |
| 1200 | QMenu context_menu; | 1208 | QMenu context_menu; |
| 1201 | 1209 | ||
| 1202 | for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) { | 1210 | for (auto const& gpu_accuracy_pair : ConfigurationShared::gpu_accuracy_texts_map) { |
| 1203 | if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) { | 1211 | if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) { |
| 1204 | continue; | 1212 | continue; |
| 1205 | } | 1213 | } |
| @@ -1228,7 +1236,8 @@ void GMainWindow::InitializeWidgets() { | |||
| 1228 | [this](const QPoint& menu_location) { | 1236 | [this](const QPoint& menu_location) { |
| 1229 | QMenu context_menu; | 1237 | QMenu context_menu; |
| 1230 | 1238 | ||
| 1231 | for (auto const& renderer_backend_pair : Config::renderer_backend_texts_map) { | 1239 | for (auto const& renderer_backend_pair : |
| 1240 | ConfigurationShared::renderer_backend_texts_map) { | ||
| 1232 | if (renderer_backend_pair.first == Settings::RendererBackend::Null) { | 1241 | if (renderer_backend_pair.first == Settings::RendererBackend::Null) { |
| 1233 | continue; | 1242 | continue; |
| 1234 | } | 1243 | } |
| @@ -1293,16 +1302,17 @@ void GMainWindow::InitializeRecentFileMenuActions() { | |||
| 1293 | 1302 | ||
| 1294 | void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name, | 1303 | void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name, |
| 1295 | const bool tas_allowed) { | 1304 | const bool tas_allowed) { |
| 1296 | static const QString main_window = QStringLiteral("Main Window"); | 1305 | static const auto main_window = std::string("Main Window"); |
| 1297 | action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); | 1306 | action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name.toStdString())); |
| 1298 | action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); | 1307 | action->setShortcutContext( |
| 1308 | hotkey_registry.GetShortcutContext(main_window, action_name.toStdString())); | ||
| 1299 | action->setAutoRepeat(false); | 1309 | action->setAutoRepeat(false); |
| 1300 | 1310 | ||
| 1301 | this->addAction(action); | 1311 | this->addAction(action); |
| 1302 | 1312 | ||
| 1303 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | 1313 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 1304 | const auto* controller_hotkey = | 1314 | const auto* controller_hotkey = |
| 1305 | hotkey_registry.GetControllerHotkey(main_window, action_name, controller); | 1315 | hotkey_registry.GetControllerHotkey(main_window, action_name.toStdString(), controller); |
| 1306 | connect( | 1316 | connect( |
| 1307 | controller_hotkey, &ControllerShortcut::Activated, this, | 1317 | controller_hotkey, &ControllerShortcut::Activated, this, |
| 1308 | [action, tas_allowed, this] { | 1318 | [action, tas_allowed, this] { |
| @@ -1334,10 +1344,11 @@ void GMainWindow::InitializeHotkeys() { | |||
| 1334 | 1344 | ||
| 1335 | static const QString main_window = QStringLiteral("Main Window"); | 1345 | static const QString main_window = QStringLiteral("Main Window"); |
| 1336 | const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { | 1346 | const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { |
| 1337 | const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this); | 1347 | const auto* hotkey = |
| 1348 | hotkey_registry.GetHotkey(main_window.toStdString(), action_name.toStdString(), this); | ||
| 1338 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | 1349 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 1339 | const auto* controller_hotkey = | 1350 | const auto* controller_hotkey = hotkey_registry.GetControllerHotkey( |
| 1340 | hotkey_registry.GetControllerHotkey(main_window, action_name, controller); | 1351 | main_window.toStdString(), action_name.toStdString(), controller); |
| 1341 | connect(hotkey, &QShortcut::activated, this, function); | 1352 | connect(hotkey, &QShortcut::activated, this, function); |
| 1342 | connect(controller_hotkey, &ControllerShortcut::Activated, this, function, | 1353 | connect(controller_hotkey, &ControllerShortcut::Activated, this, function, |
| 1343 | Qt::QueuedConnection); | 1354 | Qt::QueuedConnection); |
| @@ -1574,6 +1585,7 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 1574 | connect_menu(ui->action_Load_Cabinet_Formatter, | 1585 | connect_menu(ui->action_Load_Cabinet_Formatter, |
| 1575 | [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); }); | 1586 | [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); }); |
| 1576 | connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit); | 1587 | connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit); |
| 1588 | connect_menu(ui->action_Open_Controller_Menu, &GMainWindow::OnOpenControllerMenu); | ||
| 1577 | connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); | 1589 | connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); |
| 1578 | 1590 | ||
| 1579 | // TAS | 1591 | // TAS |
| @@ -1601,14 +1613,13 @@ void GMainWindow::UpdateMenuState() { | |||
| 1601 | ui->action_Pause, | 1613 | ui->action_Pause, |
| 1602 | }; | 1614 | }; |
| 1603 | 1615 | ||
| 1604 | const std::array applet_actions{ | 1616 | const std::array applet_actions{ui->action_Load_Album, |
| 1605 | ui->action_Load_Album, | 1617 | ui->action_Load_Cabinet_Nickname_Owner, |
| 1606 | ui->action_Load_Cabinet_Nickname_Owner, | 1618 | ui->action_Load_Cabinet_Eraser, |
| 1607 | ui->action_Load_Cabinet_Eraser, | 1619 | ui->action_Load_Cabinet_Restorer, |
| 1608 | ui->action_Load_Cabinet_Restorer, | 1620 | ui->action_Load_Cabinet_Formatter, |
| 1609 | ui->action_Load_Cabinet_Formatter, | 1621 | ui->action_Load_Mii_Edit, |
| 1610 | ui->action_Load_Mii_Edit, | 1622 | ui->action_Open_Controller_Menu}; |
| 1611 | }; | ||
| 1612 | 1623 | ||
| 1613 | for (QAction* action : running_actions) { | 1624 | for (QAction* action : running_actions) { |
| 1614 | action->setEnabled(emulation_running); | 1625 | action->setEnabled(emulation_running); |
| @@ -1908,12 +1919,16 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) { | |||
| 1908 | void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, | 1919 | void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, |
| 1909 | StartGameType type, AmLaunchType launch_type) { | 1920 | StartGameType type, AmLaunchType launch_type) { |
| 1910 | LOG_INFO(Frontend, "yuzu starting..."); | 1921 | LOG_INFO(Frontend, "yuzu starting..."); |
| 1911 | StoreRecentFile(filename); // Put the filename on top of the list | 1922 | |
| 1923 | if (program_id == 0 || | ||
| 1924 | program_id > static_cast<u64>(Service::AM::Applets::AppletProgramId::MaxProgramId)) { | ||
| 1925 | StoreRecentFile(filename); // Put the filename on top of the list | ||
| 1926 | } | ||
| 1912 | 1927 | ||
| 1913 | // Save configurations | 1928 | // Save configurations |
| 1914 | UpdateUISettings(); | 1929 | UpdateUISettings(); |
| 1915 | game_list->SaveInterfaceLayout(); | 1930 | game_list->SaveInterfaceLayout(); |
| 1916 | config->Save(); | 1931 | config->SaveAllValues(); |
| 1917 | 1932 | ||
| 1918 | u64 title_id{0}; | 1933 | u64 title_id{0}; |
| 1919 | 1934 | ||
| @@ -1931,7 +1946,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t | |||
| 1931 | const auto config_file_name = title_id == 0 | 1946 | const auto config_file_name = title_id == 0 |
| 1932 | ? Common::FS::PathToUTF8String(file_path.filename()) | 1947 | ? Common::FS::PathToUTF8String(file_path.filename()) |
| 1933 | : fmt::format("{:016X}", title_id); | 1948 | : fmt::format("{:016X}", title_id); |
| 1934 | Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); | 1949 | QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig); |
| 1935 | system->HIDCore().ReloadInputDevices(); | 1950 | system->HIDCore().ReloadInputDevices(); |
| 1936 | system->ApplySettings(); | 1951 | system->ApplySettings(); |
| 1937 | } | 1952 | } |
| @@ -2156,6 +2171,10 @@ void GMainWindow::OnEmulationStopped() { | |||
| 2156 | emu_frametime_label->setVisible(false); | 2171 | emu_frametime_label->setVisible(false); |
| 2157 | renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); | 2172 | renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); |
| 2158 | 2173 | ||
| 2174 | if (!firmware_label->text().isEmpty()) { | ||
| 2175 | firmware_label->setVisible(true); | ||
| 2176 | } | ||
| 2177 | |||
| 2159 | current_game_path.clear(); | 2178 | current_game_path.clear(); |
| 2160 | 2179 | ||
| 2161 | // When closing the game, destroy the GLWindow to clear the context after the game is closed | 2180 | // When closing the game, destroy the GLWindow to clear the context after the game is closed |
| @@ -2174,6 +2193,7 @@ void GMainWindow::ShutdownGame() { | |||
| 2174 | return; | 2193 | return; |
| 2175 | } | 2194 | } |
| 2176 | 2195 | ||
| 2196 | play_time_manager->Stop(); | ||
| 2177 | OnShutdownBegin(); | 2197 | OnShutdownBegin(); |
| 2178 | OnEmulationStopTimeExpired(); | 2198 | OnEmulationStopTimeExpired(); |
| 2179 | OnEmulationStopped(); | 2199 | OnEmulationStopped(); |
| @@ -2737,7 +2757,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 2737 | return; | 2757 | return; |
| 2738 | } | 2758 | } |
| 2739 | 2759 | ||
| 2740 | const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); | 2760 | const auto extracted = FileSys::ExtractRomFS(romfs); |
| 2741 | if (extracted == nullptr) { | 2761 | if (extracted == nullptr) { |
| 2742 | failed(); | 2762 | failed(); |
| 2743 | return; | 2763 | return; |
| @@ -2842,170 +2862,259 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, | |||
| 2842 | QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); | 2862 | QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); |
| 2843 | } | 2863 | } |
| 2844 | 2864 | ||
| 2845 | void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, | 2865 | bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, |
| 2846 | GameListShortcutTarget target) { | 2866 | const std::string& comment, |
| 2847 | // Get path to yuzu executable | 2867 | const std::filesystem::path& icon_path, |
| 2848 | const QStringList args = QApplication::arguments(); | 2868 | const std::filesystem::path& command, |
| 2849 | std::filesystem::path yuzu_command = args[0].toStdString(); | 2869 | const std::string& arguments, const std::string& categories, |
| 2850 | 2870 | const std::string& keywords, const std::string& name) try { | |
| 2851 | // If relative path, make it an absolute path | 2871 | #if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD |
| 2852 | if (yuzu_command.c_str()[0] == '.') { | 2872 | std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop"); |
| 2853 | yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; | 2873 | std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc); |
| 2874 | if (!shortcut_stream.is_open()) { | ||
| 2875 | LOG_ERROR(Frontend, "Failed to create shortcut"); | ||
| 2876 | return false; | ||
| 2854 | } | 2877 | } |
| 2855 | 2878 | // TODO: Migrate fmt::print to std::print in futures STD C++ 23. | |
| 2856 | #if defined(__linux__) | 2879 | fmt::print(shortcut_stream, "[Desktop Entry]\n"); |
| 2857 | // Warn once if we are making a shortcut to a volatile AppImage | 2880 | fmt::print(shortcut_stream, "Type=Application\n"); |
| 2858 | const std::string appimage_ending = | 2881 | fmt::print(shortcut_stream, "Version=1.0\n"); |
| 2859 | std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); | 2882 | fmt::print(shortcut_stream, "Name={}\n", name); |
| 2860 | if (yuzu_command.string().ends_with(appimage_ending) && | 2883 | if (!comment.empty()) { |
| 2861 | !UISettings::values.shortcut_already_warned) { | 2884 | fmt::print(shortcut_stream, "Comment={}\n", comment); |
| 2862 | if (QMessageBox::warning(this, tr("Create Shortcut"), | 2885 | } |
| 2863 | tr("This will create a shortcut to the current AppImage. This may " | 2886 | if (std::filesystem::is_regular_file(icon_path)) { |
| 2864 | "not work well if you update. Continue?"), | 2887 | fmt::print(shortcut_stream, "Icon={}\n", icon_path.string()); |
| 2865 | QMessageBox::StandardButton::Ok | | 2888 | } |
| 2866 | QMessageBox::StandardButton::Cancel) == | 2889 | fmt::print(shortcut_stream, "TryExec={}\n", command.string()); |
| 2867 | QMessageBox::StandardButton::Cancel) { | 2890 | fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments); |
| 2868 | return; | 2891 | if (!categories.empty()) { |
| 2892 | fmt::print(shortcut_stream, "Categories={}\n", categories); | ||
| 2893 | } | ||
| 2894 | if (!keywords.empty()) { | ||
| 2895 | fmt::print(shortcut_stream, "Keywords={}\n", keywords); | ||
| 2896 | } | ||
| 2897 | return true; | ||
| 2898 | #elif defined(_WIN32) // Windows | ||
| 2899 | HRESULT hr = CoInitialize(nullptr); | ||
| 2900 | if (FAILED(hr)) { | ||
| 2901 | LOG_ERROR(Frontend, "CoInitialize failed"); | ||
| 2902 | return false; | ||
| 2903 | } | ||
| 2904 | SCOPE_EXIT({ CoUninitialize(); }); | ||
| 2905 | IShellLinkW* ps1 = nullptr; | ||
| 2906 | IPersistFile* persist_file = nullptr; | ||
| 2907 | SCOPE_EXIT({ | ||
| 2908 | if (persist_file != nullptr) { | ||
| 2909 | persist_file->Release(); | ||
| 2869 | } | 2910 | } |
| 2870 | UISettings::values.shortcut_already_warned = true; | 2911 | if (ps1 != nullptr) { |
| 2912 | ps1->Release(); | ||
| 2913 | } | ||
| 2914 | }); | ||
| 2915 | HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW, | ||
| 2916 | reinterpret_cast<void**>(&ps1)); | ||
| 2917 | if (FAILED(hres)) { | ||
| 2918 | LOG_ERROR(Frontend, "Failed to create IShellLinkW instance"); | ||
| 2919 | return false; | ||
| 2871 | } | 2920 | } |
| 2872 | #endif // __linux__ | 2921 | hres = ps1->SetPath(command.c_str()); |
| 2873 | 2922 | if (FAILED(hres)) { | |
| 2874 | std::filesystem::path target_directory{}; | 2923 | LOG_ERROR(Frontend, "Failed to set path"); |
| 2875 | 2924 | return false; | |
| 2876 | switch (target) { | ||
| 2877 | case GameListShortcutTarget::Desktop: { | ||
| 2878 | const QString desktop_path = | ||
| 2879 | QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); | ||
| 2880 | target_directory = desktop_path.toUtf8().toStdString(); | ||
| 2881 | break; | ||
| 2882 | } | 2925 | } |
| 2883 | case GameListShortcutTarget::Applications: { | 2926 | if (!arguments.empty()) { |
| 2884 | const QString applications_path = | 2927 | hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data()); |
| 2885 | QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); | 2928 | if (FAILED(hres)) { |
| 2886 | if (applications_path.isEmpty()) { | 2929 | LOG_ERROR(Frontend, "Failed to set arguments"); |
| 2887 | const char* home = std::getenv("HOME"); | 2930 | return false; |
| 2888 | if (home != nullptr) { | ||
| 2889 | target_directory = std::filesystem::path(home) / ".local/share/applications"; | ||
| 2890 | } | ||
| 2891 | } else { | ||
| 2892 | target_directory = applications_path.toUtf8().toStdString(); | ||
| 2893 | } | 2931 | } |
| 2894 | break; | ||
| 2895 | } | 2932 | } |
| 2896 | default: | 2933 | if (!comment.empty()) { |
| 2897 | return; | 2934 | hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data()); |
| 2935 | if (FAILED(hres)) { | ||
| 2936 | LOG_ERROR(Frontend, "Failed to set description"); | ||
| 2937 | return false; | ||
| 2938 | } | ||
| 2898 | } | 2939 | } |
| 2899 | 2940 | if (std::filesystem::is_regular_file(icon_path)) { | |
| 2900 | const QDir dir(QString::fromStdString(target_directory.generic_string())); | 2941 | hres = ps1->SetIconLocation(icon_path.c_str(), 0); |
| 2901 | if (!dir.exists()) { | 2942 | if (FAILED(hres)) { |
| 2902 | QMessageBox::critical(this, tr("Create Shortcut"), | 2943 | LOG_ERROR(Frontend, "Failed to set icon location"); |
| 2903 | tr("Cannot create shortcut. Path \"%1\" does not exist.") | 2944 | return false; |
| 2904 | .arg(QString::fromStdString(target_directory.generic_string())), | 2945 | } |
| 2905 | QMessageBox::StandardButton::Ok); | ||
| 2906 | return; | ||
| 2907 | } | 2946 | } |
| 2947 | hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&persist_file)); | ||
| 2948 | if (FAILED(hres)) { | ||
| 2949 | LOG_ERROR(Frontend, "Failed to get IPersistFile interface"); | ||
| 2950 | return false; | ||
| 2951 | } | ||
| 2952 | hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE); | ||
| 2953 | if (FAILED(hres)) { | ||
| 2954 | LOG_ERROR(Frontend, "Failed to save shortcut"); | ||
| 2955 | return false; | ||
| 2956 | } | ||
| 2957 | return true; | ||
| 2958 | #else // Unsupported platform | ||
| 2959 | return false; | ||
| 2960 | #endif | ||
| 2961 | } catch (const std::exception& e) { | ||
| 2962 | LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what()); | ||
| 2963 | return false; | ||
| 2964 | } | ||
| 2965 | // Messages in pre-defined message boxes for less code spaghetti | ||
| 2966 | bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) { | ||
| 2967 | int result = 0; | ||
| 2968 | QMessageBox::StandardButtons buttons; | ||
| 2969 | switch (imsg) { | ||
| 2970 | case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES: | ||
| 2971 | buttons = QMessageBox::Yes | QMessageBox::No; | ||
| 2972 | result = | ||
| 2973 | QMessageBox::information(parent, tr("Create Shortcut"), | ||
| 2974 | tr("Do you want to launch the game in fullscreen?"), buttons); | ||
| 2975 | return result == QMessageBox::Yes; | ||
| 2976 | case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS: | ||
| 2977 | QMessageBox::information(parent, tr("Create Shortcut"), | ||
| 2978 | tr("Successfully created a shortcut to %1").arg(game_title)); | ||
| 2979 | return false; | ||
| 2980 | case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING: | ||
| 2981 | buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel; | ||
| 2982 | result = | ||
| 2983 | QMessageBox::warning(this, tr("Create Shortcut"), | ||
| 2984 | tr("This will create a shortcut to the current AppImage. This may " | ||
| 2985 | "not work well if you update. Continue?"), | ||
| 2986 | buttons); | ||
| 2987 | return result == QMessageBox::Ok; | ||
| 2988 | default: | ||
| 2989 | buttons = QMessageBox::Ok; | ||
| 2990 | QMessageBox::critical(parent, tr("Create Shortcut"), | ||
| 2991 | tr("Failed to create a shortcut to %1").arg(game_title), buttons); | ||
| 2992 | return false; | ||
| 2993 | } | ||
| 2994 | } | ||
| 2908 | 2995 | ||
| 2909 | const std::string game_file_name = std::filesystem::path(game_path).filename().string(); | 2996 | bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, |
| 2910 | // Determine full paths for icon and shortcut | 2997 | std::filesystem::path& out_icon_path) { |
| 2911 | #if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) | 2998 | // Get path to Yuzu icons directory & icon extension |
| 2912 | const char* home = std::getenv("HOME"); | 2999 | std::string ico_extension = "png"; |
| 2913 | const std::filesystem::path home_path = (home == nullptr ? "~" : home); | 3000 | #if defined(_WIN32) |
| 2914 | const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); | 3001 | out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::IconsDir); |
| 2915 | 3002 | ico_extension = "ico"; | |
| 2916 | std::filesystem::path system_icons_path = | 3003 | #elif defined(__linux__) || defined(__FreeBSD__) |
| 2917 | (xdg_data_home == nullptr ? home_path / ".local/share/" | 3004 | out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; |
| 2918 | : std::filesystem::path(xdg_data_home)) / | 3005 | #endif |
| 2919 | "icons/hicolor/256x256"; | 3006 | // Create icons directory if it doesn't exist |
| 2920 | if (!Common::FS::CreateDirs(system_icons_path)) { | 3007 | if (!Common::FS::CreateDirs(out_icon_path)) { |
| 2921 | QMessageBox::critical( | 3008 | QMessageBox::critical( |
| 2922 | this, tr("Create Icon"), | 3009 | this, tr("Create Icon"), |
| 2923 | tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") | 3010 | tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") |
| 2924 | .arg(QString::fromStdString(system_icons_path)), | 3011 | .arg(QString::fromStdString(out_icon_path.string())), |
| 2925 | QMessageBox::StandardButton::Ok); | 3012 | QMessageBox::StandardButton::Ok); |
| 2926 | return; | 3013 | out_icon_path.clear(); |
| 3014 | return false; | ||
| 2927 | } | 3015 | } |
| 2928 | std::filesystem::path icon_path = | ||
| 2929 | system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name) | ||
| 2930 | : fmt::format("yuzu-{:016X}.png", program_id)); | ||
| 2931 | const std::filesystem::path shortcut_path = | ||
| 2932 | target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) | ||
| 2933 | : fmt::format("yuzu-{:016X}.desktop", program_id)); | ||
| 2934 | #elif defined(WIN32) | ||
| 2935 | std::filesystem::path icons_path = | ||
| 2936 | Common::FS::GetYuzuPathString(Common::FS::YuzuPath::IconsDir); | ||
| 2937 | std::filesystem::path icon_path = | ||
| 2938 | icons_path / ((program_id == 0 ? fmt::format("yuzu-{}.ico", game_file_name) | ||
| 2939 | : fmt::format("yuzu-{:016X}.ico", program_id))); | ||
| 2940 | #else | ||
| 2941 | std::string icon_extension; | ||
| 2942 | #endif | ||
| 2943 | |||
| 2944 | // Get title from game file | ||
| 2945 | const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), | ||
| 2946 | system->GetContentProvider()}; | ||
| 2947 | const auto control = pm.GetControlMetadata(); | ||
| 2948 | const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); | ||
| 2949 | 3016 | ||
| 2950 | std::string title{fmt::format("{:016X}", program_id)}; | 3017 | // Create icon file path |
| 2951 | 3018 | out_icon_path /= (program_id == 0 ? fmt::format("yuzu-{}.{}", game_file_name, ico_extension) | |
| 2952 | if (control.first != nullptr) { | 3019 | : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension)); |
| 2953 | title = control.first->GetApplicationName(); | 3020 | return true; |
| 2954 | } else { | 3021 | } |
| 2955 | loader->ReadTitle(title); | ||
| 2956 | } | ||
| 2957 | 3022 | ||
| 2958 | // Get icon from game file | 3023 | void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, |
| 2959 | std::vector<u8> icon_image_file{}; | 3024 | GameListShortcutTarget target) { |
| 2960 | if (control.second != nullptr) { | 3025 | std::string game_title; |
| 2961 | icon_image_file = control.second->ReadAllBytes(); | 3026 | QString qt_game_title; |
| 2962 | } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { | 3027 | std::filesystem::path out_icon_path; |
| 2963 | LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); | 3028 | // Get path to yuzu executable |
| 3029 | const QStringList args = QApplication::arguments(); | ||
| 3030 | std::filesystem::path yuzu_command = args[0].toStdString(); | ||
| 3031 | // If relative path, make it an absolute path | ||
| 3032 | if (yuzu_command.c_str()[0] == '.') { | ||
| 3033 | yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; | ||
| 2964 | } | 3034 | } |
| 2965 | 3035 | // Shortcut path | |
| 2966 | QImage icon_data = | 3036 | std::filesystem::path shortcut_path{}; |
| 2967 | QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); | 3037 | if (target == GameListShortcutTarget::Desktop) { |
| 2968 | #if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) | 3038 | shortcut_path = |
| 2969 | // Convert and write the icon as a PNG | 3039 | QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString(); |
| 2970 | if (!icon_data.save(QString::fromStdString(icon_path.string()))) { | 3040 | } else if (target == GameListShortcutTarget::Applications) { |
| 2971 | LOG_ERROR(Frontend, "Could not write icon as PNG to file"); | 3041 | shortcut_path = |
| 3042 | QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString(); | ||
| 3043 | } | ||
| 3044 | // Icon path and title | ||
| 3045 | if (std::filesystem::exists(shortcut_path)) { | ||
| 3046 | // Get title from game file | ||
| 3047 | const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), | ||
| 3048 | system->GetContentProvider()}; | ||
| 3049 | const auto control = pm.GetControlMetadata(); | ||
| 3050 | const auto loader = | ||
| 3051 | Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); | ||
| 3052 | game_title = fmt::format("{:016X}", program_id); | ||
| 3053 | if (control.first != nullptr) { | ||
| 3054 | game_title = control.first->GetApplicationName(); | ||
| 3055 | } else { | ||
| 3056 | loader->ReadTitle(game_title); | ||
| 3057 | } | ||
| 3058 | // Delete illegal characters from title | ||
| 3059 | const std::string illegal_chars = "<>:\"/\\|?*."; | ||
| 3060 | for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) { | ||
| 3061 | if (illegal_chars.find(*it) != std::string::npos) { | ||
| 3062 | game_title.erase(it.base() - 1); | ||
| 3063 | } | ||
| 3064 | } | ||
| 3065 | qt_game_title = QString::fromStdString(game_title); | ||
| 3066 | // Get icon from game file | ||
| 3067 | std::vector<u8> icon_image_file{}; | ||
| 3068 | if (control.second != nullptr) { | ||
| 3069 | icon_image_file = control.second->ReadAllBytes(); | ||
| 3070 | } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { | ||
| 3071 | LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); | ||
| 3072 | } | ||
| 3073 | QImage icon_data = | ||
| 3074 | QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); | ||
| 3075 | if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) { | ||
| 3076 | if (!SaveIconToFile(out_icon_path, icon_data)) { | ||
| 3077 | LOG_ERROR(Frontend, "Could not write icon to file"); | ||
| 3078 | } | ||
| 3079 | } | ||
| 2972 | } else { | 3080 | } else { |
| 2973 | LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); | 3081 | GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, |
| 2974 | } | 3082 | qt_game_title); |
| 2975 | #elif defined(WIN32) | 3083 | LOG_ERROR(Frontend, "Invalid shortcut target"); |
| 2976 | if (!SaveIconToFile(icon_path.string(), icon_data)) { | ||
| 2977 | LOG_ERROR(Frontend, "Could not write icon to file"); | ||
| 2978 | return; | 3084 | return; |
| 2979 | } | 3085 | } |
| 3086 | #if defined(__linux__) | ||
| 3087 | // Special case for AppImages | ||
| 3088 | // Warn once if we are making a shortcut to a volatile AppImage | ||
| 3089 | const std::string appimage_ending = | ||
| 3090 | std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); | ||
| 3091 | if (yuzu_command.string().ends_with(appimage_ending) && | ||
| 3092 | !UISettings::values.shortcut_already_warned) { | ||
| 3093 | if (GMainWindow::CreateShortcutMessagesGUI( | ||
| 3094 | this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qt_game_title)) { | ||
| 3095 | return; | ||
| 3096 | } | ||
| 3097 | UISettings::values.shortcut_already_warned = true; | ||
| 3098 | } | ||
| 2980 | #endif // __linux__ | 3099 | #endif // __linux__ |
| 2981 | 3100 | // Create shortcut | |
| 2982 | #ifdef _WIN32 | 3101 | std::string arguments = fmt::format("-g \"{:s}\"", game_path); |
| 2983 | // Replace characters that are illegal in Windows filenames by a dash | 3102 | if (GMainWindow::CreateShortcutMessagesGUI( |
| 2984 | const std::string illegal_chars = "<>:\"/\\|?*"; | 3103 | this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) { |
| 2985 | for (char c : illegal_chars) { | 3104 | arguments = "-f " + arguments; |
| 2986 | std::replace(title.begin(), title.end(), c, '_'); | ||
| 2987 | } | 3105 | } |
| 2988 | const std::filesystem::path shortcut_path = target_directory / (title + ".lnk").c_str(); | 3106 | const std::string comment = fmt::format("Start {:s} with the yuzu Emulator", game_title); |
| 2989 | #endif | ||
| 2990 | |||
| 2991 | const std::string comment = | ||
| 2992 | tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); | ||
| 2993 | const std::string arguments = fmt::format("-g \"{:s}\"", game_path); | ||
| 2994 | const std::string categories = "Game;Emulator;Qt;"; | 3107 | const std::string categories = "Game;Emulator;Qt;"; |
| 2995 | const std::string keywords = "Switch;Nintendo;"; | 3108 | const std::string keywords = "Switch;Nintendo;"; |
| 2996 | 3109 | ||
| 2997 | if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), | 3110 | if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, yuzu_command, |
| 2998 | yuzu_command.string(), arguments, categories, keywords)) { | 3111 | arguments, categories, keywords, game_title)) { |
| 2999 | QMessageBox::critical(this, tr("Create Shortcut"), | 3112 | GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS, |
| 3000 | tr("Failed to create a shortcut at %1") | 3113 | qt_game_title); |
| 3001 | .arg(QString::fromStdString(shortcut_path.string()))); | ||
| 3002 | return; | 3114 | return; |
| 3003 | } | 3115 | } |
| 3004 | 3116 | GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, | |
| 3005 | LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string()); | 3117 | qt_game_title); |
| 3006 | QMessageBox::information( | ||
| 3007 | this, tr("Create Shortcut"), | ||
| 3008 | tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title))); | ||
| 3009 | } | 3118 | } |
| 3010 | 3119 | ||
| 3011 | void GMainWindow::OnGameListOpenDirectory(const QString& directory) { | 3120 | void GMainWindow::OnGameListOpenDirectory(const QString& directory) { |
| @@ -3040,7 +3149,7 @@ void GMainWindow::OnGameListAddDirectory() { | |||
| 3040 | return; | 3149 | return; |
| 3041 | } | 3150 | } |
| 3042 | 3151 | ||
| 3043 | UISettings::GameDir game_dir{dir_path, false, true}; | 3152 | UISettings::GameDir game_dir{dir_path.toStdString(), false, true}; |
| 3044 | if (!UISettings::values.game_dirs.contains(game_dir)) { | 3153 | if (!UISettings::values.game_dirs.contains(game_dir)) { |
| 3045 | UISettings::values.game_dirs.append(game_dir); | 3154 | UISettings::values.game_dirs.append(game_dir); |
| 3046 | game_list->PopulateAsync(UISettings::values.game_dirs); | 3155 | game_list->PopulateAsync(UISettings::values.game_dirs); |
| @@ -3086,14 +3195,14 @@ void GMainWindow::OnMenuLoadFile() { | |||
| 3086 | "%1 is an identifier for the Switch executable file extensions.") | 3195 | "%1 is an identifier for the Switch executable file extensions.") |
| 3087 | .arg(extensions); | 3196 | .arg(extensions); |
| 3088 | const QString filename = QFileDialog::getOpenFileName( | 3197 | const QString filename = QFileDialog::getOpenFileName( |
| 3089 | this, tr("Load File"), UISettings::values.roms_path, file_filter); | 3198 | this, tr("Load File"), QString::fromStdString(UISettings::values.roms_path), file_filter); |
| 3090 | is_load_file_select_active = false; | 3199 | is_load_file_select_active = false; |
| 3091 | 3200 | ||
| 3092 | if (filename.isEmpty()) { | 3201 | if (filename.isEmpty()) { |
| 3093 | return; | 3202 | return; |
| 3094 | } | 3203 | } |
| 3095 | 3204 | ||
| 3096 | UISettings::values.roms_path = QFileInfo(filename).path(); | 3205 | UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); |
| 3097 | BootGame(filename); | 3206 | BootGame(filename); |
| 3098 | } | 3207 | } |
| 3099 | 3208 | ||
| @@ -3126,7 +3235,8 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 3126 | "Image (*.xci)"); | 3235 | "Image (*.xci)"); |
| 3127 | 3236 | ||
| 3128 | QStringList filenames = QFileDialog::getOpenFileNames( | 3237 | QStringList filenames = QFileDialog::getOpenFileNames( |
| 3129 | this, tr("Install Files"), UISettings::values.roms_path, file_filter); | 3238 | this, tr("Install Files"), QString::fromStdString(UISettings::values.roms_path), |
| 3239 | file_filter); | ||
| 3130 | 3240 | ||
| 3131 | if (filenames.isEmpty()) { | 3241 | if (filenames.isEmpty()) { |
| 3132 | return; | 3242 | return; |
| @@ -3144,7 +3254,7 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 3144 | } | 3254 | } |
| 3145 | 3255 | ||
| 3146 | // Save folder location of the first selected file | 3256 | // Save folder location of the first selected file |
| 3147 | UISettings::values.roms_path = QFileInfo(filenames[0]).path(); | 3257 | UISettings::values.roms_path = QFileInfo(filenames[0]).path().toStdString(); |
| 3148 | 3258 | ||
| 3149 | int remaining = filenames.size(); | 3259 | int remaining = filenames.size(); |
| 3150 | 3260 | ||
| @@ -3484,12 +3594,12 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) { | |||
| 3484 | } | 3594 | } |
| 3485 | 3595 | ||
| 3486 | void GMainWindow::OnExit() { | 3596 | void GMainWindow::OnExit() { |
| 3487 | OnStopGame(); | 3597 | ShutdownGame(); |
| 3488 | } | 3598 | } |
| 3489 | 3599 | ||
| 3490 | void GMainWindow::OnSaveConfig() { | 3600 | void GMainWindow::OnSaveConfig() { |
| 3491 | system->ApplySettings(); | 3601 | system->ApplySettings(); |
| 3492 | config->Save(); | 3602 | config->SaveAllValues(); |
| 3493 | } | 3603 | } |
| 3494 | 3604 | ||
| 3495 | void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { | 3605 | void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { |
| @@ -3745,7 +3855,7 @@ void GMainWindow::OnConfigure() { | |||
| 3745 | 3855 | ||
| 3746 | Settings::values.disabled_addons.clear(); | 3856 | Settings::values.disabled_addons.clear(); |
| 3747 | 3857 | ||
| 3748 | config = std::make_unique<Config>(); | 3858 | config = std::make_unique<QtConfig>(); |
| 3749 | UISettings::values.reset_to_defaults = false; | 3859 | UISettings::values.reset_to_defaults = false; |
| 3750 | 3860 | ||
| 3751 | UISettings::values.game_dirs = std::move(old_game_dirs); | 3861 | UISettings::values.game_dirs = std::move(old_game_dirs); |
| @@ -3780,7 +3890,7 @@ void GMainWindow::OnConfigure() { | |||
| 3780 | 3890 | ||
| 3781 | UISettings::values.configuration_applied = false; | 3891 | UISettings::values.configuration_applied = false; |
| 3782 | 3892 | ||
| 3783 | config->Save(); | 3893 | config->SaveAllValues(); |
| 3784 | 3894 | ||
| 3785 | if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { | 3895 | if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { |
| 3786 | render_window->installEventFilter(render_window); | 3896 | render_window->installEventFilter(render_window); |
| @@ -3996,68 +4106,8 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file | |||
| 3996 | UISettings::values.configuration_applied = false; | 4106 | UISettings::values.configuration_applied = false; |
| 3997 | 4107 | ||
| 3998 | if (!is_powered_on) { | 4108 | if (!is_powered_on) { |
| 3999 | config->Save(); | 4109 | config->SaveAllValues(); |
| 4000 | } | ||
| 4001 | } | ||
| 4002 | |||
| 4003 | bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title, | ||
| 4004 | const std::string& comment, const std::string& icon_path, | ||
| 4005 | const std::string& command, const std::string& arguments, | ||
| 4006 | const std::string& categories, const std::string& keywords) { | ||
| 4007 | #if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) | ||
| 4008 | // This desktop file template was writing referencing | ||
| 4009 | // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html | ||
| 4010 | std::string shortcut_contents{}; | ||
| 4011 | shortcut_contents.append("[Desktop Entry]\n"); | ||
| 4012 | shortcut_contents.append("Type=Application\n"); | ||
| 4013 | shortcut_contents.append("Version=1.0\n"); | ||
| 4014 | shortcut_contents.append(fmt::format("Name={:s}\n", title)); | ||
| 4015 | shortcut_contents.append(fmt::format("Comment={:s}\n", comment)); | ||
| 4016 | shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path)); | ||
| 4017 | shortcut_contents.append(fmt::format("TryExec={:s}\n", command)); | ||
| 4018 | shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments)); | ||
| 4019 | shortcut_contents.append(fmt::format("Categories={:s}\n", categories)); | ||
| 4020 | shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords)); | ||
| 4021 | |||
| 4022 | std::ofstream shortcut_stream(shortcut_path); | ||
| 4023 | if (!shortcut_stream.is_open()) { | ||
| 4024 | LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path); | ||
| 4025 | return false; | ||
| 4026 | } | 4110 | } |
| 4027 | shortcut_stream << shortcut_contents; | ||
| 4028 | shortcut_stream.close(); | ||
| 4029 | |||
| 4030 | return true; | ||
| 4031 | #elif defined(WIN32) | ||
| 4032 | IShellLinkW* shell_link; | ||
| 4033 | auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, | ||
| 4034 | (void**)&shell_link); | ||
| 4035 | if (FAILED(hres)) { | ||
| 4036 | return false; | ||
| 4037 | } | ||
| 4038 | shell_link->SetPath( | ||
| 4039 | Common::UTF8ToUTF16W(command).data()); // Path to the object we are referring to | ||
| 4040 | shell_link->SetArguments(Common::UTF8ToUTF16W(arguments).data()); | ||
| 4041 | shell_link->SetDescription(Common::UTF8ToUTF16W(comment).data()); | ||
| 4042 | shell_link->SetIconLocation(Common::UTF8ToUTF16W(icon_path).data(), 0); | ||
| 4043 | |||
| 4044 | IPersistFile* persist_file; | ||
| 4045 | hres = shell_link->QueryInterface(IID_IPersistFile, (void**)&persist_file); | ||
| 4046 | if (FAILED(hres)) { | ||
| 4047 | return false; | ||
| 4048 | } | ||
| 4049 | |||
| 4050 | hres = persist_file->Save(Common::UTF8ToUTF16W(shortcut_path).data(), TRUE); | ||
| 4051 | if (FAILED(hres)) { | ||
| 4052 | return false; | ||
| 4053 | } | ||
| 4054 | |||
| 4055 | persist_file->Release(); | ||
| 4056 | shell_link->Release(); | ||
| 4057 | |||
| 4058 | return true; | ||
| 4059 | #endif | ||
| 4060 | return false; | ||
| 4061 | } | 4111 | } |
| 4062 | 4112 | ||
| 4063 | void GMainWindow::OnLoadAmiibo() { | 4113 | void GMainWindow::OnLoadAmiibo() { |
| @@ -4098,7 +4148,6 @@ void GMainWindow::OnLoadAmiibo() { | |||
| 4098 | bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text, | 4148 | bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text, |
| 4099 | QMessageBox::StandardButtons buttons, | 4149 | QMessageBox::StandardButtons buttons, |
| 4100 | QMessageBox::StandardButton defaultButton) { | 4150 | QMessageBox::StandardButton defaultButton) { |
| 4101 | |||
| 4102 | QMessageBox* box_dialog = new QMessageBox(parent); | 4151 | QMessageBox* box_dialog = new QMessageBox(parent); |
| 4103 | box_dialog->setWindowTitle(title); | 4152 | box_dialog->setWindowTitle(title); |
| 4104 | box_dialog->setText(text); | 4153 | box_dialog->setText(text); |
| @@ -4272,7 +4321,7 @@ void GMainWindow::OnToggleStatusBar() { | |||
| 4272 | } | 4321 | } |
| 4273 | 4322 | ||
| 4274 | void GMainWindow::OnAlbum() { | 4323 | void GMainWindow::OnAlbum() { |
| 4275 | constexpr u64 AlbumId = 0x010000000000100Dull; | 4324 | constexpr u64 AlbumId = static_cast<u64>(Service::AM::Applets::AppletProgramId::PhotoViewer); |
| 4276 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); | 4325 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); |
| 4277 | if (!bis_system) { | 4326 | if (!bis_system) { |
| 4278 | QMessageBox::warning(this, tr("No firmware available"), | 4327 | QMessageBox::warning(this, tr("No firmware available"), |
| @@ -4290,12 +4339,12 @@ void GMainWindow::OnAlbum() { | |||
| 4290 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); | 4339 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); |
| 4291 | 4340 | ||
| 4292 | const auto filename = QString::fromStdString(album_nca->GetFullPath()); | 4341 | const auto filename = QString::fromStdString(album_nca->GetFullPath()); |
| 4293 | UISettings::values.roms_path = QFileInfo(filename).path(); | 4342 | UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); |
| 4294 | BootGame(filename); | 4343 | BootGame(filename, AlbumId); |
| 4295 | } | 4344 | } |
| 4296 | 4345 | ||
| 4297 | void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { | 4346 | void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { |
| 4298 | constexpr u64 CabinetId = 0x0100000000001002ull; | 4347 | constexpr u64 CabinetId = static_cast<u64>(Service::AM::Applets::AppletProgramId::Cabinet); |
| 4299 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); | 4348 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); |
| 4300 | if (!bis_system) { | 4349 | if (!bis_system) { |
| 4301 | QMessageBox::warning(this, tr("No firmware available"), | 4350 | QMessageBox::warning(this, tr("No firmware available"), |
| @@ -4314,12 +4363,12 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { | |||
| 4314 | system->GetAppletManager().SetCabinetMode(mode); | 4363 | system->GetAppletManager().SetCabinetMode(mode); |
| 4315 | 4364 | ||
| 4316 | const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); | 4365 | const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); |
| 4317 | UISettings::values.roms_path = QFileInfo(filename).path(); | 4366 | UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); |
| 4318 | BootGame(filename); | 4367 | BootGame(filename, CabinetId); |
| 4319 | } | 4368 | } |
| 4320 | 4369 | ||
| 4321 | void GMainWindow::OnMiiEdit() { | 4370 | void GMainWindow::OnMiiEdit() { |
| 4322 | constexpr u64 MiiEditId = 0x0100000000001009ull; | 4371 | constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); |
| 4323 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); | 4372 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); |
| 4324 | if (!bis_system) { | 4373 | if (!bis_system) { |
| 4325 | QMessageBox::warning(this, tr("No firmware available"), | 4374 | QMessageBox::warning(this, tr("No firmware available"), |
| @@ -4337,8 +4386,33 @@ void GMainWindow::OnMiiEdit() { | |||
| 4337 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); | 4386 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); |
| 4338 | 4387 | ||
| 4339 | const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); | 4388 | const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); |
| 4340 | UISettings::values.roms_path = QFileInfo(filename).path(); | 4389 | UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); |
| 4341 | BootGame(filename); | 4390 | BootGame(filename, MiiEditId); |
| 4391 | } | ||
| 4392 | |||
| 4393 | void GMainWindow::OnOpenControllerMenu() { | ||
| 4394 | constexpr u64 ControllerAppletId = | ||
| 4395 | static_cast<u64>(Service::AM::Applets::AppletProgramId::Controller); | ||
| 4396 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); | ||
| 4397 | if (!bis_system) { | ||
| 4398 | QMessageBox::warning(this, tr("No firmware available"), | ||
| 4399 | tr("Please install the firmware to use the Controller Menu.")); | ||
| 4400 | return; | ||
| 4401 | } | ||
| 4402 | |||
| 4403 | auto controller_applet_nca = | ||
| 4404 | bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program); | ||
| 4405 | if (!controller_applet_nca) { | ||
| 4406 | QMessageBox::warning(this, tr("Controller Applet"), | ||
| 4407 | tr("Controller Menu is not available. Please reinstall firmware.")); | ||
| 4408 | return; | ||
| 4409 | } | ||
| 4410 | |||
| 4411 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller); | ||
| 4412 | |||
| 4413 | const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath())); | ||
| 4414 | UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); | ||
| 4415 | BootGame(filename, ControllerAppletId); | ||
| 4342 | } | 4416 | } |
| 4343 | 4417 | ||
| 4344 | void GMainWindow::OnCaptureScreenshot() { | 4418 | void GMainWindow::OnCaptureScreenshot() { |
| @@ -4527,11 +4601,13 @@ void GMainWindow::UpdateStatusBar() { | |||
| 4527 | emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); | 4601 | emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); |
| 4528 | game_fps_label->setVisible(true); | 4602 | game_fps_label->setVisible(true); |
| 4529 | emu_frametime_label->setVisible(true); | 4603 | emu_frametime_label->setVisible(true); |
| 4604 | firmware_label->setVisible(false); | ||
| 4530 | } | 4605 | } |
| 4531 | 4606 | ||
| 4532 | void GMainWindow::UpdateGPUAccuracyButton() { | 4607 | void GMainWindow::UpdateGPUAccuracyButton() { |
| 4533 | const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); | 4608 | const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); |
| 4534 | const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second; | 4609 | const auto gpu_accuracy_text = |
| 4610 | ConfigurationShared::gpu_accuracy_texts_map.find(gpu_accuracy)->second; | ||
| 4535 | gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); | 4611 | gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); |
| 4536 | gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal); | 4612 | gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal); |
| 4537 | } | 4613 | } |
| @@ -4540,31 +4616,32 @@ void GMainWindow::UpdateDockedButton() { | |||
| 4540 | const auto console_mode = Settings::values.use_docked_mode.GetValue(); | 4616 | const auto console_mode = Settings::values.use_docked_mode.GetValue(); |
| 4541 | dock_status_button->setChecked(Settings::IsDockedMode()); | 4617 | dock_status_button->setChecked(Settings::IsDockedMode()); |
| 4542 | dock_status_button->setText( | 4618 | dock_status_button->setText( |
| 4543 | Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); | 4619 | ConfigurationShared::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); |
| 4544 | } | 4620 | } |
| 4545 | 4621 | ||
| 4546 | void GMainWindow::UpdateAPIText() { | 4622 | void GMainWindow::UpdateAPIText() { |
| 4547 | const auto api = Settings::values.renderer_backend.GetValue(); | 4623 | const auto api = Settings::values.renderer_backend.GetValue(); |
| 4548 | const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second; | 4624 | const auto renderer_status_text = |
| 4625 | ConfigurationShared::renderer_backend_texts_map.find(api)->second; | ||
| 4549 | renderer_status_button->setText( | 4626 | renderer_status_button->setText( |
| 4550 | api == Settings::RendererBackend::OpenGL | 4627 | api == Settings::RendererBackend::OpenGL |
| 4551 | ? tr("%1 %2").arg( | 4628 | ? tr("%1 %2").arg(renderer_status_text.toUpper(), |
| 4552 | renderer_status_text.toUpper(), | 4629 | ConfigurationShared::shader_backend_texts_map |
| 4553 | Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue()) | 4630 | .find(Settings::values.shader_backend.GetValue()) |
| 4554 | ->second) | 4631 | ->second) |
| 4555 | : renderer_status_text.toUpper()); | 4632 | : renderer_status_text.toUpper()); |
| 4556 | } | 4633 | } |
| 4557 | 4634 | ||
| 4558 | void GMainWindow::UpdateFilterText() { | 4635 | void GMainWindow::UpdateFilterText() { |
| 4559 | const auto filter = Settings::values.scaling_filter.GetValue(); | 4636 | const auto filter = Settings::values.scaling_filter.GetValue(); |
| 4560 | const auto filter_text = Config::scaling_filter_texts_map.find(filter)->second; | 4637 | const auto filter_text = ConfigurationShared::scaling_filter_texts_map.find(filter)->second; |
| 4561 | filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR") | 4638 | filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR") |
| 4562 | : filter_text.toUpper()); | 4639 | : filter_text.toUpper()); |
| 4563 | } | 4640 | } |
| 4564 | 4641 | ||
| 4565 | void GMainWindow::UpdateAAText() { | 4642 | void GMainWindow::UpdateAAText() { |
| 4566 | const auto aa_mode = Settings::values.anti_aliasing.GetValue(); | 4643 | const auto aa_mode = Settings::values.anti_aliasing.GetValue(); |
| 4567 | const auto aa_text = Config::anti_aliasing_texts_map.find(aa_mode)->second; | 4644 | const auto aa_text = ConfigurationShared::anti_aliasing_texts_map.find(aa_mode)->second; |
| 4568 | aa_status_button->setText(aa_mode == Settings::AntiAliasing::None | 4645 | aa_status_button->setText(aa_mode == Settings::AntiAliasing::None |
| 4569 | ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA")) | 4646 | ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA")) |
| 4570 | : aa_text.toUpper()); | 4647 | : aa_text.toUpper()); |
| @@ -4744,6 +4821,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 4744 | "games.")); | 4821 | "games.")); |
| 4745 | } | 4822 | } |
| 4746 | 4823 | ||
| 4824 | SetFirmwareVersion(); | ||
| 4825 | |||
| 4747 | if (behavior == ReinitializeKeyBehavior::Warning) { | 4826 | if (behavior == ReinitializeKeyBehavior::Warning) { |
| 4748 | game_list->PopulateAsync(UISettings::values.game_dirs); | 4827 | game_list->PopulateAsync(UISettings::values.game_dirs); |
| 4749 | } | 4828 | } |
| @@ -4771,7 +4850,7 @@ bool GMainWindow::CheckSystemArchiveDecryption() { | |||
| 4771 | } | 4850 | } |
| 4772 | 4851 | ||
| 4773 | bool GMainWindow::CheckFirmwarePresence() { | 4852 | bool GMainWindow::CheckFirmwarePresence() { |
| 4774 | constexpr u64 MiiEditId = 0x0100000000001009ull; | 4853 | constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); |
| 4775 | 4854 | ||
| 4776 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); | 4855 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); |
| 4777 | if (!bis_system) { | 4856 | if (!bis_system) { |
| @@ -4786,6 +4865,28 @@ bool GMainWindow::CheckFirmwarePresence() { | |||
| 4786 | return true; | 4865 | return true; |
| 4787 | } | 4866 | } |
| 4788 | 4867 | ||
| 4868 | void GMainWindow::SetFirmwareVersion() { | ||
| 4869 | Service::Set::FirmwareVersionFormat firmware_data{}; | ||
| 4870 | const auto result = Service::Set::GetFirmwareVersionImpl( | ||
| 4871 | firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2); | ||
| 4872 | |||
| 4873 | if (result.IsError() || !CheckFirmwarePresence()) { | ||
| 4874 | LOG_INFO(Frontend, "Installed firmware: No firmware available"); | ||
| 4875 | firmware_label->setVisible(false); | ||
| 4876 | return; | ||
| 4877 | } | ||
| 4878 | |||
| 4879 | firmware_label->setVisible(true); | ||
| 4880 | |||
| 4881 | const std::string display_version(firmware_data.display_version.data()); | ||
| 4882 | const std::string display_title(firmware_data.display_title.data()); | ||
| 4883 | |||
| 4884 | LOG_INFO(Frontend, "Installed firmware: {}", display_title); | ||
| 4885 | |||
| 4886 | firmware_label->setText(QString::fromStdString(display_version)); | ||
| 4887 | firmware_label->setToolTip(QString::fromStdString(display_title)); | ||
| 4888 | } | ||
| 4889 | |||
| 4789 | bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, | 4890 | bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, |
| 4790 | u64* selected_title_id, u8* selected_content_record_type) { | 4891 | u64* selected_title_id, u8* selected_content_record_type) { |
| 4791 | using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>; | 4892 | using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>; |
| @@ -4847,7 +4948,12 @@ bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installe | |||
| 4847 | } | 4948 | } |
| 4848 | 4949 | ||
| 4849 | bool GMainWindow::ConfirmClose() { | 4950 | bool GMainWindow::ConfirmClose() { |
| 4850 | if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { | 4951 | if (emu_thread == nullptr || |
| 4952 | UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) { | ||
| 4953 | return true; | ||
| 4954 | } | ||
| 4955 | if (!system->GetExitLocked() && | ||
| 4956 | UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) { | ||
| 4851 | return true; | 4957 | return true; |
| 4852 | } | 4958 | } |
| 4853 | const auto text = tr("Are you sure you want to close yuzu?"); | 4959 | const auto text = tr("Are you sure you want to close yuzu?"); |
| @@ -4862,6 +4968,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 4862 | 4968 | ||
| 4863 | UpdateUISettings(); | 4969 | UpdateUISettings(); |
| 4864 | game_list->SaveInterfaceLayout(); | 4970 | game_list->SaveInterfaceLayout(); |
| 4971 | UISettings::SaveWindowState(); | ||
| 4865 | hotkey_registry.SaveHotkeys(); | 4972 | hotkey_registry.SaveHotkeys(); |
| 4866 | 4973 | ||
| 4867 | // Unload controllers early | 4974 | // Unload controllers early |
| @@ -4952,7 +5059,7 @@ bool GMainWindow::ConfirmChangeGame() { | |||
| 4952 | } | 5059 | } |
| 4953 | 5060 | ||
| 4954 | bool GMainWindow::ConfirmForceLockedExit() { | 5061 | bool GMainWindow::ConfirmForceLockedExit() { |
| 4955 | if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { | 5062 | if (emu_thread == nullptr) { |
| 4956 | return true; | 5063 | return true; |
| 4957 | } | 5064 | } |
| 4958 | const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" | 5065 | const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" |
| @@ -5016,9 +5123,9 @@ static void AdjustLinkColor() { | |||
| 5016 | } | 5123 | } |
| 5017 | 5124 | ||
| 5018 | void GMainWindow::UpdateUITheme() { | 5125 | void GMainWindow::UpdateUITheme() { |
| 5019 | const QString default_theme = | 5126 | const QString default_theme = QString::fromUtf8( |
| 5020 | QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second); | 5127 | UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second); |
| 5021 | QString current_theme = UISettings::values.theme; | 5128 | QString current_theme = QString::fromStdString(UISettings::values.theme); |
| 5022 | 5129 | ||
| 5023 | if (current_theme.isEmpty()) { | 5130 | if (current_theme.isEmpty()) { |
| 5024 | current_theme = default_theme; | 5131 | current_theme = default_theme; |
| @@ -5046,7 +5153,7 @@ void GMainWindow::UpdateUITheme() { | |||
| 5046 | QFile f(theme_uri); | 5153 | QFile f(theme_uri); |
| 5047 | if (!f.open(QFile::ReadOnly | QFile::Text)) { | 5154 | if (!f.open(QFile::ReadOnly | QFile::Text)) { |
| 5048 | LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme", | 5155 | LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme", |
| 5049 | UISettings::values.theme.toStdString()); | 5156 | UISettings::values.theme); |
| 5050 | current_theme = default_theme; | 5157 | current_theme = default_theme; |
| 5051 | } | 5158 | } |
| 5052 | } | 5159 | } |
| @@ -5059,7 +5166,7 @@ void GMainWindow::UpdateUITheme() { | |||
| 5059 | setStyleSheet(ts.readAll()); | 5166 | setStyleSheet(ts.readAll()); |
| 5060 | } else { | 5167 | } else { |
| 5061 | LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found", | 5168 | LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found", |
| 5062 | UISettings::values.theme.toStdString()); | 5169 | UISettings::values.theme); |
| 5063 | qApp->setStyleSheet({}); | 5170 | qApp->setStyleSheet({}); |
| 5064 | setStyleSheet({}); | 5171 | setStyleSheet({}); |
| 5065 | } | 5172 | } |
| @@ -5068,27 +5175,28 @@ void GMainWindow::UpdateUITheme() { | |||
| 5068 | void GMainWindow::LoadTranslation() { | 5175 | void GMainWindow::LoadTranslation() { |
| 5069 | bool loaded; | 5176 | bool loaded; |
| 5070 | 5177 | ||
| 5071 | if (UISettings::values.language.isEmpty()) { | 5178 | if (UISettings::values.language.empty()) { |
| 5072 | // If the selected language is empty, use system locale | 5179 | // If the selected language is empty, use system locale |
| 5073 | loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); | 5180 | loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); |
| 5074 | } else { | 5181 | } else { |
| 5075 | // Otherwise load from the specified file | 5182 | // Otherwise load from the specified file |
| 5076 | loaded = translator.load(UISettings::values.language, QStringLiteral(":/languages/")); | 5183 | loaded = translator.load(QString::fromStdString(UISettings::values.language), |
| 5184 | QStringLiteral(":/languages/")); | ||
| 5077 | } | 5185 | } |
| 5078 | 5186 | ||
| 5079 | if (loaded) { | 5187 | if (loaded) { |
| 5080 | qApp->installTranslator(&translator); | 5188 | qApp->installTranslator(&translator); |
| 5081 | } else { | 5189 | } else { |
| 5082 | UISettings::values.language = QStringLiteral("en"); | 5190 | UISettings::values.language = std::string("en"); |
| 5083 | } | 5191 | } |
| 5084 | } | 5192 | } |
| 5085 | 5193 | ||
| 5086 | void GMainWindow::OnLanguageChanged(const QString& locale) { | 5194 | void GMainWindow::OnLanguageChanged(const QString& locale) { |
| 5087 | if (UISettings::values.language != QStringLiteral("en")) { | 5195 | if (UISettings::values.language != std::string("en")) { |
| 5088 | qApp->removeTranslator(&translator); | 5196 | qApp->removeTranslator(&translator); |
| 5089 | } | 5197 | } |
| 5090 | 5198 | ||
| 5091 | UISettings::values.language = locale; | 5199 | UISettings::values.language = locale.toStdString(); |
| 5092 | LoadTranslation(); | 5200 | LoadTranslation(); |
| 5093 | ui->retranslateUi(this); | 5201 | ui->retranslateUi(this); |
| 5094 | multiplayer_state->retranslateUi(); | 5202 | multiplayer_state->retranslateUi(); |
| @@ -5114,7 +5222,7 @@ void GMainWindow::changeEvent(QEvent* event) { | |||
| 5114 | // UpdateUITheme is a decent work around | 5222 | // UpdateUITheme is a decent work around |
| 5115 | if (event->type() == QEvent::PaletteChange) { | 5223 | if (event->type() == QEvent::PaletteChange) { |
| 5116 | const QPalette test_palette(qApp->palette()); | 5224 | const QPalette test_palette(qApp->palette()); |
| 5117 | const QString current_theme = UISettings::values.theme; | 5225 | const QString current_theme = QString::fromStdString(UISettings::values.theme); |
| 5118 | // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too | 5226 | // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too |
| 5119 | static QColor last_window_color; | 5227 | static QColor last_window_color; |
| 5120 | const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); | 5228 | const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); |
| @@ -5208,7 +5316,8 @@ static void SetHighDPIAttributes() { | |||
| 5208 | } | 5316 | } |
| 5209 | 5317 | ||
| 5210 | int main(int argc, char* argv[]) { | 5318 | int main(int argc, char* argv[]) { |
| 5211 | std::unique_ptr<Config> config = std::make_unique<Config>(); | 5319 | std::unique_ptr<QtConfig> config = std::make_unique<QtConfig>(); |
| 5320 | UISettings::RestoreWindowState(config); | ||
| 5212 | bool has_broken_vulkan = false; | 5321 | bool has_broken_vulkan = false; |
| 5213 | bool is_child = false; | 5322 | bool is_child = false; |
| 5214 | if (CheckEnvVars(&is_child)) { | 5323 | if (CheckEnvVars(&is_child)) { |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index f9c6efe4f..e99d58995 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <optional> | 7 | #include <optional> |
| 8 | 8 | ||
| 9 | #include <filesystem> | ||
| 9 | #include <QMainWindow> | 10 | #include <QMainWindow> |
| 10 | #include <QMessageBox> | 11 | #include <QMessageBox> |
| 11 | #include <QPushButton> | 12 | #include <QPushButton> |
| @@ -14,6 +15,7 @@ | |||
| 14 | 15 | ||
| 15 | #include "common/announce_multiplayer_room.h" | 16 | #include "common/announce_multiplayer_room.h" |
| 16 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 18 | #include "configuration/qt_config.h" | ||
| 17 | #include "input_common/drivers/tas_input.h" | 19 | #include "input_common/drivers/tas_input.h" |
| 18 | #include "yuzu/compatibility_list.h" | 20 | #include "yuzu/compatibility_list.h" |
| 19 | #include "yuzu/hotkeys.h" | 21 | #include "yuzu/hotkeys.h" |
| @@ -25,7 +27,7 @@ | |||
| 25 | #include <QtDBus/QtDBus> | 27 | #include <QtDBus/QtDBus> |
| 26 | #endif | 28 | #endif |
| 27 | 29 | ||
| 28 | class Config; | 30 | class QtConfig; |
| 29 | class ClickableLabel; | 31 | class ClickableLabel; |
| 30 | class EmuThread; | 32 | class EmuThread; |
| 31 | class GameList; | 33 | class GameList; |
| @@ -174,10 +176,17 @@ class GMainWindow : public QMainWindow { | |||
| 174 | UI_EMU_STOPPING, | 176 | UI_EMU_STOPPING, |
| 175 | }; | 177 | }; |
| 176 | 178 | ||
| 179 | enum { | ||
| 180 | CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, | ||
| 181 | CREATE_SHORTCUT_MSGBOX_SUCCESS, | ||
| 182 | CREATE_SHORTCUT_MSGBOX_ERROR, | ||
| 183 | CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, | ||
| 184 | }; | ||
| 185 | |||
| 177 | public: | 186 | public: |
| 178 | void filterBarSetChecked(bool state); | 187 | void filterBarSetChecked(bool state); |
| 179 | void UpdateUITheme(); | 188 | void UpdateUITheme(); |
| 180 | explicit GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan); | 189 | explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan); |
| 181 | ~GMainWindow() override; | 190 | ~GMainWindow() override; |
| 182 | 191 | ||
| 183 | bool DropAction(QDropEvent* event); | 192 | bool DropAction(QDropEvent* event); |
| @@ -402,6 +411,7 @@ private slots: | |||
| 402 | void OnAlbum(); | 411 | void OnAlbum(); |
| 403 | void OnCabinet(Service::NFP::CabinetMode mode); | 412 | void OnCabinet(Service::NFP::CabinetMode mode); |
| 404 | void OnMiiEdit(); | 413 | void OnMiiEdit(); |
| 414 | void OnOpenControllerMenu(); | ||
| 405 | void OnCaptureScreenshot(); | 415 | void OnCaptureScreenshot(); |
| 406 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); | 416 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); |
| 407 | void OnLanguageChanged(const QString& locale); | 417 | void OnLanguageChanged(const QString& locale); |
| @@ -448,6 +458,7 @@ private: | |||
| 448 | bool CheckDarkMode(); | 458 | bool CheckDarkMode(); |
| 449 | bool CheckSystemArchiveDecryption(); | 459 | bool CheckSystemArchiveDecryption(); |
| 450 | bool CheckFirmwarePresence(); | 460 | bool CheckFirmwarePresence(); |
| 461 | void SetFirmwareVersion(); | ||
| 451 | void ConfigureFilesystemProvider(const std::string& filepath); | 462 | void ConfigureFilesystemProvider(const std::string& filepath); |
| 452 | /** | 463 | /** |
| 453 | * Open (or not) the right confirm dialog based on current setting and game exit lock | 464 | * Open (or not) the right confirm dialog based on current setting and game exit lock |
| @@ -456,11 +467,14 @@ private: | |||
| 456 | bool ConfirmShutdownGame(); | 467 | bool ConfirmShutdownGame(); |
| 457 | 468 | ||
| 458 | QString GetTasStateDescription() const; | 469 | QString GetTasStateDescription() const; |
| 459 | bool CreateShortcut(const std::string& shortcut_path, const std::string& title, | 470 | bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title); |
| 460 | const std::string& comment, const std::string& icon_path, | 471 | bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, |
| 461 | const std::string& command, const std::string& arguments, | 472 | std::filesystem::path& out_icon_path); |
| 462 | const std::string& categories, const std::string& keywords); | 473 | bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, |
| 463 | 474 | const std::filesystem::path& icon_path, | |
| 475 | const std::filesystem::path& command, const std::string& arguments, | ||
| 476 | const std::string& categories, const std::string& keywords, | ||
| 477 | const std::string& name); | ||
| 464 | /** | 478 | /** |
| 465 | * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog | 479 | * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog |
| 466 | * The only difference is that it returns a boolean. | 480 | * The only difference is that it returns a boolean. |
| @@ -499,6 +513,7 @@ private: | |||
| 499 | QLabel* game_fps_label = nullptr; | 513 | QLabel* game_fps_label = nullptr; |
| 500 | QLabel* emu_frametime_label = nullptr; | 514 | QLabel* emu_frametime_label = nullptr; |
| 501 | QLabel* tas_label = nullptr; | 515 | QLabel* tas_label = nullptr; |
| 516 | QLabel* firmware_label = nullptr; | ||
| 502 | QPushButton* gpu_accuracy_button = nullptr; | 517 | QPushButton* gpu_accuracy_button = nullptr; |
| 503 | QPushButton* renderer_status_button = nullptr; | 518 | QPushButton* renderer_status_button = nullptr; |
| 504 | QPushButton* dock_status_button = nullptr; | 519 | QPushButton* dock_status_button = nullptr; |
| @@ -509,7 +524,7 @@ private: | |||
| 509 | QSlider* volume_slider = nullptr; | 524 | QSlider* volume_slider = nullptr; |
| 510 | QTimer status_bar_update_timer; | 525 | QTimer status_bar_update_timer; |
| 511 | 526 | ||
| 512 | std::unique_ptr<Config> config; | 527 | std::unique_ptr<QtConfig> config; |
| 513 | 528 | ||
| 514 | // Whether emulation is currently running in yuzu. | 529 | // Whether emulation is currently running in yuzu. |
| 515 | bool emulation_running = false; | 530 | bool emulation_running = false; |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 88684ffb5..e53f9951e 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -25,7 +25,7 @@ | |||
| 25 | </property> | 25 | </property> |
| 26 | <widget class="QWidget" name="centralwidget"> | 26 | <widget class="QWidget" name="centralwidget"> |
| 27 | <layout class="QHBoxLayout" name="horizontalLayout"> | 27 | <layout class="QHBoxLayout" name="horizontalLayout"> |
| 28 | <property name="margin"> | 28 | <property name="margin" stdset="0"> |
| 29 | <number>0</number> | 29 | <number>0</number> |
| 30 | </property> | 30 | </property> |
| 31 | </layout> | 31 | </layout> |
| @@ -36,7 +36,7 @@ | |||
| 36 | <x>0</x> | 36 | <x>0</x> |
| 37 | <y>0</y> | 37 | <y>0</y> |
| 38 | <width>1280</width> | 38 | <width>1280</width> |
| 39 | <height>26</height> | 39 | <height>21</height> |
| 40 | </rect> | 40 | </rect> |
| 41 | </property> | 41 | </property> |
| 42 | <widget class="QMenu" name="menu_File"> | 42 | <widget class="QMenu" name="menu_File"> |
| @@ -162,6 +162,7 @@ | |||
| 162 | <addaction name="menu_cabinet_applet"/> | 162 | <addaction name="menu_cabinet_applet"/> |
| 163 | <addaction name="action_Load_Album"/> | 163 | <addaction name="action_Load_Album"/> |
| 164 | <addaction name="action_Load_Mii_Edit"/> | 164 | <addaction name="action_Load_Mii_Edit"/> |
| 165 | <addaction name="action_Open_Controller_Menu"/> | ||
| 165 | <addaction name="separator"/> | 166 | <addaction name="separator"/> |
| 166 | <addaction name="action_Capture_Screenshot"/> | 167 | <addaction name="action_Capture_Screenshot"/> |
| 167 | <addaction name="menuTAS"/> | 168 | <addaction name="menuTAS"/> |
| @@ -382,9 +383,9 @@ | |||
| 382 | </property> | 383 | </property> |
| 383 | </action> | 384 | </action> |
| 384 | <action name="action_Load_Album"> | 385 | <action name="action_Load_Album"> |
| 385 | <property name="text"> | 386 | <property name="text"> |
| 386 | <string>Open &Album</string> | 387 | <string>Open &Album</string> |
| 387 | </property> | 388 | </property> |
| 388 | </action> | 389 | </action> |
| 389 | <action name="action_Load_Cabinet_Nickname_Owner"> | 390 | <action name="action_Load_Cabinet_Nickname_Owner"> |
| 390 | <property name="text"> | 391 | <property name="text"> |
| @@ -407,9 +408,9 @@ | |||
| 407 | </property> | 408 | </property> |
| 408 | </action> | 409 | </action> |
| 409 | <action name="action_Load_Mii_Edit"> | 410 | <action name="action_Load_Mii_Edit"> |
| 410 | <property name="text"> | 411 | <property name="text"> |
| 411 | <string>Open &Mii Editor</string> | 412 | <string>Open &Mii Editor</string> |
| 412 | </property> | 413 | </property> |
| 413 | </action> | 414 | </action> |
| 414 | <action name="action_Configure_Tas"> | 415 | <action name="action_Configure_Tas"> |
| 415 | <property name="text"> | 416 | <property name="text"> |
| @@ -454,6 +455,11 @@ | |||
| 454 | <string>R&ecord</string> | 455 | <string>R&ecord</string> |
| 455 | </property> | 456 | </property> |
| 456 | </action> | 457 | </action> |
| 458 | <action name="action_Open_Controller_Menu"> | ||
| 459 | <property name="text"> | ||
| 460 | <string>Open &Controller Menu</string> | ||
| 461 | </property> | ||
| 462 | </action> | ||
| 457 | </widget> | 463 | </widget> |
| 458 | <resources> | 464 | <resources> |
| 459 | <include location="yuzu.qrc"/> | 465 | <include location="yuzu.qrc"/> |
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp index 1c833767b..7bb7e95af 100644 --- a/src/yuzu/uisettings.cpp +++ b/src/yuzu/uisettings.cpp | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project | 1 | // SPDX-FileCopyrightText: 2016 Citra Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <QSettings> | ||
| 5 | #include "common/fs/fs.h" | ||
| 6 | #include "common/fs/path_util.h" | ||
| 4 | #include "yuzu/uisettings.h" | 7 | #include "yuzu/uisettings.h" |
| 5 | 8 | ||
| 6 | #ifndef CANNOT_EXPLICITLY_INSTANTIATE | 9 | #ifndef CANNOT_EXPLICITLY_INSTANTIATE |
| @@ -15,6 +18,8 @@ template class Setting<unsigned long long>; | |||
| 15 | } // namespace Settings | 18 | } // namespace Settings |
| 16 | #endif | 19 | #endif |
| 17 | 20 | ||
| 21 | namespace FS = Common::FS; | ||
| 22 | |||
| 18 | namespace UISettings { | 23 | namespace UISettings { |
| 19 | 24 | ||
| 20 | const Themes themes{{ | 25 | const Themes themes{{ |
| @@ -28,10 +33,8 @@ const Themes themes{{ | |||
| 28 | 33 | ||
| 29 | bool IsDarkTheme() { | 34 | bool IsDarkTheme() { |
| 30 | const auto& theme = UISettings::values.theme; | 35 | const auto& theme = UISettings::values.theme; |
| 31 | return theme == QStringLiteral("qdarkstyle") || | 36 | return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") || |
| 32 | theme == QStringLiteral("qdarkstyle_midnight_blue") || | 37 | theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue"); |
| 33 | theme == QStringLiteral("colorful_dark") || | ||
| 34 | theme == QStringLiteral("colorful_midnight_blue"); | ||
| 35 | } | 38 | } |
| 36 | 39 | ||
| 37 | Values values = {}; | 40 | Values values = {}; |
| @@ -52,4 +55,58 @@ u32 CalculateWidth(u32 height, Settings::AspectRatio ratio) { | |||
| 52 | return height * 16 / 9; | 55 | return height * 16 / 9; |
| 53 | } | 56 | } |
| 54 | 57 | ||
| 58 | void SaveWindowState() { | ||
| 59 | const auto window_state_config_loc = | ||
| 60 | FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini"); | ||
| 61 | |||
| 62 | void(FS::CreateParentDir(window_state_config_loc)); | ||
| 63 | QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat); | ||
| 64 | |||
| 65 | config.setValue(QStringLiteral("geometry"), values.geometry); | ||
| 66 | config.setValue(QStringLiteral("state"), values.state); | ||
| 67 | config.setValue(QStringLiteral("geometryRenderWindow"), values.renderwindow_geometry); | ||
| 68 | config.setValue(QStringLiteral("gameListHeaderState"), values.gamelist_header_state); | ||
| 69 | config.setValue(QStringLiteral("microProfileDialogGeometry"), values.microprofile_geometry); | ||
| 70 | |||
| 71 | config.sync(); | ||
| 72 | } | ||
| 73 | |||
| 74 | void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig) { | ||
| 75 | const auto window_state_config_loc = | ||
| 76 | FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini"); | ||
| 77 | |||
| 78 | // Migrate window state from old location | ||
| 79 | if (!FS::Exists(window_state_config_loc) && qtConfig->Exists("UI", "UILayout\\geometry")) { | ||
| 80 | const auto config_loc = | ||
| 81 | FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "qt-config.ini"); | ||
| 82 | QSettings config(QString::fromStdString(config_loc), QSettings::IniFormat); | ||
| 83 | |||
| 84 | config.beginGroup(QStringLiteral("UI")); | ||
| 85 | config.beginGroup(QStringLiteral("UILayout")); | ||
| 86 | values.geometry = config.value(QStringLiteral("geometry")).toByteArray(); | ||
| 87 | values.state = config.value(QStringLiteral("state")).toByteArray(); | ||
| 88 | values.renderwindow_geometry = | ||
| 89 | config.value(QStringLiteral("geometryRenderWindow")).toByteArray(); | ||
| 90 | values.gamelist_header_state = | ||
| 91 | config.value(QStringLiteral("gameListHeaderState")).toByteArray(); | ||
| 92 | values.microprofile_geometry = | ||
| 93 | config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray(); | ||
| 94 | config.endGroup(); | ||
| 95 | config.endGroup(); | ||
| 96 | return; | ||
| 97 | } | ||
| 98 | |||
| 99 | void(FS::CreateParentDir(window_state_config_loc)); | ||
| 100 | const QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat); | ||
| 101 | |||
| 102 | values.geometry = config.value(QStringLiteral("geometry")).toByteArray(); | ||
| 103 | values.state = config.value(QStringLiteral("state")).toByteArray(); | ||
| 104 | values.renderwindow_geometry = | ||
| 105 | config.value(QStringLiteral("geometryRenderWindow")).toByteArray(); | ||
| 106 | values.gamelist_header_state = | ||
| 107 | config.value(QStringLiteral("gameListHeaderState")).toByteArray(); | ||
| 108 | values.microprofile_geometry = | ||
| 109 | config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray(); | ||
| 110 | } | ||
| 111 | |||
| 55 | } // namespace UISettings | 112 | } // namespace UISettings |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index b62ff620c..549a39e1b 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "common/settings.h" | 15 | #include "common/settings.h" |
| 16 | #include "common/settings_enums.h" | 16 | #include "common/settings_enums.h" |
| 17 | #include "configuration/qt_config.h" | ||
| 17 | 18 | ||
| 18 | using Settings::Category; | 19 | using Settings::Category; |
| 19 | using Settings::ConfirmStop; | 20 | using Settings::ConfirmStop; |
| @@ -37,15 +38,15 @@ namespace UISettings { | |||
| 37 | bool IsDarkTheme(); | 38 | bool IsDarkTheme(); |
| 38 | 39 | ||
| 39 | struct ContextualShortcut { | 40 | struct ContextualShortcut { |
| 40 | QString keyseq; | 41 | std::string keyseq; |
| 41 | QString controller_keyseq; | 42 | std::string controller_keyseq; |
| 42 | int context; | 43 | int context; |
| 43 | bool repeat; | 44 | bool repeat; |
| 44 | }; | 45 | }; |
| 45 | 46 | ||
| 46 | struct Shortcut { | 47 | struct Shortcut { |
| 47 | QString name; | 48 | std::string name; |
| 48 | QString group; | 49 | std::string group; |
| 49 | ContextualShortcut shortcut; | 50 | ContextualShortcut shortcut; |
| 50 | }; | 51 | }; |
| 51 | 52 | ||
| @@ -58,11 +59,19 @@ enum class Theme { | |||
| 58 | MidnightBlueColorful, | 59 | MidnightBlueColorful, |
| 59 | }; | 60 | }; |
| 60 | 61 | ||
| 62 | static constexpr Theme default_theme{ | ||
| 63 | #ifdef _WIN32 | ||
| 64 | Theme::DarkColorful | ||
| 65 | #else | ||
| 66 | Theme::DefaultColorful | ||
| 67 | #endif | ||
| 68 | }; | ||
| 69 | |||
| 61 | using Themes = std::array<std::pair<const char*, const char*>, 6>; | 70 | using Themes = std::array<std::pair<const char*, const char*>, 6>; |
| 62 | extern const Themes themes; | 71 | extern const Themes themes; |
| 63 | 72 | ||
| 64 | struct GameDir { | 73 | struct GameDir { |
| 65 | QString path; | 74 | std::string path; |
| 66 | bool deep_scan = false; | 75 | bool deep_scan = false; |
| 67 | bool expanded = false; | 76 | bool expanded = false; |
| 68 | bool operator==(const GameDir& rhs) const { | 77 | bool operator==(const GameDir& rhs) const { |
| @@ -93,10 +102,6 @@ struct Values { | |||
| 93 | Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui}; | 102 | Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui}; |
| 94 | Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui}; | 103 | Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui}; |
| 95 | 104 | ||
| 96 | Setting<bool> confirm_before_closing{ | ||
| 97 | linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default, | ||
| 98 | true, true}; | ||
| 99 | |||
| 100 | SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage, | 105 | SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage, |
| 101 | ConfirmStop::Ask_Always, | 106 | ConfirmStop::Ask_Always, |
| 102 | "confirmStop", | 107 | "confirmStop", |
| @@ -113,9 +118,13 @@ struct Values { | |||
| 113 | Settings::Specialization::Default, | 118 | Settings::Specialization::Default, |
| 114 | true, | 119 | true, |
| 115 | true}; | 120 | true}; |
| 116 | Setting<bool> mute_when_in_background{ | 121 | Setting<bool> mute_when_in_background{linkage, |
| 117 | linkage, false, "muteWhenInBackground", Category::Audio, Settings::Specialization::Default, | 122 | false, |
| 118 | true, true}; | 123 | "muteWhenInBackground", |
| 124 | Category::UiAudio, | ||
| 125 | Settings::Specialization::Default, | ||
| 126 | true, | ||
| 127 | true}; | ||
| 119 | Setting<bool> hide_mouse{ | 128 | Setting<bool> hide_mouse{ |
| 120 | linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default, | 129 | linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default, |
| 121 | true, true}; | 130 | true, true}; |
| @@ -144,15 +153,15 @@ struct Values { | |||
| 144 | Category::Screenshots}; | 153 | Category::Screenshots}; |
| 145 | Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots}; | 154 | Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots}; |
| 146 | 155 | ||
| 147 | QString roms_path; | 156 | std::string roms_path; |
| 148 | QString symbols_path; | 157 | std::string symbols_path; |
| 149 | QString game_dir_deprecated; | 158 | std::string game_dir_deprecated; |
| 150 | bool game_dir_deprecated_deepscan; | 159 | bool game_dir_deprecated_deepscan; |
| 151 | QVector<UISettings::GameDir> game_dirs; | 160 | QVector<GameDir> game_dirs; |
| 152 | QStringList recent_files; | 161 | QStringList recent_files; |
| 153 | QString language; | 162 | std::string language; |
| 154 | 163 | ||
| 155 | QString theme; | 164 | std::string theme; |
| 156 | 165 | ||
| 157 | // Shortcut name <Shortcut, context> | 166 | // Shortcut name <Shortcut, context> |
| 158 | std::vector<Shortcut> shortcuts; | 167 | std::vector<Shortcut> shortcuts; |
| @@ -206,6 +215,54 @@ extern Values values; | |||
| 206 | 215 | ||
| 207 | u32 CalculateWidth(u32 height, Settings::AspectRatio ratio); | 216 | u32 CalculateWidth(u32 height, Settings::AspectRatio ratio); |
| 208 | 217 | ||
| 218 | void SaveWindowState(); | ||
| 219 | void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig); | ||
| 220 | |||
| 221 | // This shouldn't have anything except static initializers (no functions). So | ||
| 222 | // QKeySequence(...).toString() is NOT ALLOWED HERE. | ||
| 223 | // This must be in alphabetical order according to action name as it must have the same order as | ||
| 224 | // UISetting::values.shortcuts, which is alphabetically ordered. | ||
| 225 | // clang-format off | ||
| 226 | const std::array<Shortcut, 23> default_hotkeys{{ | ||
| 227 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}}, | ||
| 228 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}}, | ||
| 229 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}}, | ||
| 230 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+P"), std::string("Screenshot"), Qt::WidgetWithChildrenShortcut, false}}, | ||
| 231 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F8"), std::string("Home+L"), Qt::ApplicationShortcut, false}}, | ||
| 232 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F10"), std::string("Home+X"), Qt::ApplicationShortcut, false}}, | ||
| 233 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F9"), std::string("Home+R"), Qt::ApplicationShortcut, false}}, | ||
| 234 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F4"), std::string("Home+Plus"), Qt::WindowShortcut, false}}, | ||
| 235 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Esc"), std::string(""), Qt::WindowShortcut, false}}, | ||
| 236 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"), std::string("Home+Minus"), Qt::WindowShortcut, false}}, | ||
| 237 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}}, | ||
| 238 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}}, | ||
| 239 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, | ||
| 240 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}}, | ||
| 241 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}}, | ||
| 242 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}}, | ||
| 243 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F6"), std::string(""), Qt::ApplicationShortcut, false}}, | ||
| 244 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F5"), std::string(""), Qt::ApplicationShortcut, false}}, | ||
| 245 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F"), std::string(""), Qt::WindowShortcut, false}}, | ||
| 246 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+U"), std::string("Home+Y"), Qt::ApplicationShortcut, false}}, | ||
| 247 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}}, | ||
| 248 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}}, | ||
| 249 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}}, | ||
| 250 | }}; | ||
| 251 | // clang-format on | ||
| 252 | |||
| 209 | } // namespace UISettings | 253 | } // namespace UISettings |
| 210 | 254 | ||
| 211 | Q_DECLARE_METATYPE(UISettings::GameDir*); | 255 | Q_DECLARE_METATYPE(UISettings::GameDir*); |
| 256 | |||
| 257 | // These metatype declarations cannot be in common/settings.h because core is devoid of QT | ||
| 258 | Q_DECLARE_METATYPE(Settings::CpuAccuracy); | ||
| 259 | Q_DECLARE_METATYPE(Settings::GpuAccuracy); | ||
| 260 | Q_DECLARE_METATYPE(Settings::FullscreenMode); | ||
| 261 | Q_DECLARE_METATYPE(Settings::NvdecEmulation); | ||
| 262 | Q_DECLARE_METATYPE(Settings::ResolutionSetup); | ||
| 263 | Q_DECLARE_METATYPE(Settings::ScalingFilter); | ||
| 264 | Q_DECLARE_METATYPE(Settings::AntiAliasing); | ||
| 265 | Q_DECLARE_METATYPE(Settings::RendererBackend); | ||
| 266 | Q_DECLARE_METATYPE(Settings::ShaderBackend); | ||
| 267 | Q_DECLARE_METATYPE(Settings::AstcRecompression); | ||
| 268 | Q_DECLARE_METATYPE(Settings::AstcDecodeMode); | ||
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index f2854c8ec..e22cf84bf 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp | |||
| @@ -4,7 +4,10 @@ | |||
| 4 | #include <array> | 4 | #include <array> |
| 5 | #include <cmath> | 5 | #include <cmath> |
| 6 | #include <QPainter> | 6 | #include <QPainter> |
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 7 | #include "yuzu/util/util.h" | 9 | #include "yuzu/util/util.h" |
| 10 | |||
| 8 | #ifdef _WIN32 | 11 | #ifdef _WIN32 |
| 9 | #include <windows.h> | 12 | #include <windows.h> |
| 10 | #include "common/fs/file.h" | 13 | #include "common/fs/file.h" |
| @@ -42,7 +45,7 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) { | |||
| 42 | return circle_pixmap; | 45 | return circle_pixmap; |
| 43 | } | 46 | } |
| 44 | 47 | ||
| 45 | bool SaveIconToFile(const std::string_view path, const QImage& image) { | 48 | bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) { |
| 46 | #if defined(WIN32) | 49 | #if defined(WIN32) |
| 47 | #pragma pack(push, 2) | 50 | #pragma pack(push, 2) |
| 48 | struct IconDir { | 51 | struct IconDir { |
| @@ -73,7 +76,7 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) { | |||
| 73 | .id_count = static_cast<WORD>(scale_sizes.size()), | 76 | .id_count = static_cast<WORD>(scale_sizes.size()), |
| 74 | }; | 77 | }; |
| 75 | 78 | ||
| 76 | Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, | 79 | Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write, |
| 77 | Common::FS::FileType::BinaryFile); | 80 | Common::FS::FileType::BinaryFile); |
| 78 | if (!icon_file.IsOpen()) { | 81 | if (!icon_file.IsOpen()) { |
| 79 | return false; | 82 | return false; |
| @@ -135,6 +138,14 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) { | |||
| 135 | icon_file.Close(); | 138 | icon_file.Close(); |
| 136 | 139 | ||
| 137 | return true; | 140 | return true; |
| 141 | #elif defined(__linux__) || defined(__FreeBSD__) | ||
| 142 | // Convert and write the icon as a PNG | ||
| 143 | if (!image.save(QString::fromStdString(icon_path.string()))) { | ||
| 144 | LOG_ERROR(Frontend, "Could not write icon as PNG to file"); | ||
| 145 | } else { | ||
| 146 | LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); | ||
| 147 | } | ||
| 148 | return true; | ||
| 138 | #else | 149 | #else |
| 139 | return false; | 150 | return false; |
| 140 | #endif | 151 | #endif |
diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h index 09c14ce3f..4094cf6c2 100644 --- a/src/yuzu/util/util.h +++ b/src/yuzu/util/util.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <filesystem> | ||
| 6 | #include <QFont> | 7 | #include <QFont> |
| 7 | #include <QString> | 8 | #include <QString> |
| 8 | 9 | ||
| @@ -25,4 +26,4 @@ | |||
| 25 | * @param image The image to save | 26 | * @param image The image to save |
| 26 | * @return bool If the operation succeeded | 27 | * @return bool If the operation succeeded |
| 27 | */ | 28 | */ |
| 28 | [[nodiscard]] bool SaveIconToFile(const std::string_view path, const QImage& image); | 29 | [[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image); |
diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp index 92f10d315..ab0d39c25 100644 --- a/src/yuzu/vk_device_info.cpp +++ b/src/yuzu/vk_device_info.cpp | |||
| @@ -31,6 +31,7 @@ void PopulateRecords(std::vector<Record>& records, QWindow* window) try { | |||
| 31 | // Create a test window with a Vulkan surface type for checking present modes. | 31 | // Create a test window with a Vulkan surface type for checking present modes. |
| 32 | QWindow test_window(window); | 32 | QWindow test_window(window); |
| 33 | test_window.setSurfaceType(QWindow::VulkanSurface); | 33 | test_window.setSurfaceType(QWindow::VulkanSurface); |
| 34 | test_window.create(); | ||
| 34 | auto wsi = QtCommon::GetWindowSystemInfo(&test_window); | 35 | auto wsi = QtCommon::GetWindowSystemInfo(&test_window); |
| 35 | 36 | ||
| 36 | vk::InstanceDispatch dld; | 37 | vk::InstanceDispatch dld; |
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 46eddf423..fbeba8813 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt | |||
| @@ -13,9 +13,6 @@ function(create_resource file output filename) | |||
| 13 | endfunction() | 13 | endfunction() |
| 14 | 14 | ||
| 15 | add_executable(yuzu-cmd | 15 | add_executable(yuzu-cmd |
| 16 | config.cpp | ||
| 17 | config.h | ||
| 18 | default_ini.h | ||
| 19 | emu_window/emu_window_sdl2.cpp | 16 | emu_window/emu_window_sdl2.cpp |
| 20 | emu_window/emu_window_sdl2.h | 17 | emu_window/emu_window_sdl2.h |
| 21 | emu_window/emu_window_sdl2_gl.cpp | 18 | emu_window/emu_window_sdl2_gl.cpp |
| @@ -25,14 +22,16 @@ add_executable(yuzu-cmd | |||
| 25 | emu_window/emu_window_sdl2_vk.cpp | 22 | emu_window/emu_window_sdl2_vk.cpp |
| 26 | emu_window/emu_window_sdl2_vk.h | 23 | emu_window/emu_window_sdl2_vk.h |
| 27 | precompiled_headers.h | 24 | precompiled_headers.h |
| 25 | sdl_config.cpp | ||
| 26 | sdl_config.h | ||
| 28 | yuzu.cpp | 27 | yuzu.cpp |
| 29 | yuzu.rc | 28 | yuzu.rc |
| 30 | ) | 29 | ) |
| 31 | 30 | ||
| 32 | create_target_directory_groups(yuzu-cmd) | 31 | create_target_directory_groups(yuzu-cmd) |
| 33 | 32 | ||
| 34 | target_link_libraries(yuzu-cmd PRIVATE common core input_common) | 33 | target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common) |
| 35 | target_link_libraries(yuzu-cmd PRIVATE inih::INIReader glad) | 34 | target_link_libraries(yuzu-cmd PRIVATE glad) |
| 36 | if (MSVC) | 35 | if (MSVC) |
| 37 | target_link_libraries(yuzu-cmd PRIVATE getopt) | 36 | target_link_libraries(yuzu-cmd PRIVATE getopt) |
| 38 | endif() | 37 | endif() |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp deleted file mode 100644 index 0d25ff400..000000000 --- a/src/yuzu_cmd/config.cpp +++ /dev/null | |||
| @@ -1,279 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <memory> | ||
| 5 | #include <optional> | ||
| 6 | #include <sstream> | ||
| 7 | #include <INIReader.h> | ||
| 8 | #include <SDL.h> | ||
| 9 | #include "common/fs/file.h" | ||
| 10 | #include "common/fs/fs.h" | ||
| 11 | #include "common/fs/path_util.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/settings.h" | ||
| 14 | #include "core/hle/service/acc/profile_manager.h" | ||
| 15 | #include "input_common/main.h" | ||
| 16 | #include "yuzu_cmd/config.h" | ||
| 17 | #include "yuzu_cmd/default_ini.h" | ||
| 18 | |||
| 19 | namespace FS = Common::FS; | ||
| 20 | |||
| 21 | const std::filesystem::path default_config_path = | ||
| 22 | FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; | ||
| 23 | |||
| 24 | Config::Config(std::optional<std::filesystem::path> config_path) | ||
| 25 | : sdl2_config_loc{config_path.value_or(default_config_path)}, | ||
| 26 | sdl2_config{std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc))} { | ||
| 27 | Reload(); | ||
| 28 | } | ||
| 29 | |||
| 30 | Config::~Config() = default; | ||
| 31 | |||
| 32 | bool Config::LoadINI(const std::string& default_contents, bool retry) { | ||
| 33 | const auto config_loc_str = FS::PathToUTF8String(sdl2_config_loc); | ||
| 34 | if (sdl2_config->ParseError() < 0) { | ||
| 35 | if (retry) { | ||
| 36 | LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", | ||
| 37 | config_loc_str); | ||
| 38 | |||
| 39 | void(FS::CreateParentDir(sdl2_config_loc)); | ||
| 40 | void(FS::WriteStringToFile(sdl2_config_loc, FS::FileType::TextFile, default_contents)); | ||
| 41 | |||
| 42 | sdl2_config = std::make_unique<INIReader>(config_loc_str); | ||
| 43 | |||
| 44 | return LoadINI(default_contents, false); | ||
| 45 | } | ||
| 46 | LOG_ERROR(Config, "Failed."); | ||
| 47 | return false; | ||
| 48 | } | ||
| 49 | LOG_INFO(Config, "Successfully loaded {}", config_loc_str); | ||
| 50 | return true; | ||
| 51 | } | ||
| 52 | |||
| 53 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = { | ||
| 54 | SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T, | ||
| 55 | SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W, | ||
| 56 | SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, | ||
| 57 | }; | ||
| 58 | |||
| 59 | static const std::array<int, Settings::NativeMotion::NumMotions> default_motions = { | ||
| 60 | SDL_SCANCODE_7, | ||
| 61 | SDL_SCANCODE_8, | ||
| 62 | }; | ||
| 63 | |||
| 64 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{ | ||
| 65 | { | ||
| 66 | SDL_SCANCODE_UP, | ||
| 67 | SDL_SCANCODE_DOWN, | ||
| 68 | SDL_SCANCODE_LEFT, | ||
| 69 | SDL_SCANCODE_RIGHT, | ||
| 70 | SDL_SCANCODE_D, | ||
| 71 | }, | ||
| 72 | { | ||
| 73 | SDL_SCANCODE_I, | ||
| 74 | SDL_SCANCODE_K, | ||
| 75 | SDL_SCANCODE_J, | ||
| 76 | SDL_SCANCODE_L, | ||
| 77 | SDL_SCANCODE_D, | ||
| 78 | }, | ||
| 79 | }}; | ||
| 80 | |||
| 81 | template <> | ||
| 82 | void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { | ||
| 83 | std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); | ||
| 84 | if (setting_value.empty()) { | ||
| 85 | setting_value = setting.GetDefault(); | ||
| 86 | } | ||
| 87 | setting = std::move(setting_value); | ||
| 88 | } | ||
| 89 | |||
| 90 | template <> | ||
| 91 | void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) { | ||
| 92 | setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); | ||
| 93 | } | ||
| 94 | |||
| 95 | template <typename Type, bool ranged> | ||
| 96 | void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) { | ||
| 97 | setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(), | ||
| 98 | static_cast<long>(setting.GetDefault()))); | ||
| 99 | } | ||
| 100 | |||
| 101 | void Config::ReadCategory(Settings::Category category) { | ||
| 102 | for (const auto setting : Settings::values.linkage.by_category[category]) { | ||
| 103 | const char* category_name = [&]() { | ||
| 104 | if (category == Settings::Category::Controls) { | ||
| 105 | // For compatibility with older configs | ||
| 106 | return "ControlsGeneral"; | ||
| 107 | } else { | ||
| 108 | return Settings::TranslateCategory(category); | ||
| 109 | } | ||
| 110 | }(); | ||
| 111 | std::string setting_value = | ||
| 112 | sdl2_config->Get(category_name, setting->GetLabel(), setting->DefaultToString()); | ||
| 113 | setting->LoadString(setting_value); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | void Config::ReadValues() { | ||
| 118 | // Controls | ||
| 119 | ReadCategory(Settings::Category::Controls); | ||
| 120 | |||
| 121 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 122 | auto& player = Settings::values.players.GetValue()[p]; | ||
| 123 | |||
| 124 | const auto group = fmt::format("ControlsP{}", p); | ||
| 125 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 126 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 127 | player.buttons[i] = | ||
| 128 | sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param); | ||
| 129 | if (player.buttons[i].empty()) { | ||
| 130 | player.buttons[i] = default_param; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 135 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 136 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 137 | default_analogs[i][3], default_analogs[i][4], 0.5f); | ||
| 138 | player.analogs[i] = | ||
| 139 | sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param); | ||
| 140 | if (player.analogs[i].empty()) { | ||
| 141 | player.analogs[i] = default_param; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | ||
| 146 | const std::string default_param = | ||
| 147 | InputCommon::GenerateKeyboardParam(default_motions[i]); | ||
| 148 | auto& player_motions = player.motions[i]; | ||
| 149 | |||
| 150 | player_motions = | ||
| 151 | sdl2_config->Get(group, Settings::NativeMotion::mapping[i], default_param); | ||
| 152 | if (player_motions.empty()) { | ||
| 153 | player_motions = default_param; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | player.connected = sdl2_config->GetBoolean(group, "connected", false); | ||
| 158 | } | ||
| 159 | |||
| 160 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 161 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 162 | Settings::values.debug_pad_buttons[i] = sdl2_config->Get( | ||
| 163 | "ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i], | ||
| 164 | default_param); | ||
| 165 | if (Settings::values.debug_pad_buttons[i].empty()) | ||
| 166 | Settings::values.debug_pad_buttons[i] = default_param; | ||
| 167 | } | ||
| 168 | |||
| 169 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 170 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 171 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 172 | default_analogs[i][3], default_analogs[i][4], 0.5f); | ||
| 173 | Settings::values.debug_pad_analogs[i] = sdl2_config->Get( | ||
| 174 | "ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i], | ||
| 175 | default_param); | ||
| 176 | if (Settings::values.debug_pad_analogs[i].empty()) | ||
| 177 | Settings::values.debug_pad_analogs[i] = default_param; | ||
| 178 | } | ||
| 179 | |||
| 180 | Settings::values.touchscreen.enabled = | ||
| 181 | sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); | ||
| 182 | Settings::values.touchscreen.rotation_angle = | ||
| 183 | sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0); | ||
| 184 | Settings::values.touchscreen.diameter_x = | ||
| 185 | sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); | ||
| 186 | Settings::values.touchscreen.diameter_y = | ||
| 187 | sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); | ||
| 188 | |||
| 189 | int num_touch_from_button_maps = | ||
| 190 | sdl2_config->GetInteger("ControlsGeneral", "touch_from_button_map", 0); | ||
| 191 | if (num_touch_from_button_maps > 0) { | ||
| 192 | for (int i = 0; i < num_touch_from_button_maps; ++i) { | ||
| 193 | Settings::TouchFromButtonMap map; | ||
| 194 | map.name = sdl2_config->Get("ControlsGeneral", | ||
| 195 | std::string("touch_from_button_maps_") + std::to_string(i) + | ||
| 196 | std::string("_name"), | ||
| 197 | "default"); | ||
| 198 | const int num_touch_maps = sdl2_config->GetInteger( | ||
| 199 | "ControlsGeneral", | ||
| 200 | std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"), | ||
| 201 | 0); | ||
| 202 | map.buttons.reserve(num_touch_maps); | ||
| 203 | |||
| 204 | for (int j = 0; j < num_touch_maps; ++j) { | ||
| 205 | std::string touch_mapping = | ||
| 206 | sdl2_config->Get("ControlsGeneral", | ||
| 207 | std::string("touch_from_button_maps_") + std::to_string(i) + | ||
| 208 | std::string("_bind_") + std::to_string(j), | ||
| 209 | ""); | ||
| 210 | map.buttons.emplace_back(std::move(touch_mapping)); | ||
| 211 | } | ||
| 212 | |||
| 213 | Settings::values.touch_from_button_maps.emplace_back(std::move(map)); | ||
| 214 | } | ||
| 215 | } else { | ||
| 216 | Settings::values.touch_from_button_maps.emplace_back( | ||
| 217 | Settings::TouchFromButtonMap{"default", {}}); | ||
| 218 | num_touch_from_button_maps = 1; | ||
| 219 | } | ||
| 220 | Settings::values.touch_from_button_map_index = std::clamp( | ||
| 221 | Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); | ||
| 222 | |||
| 223 | ReadCategory(Settings::Category::Audio); | ||
| 224 | ReadCategory(Settings::Category::Core); | ||
| 225 | ReadCategory(Settings::Category::Cpu); | ||
| 226 | ReadCategory(Settings::Category::CpuDebug); | ||
| 227 | ReadCategory(Settings::Category::CpuUnsafe); | ||
| 228 | ReadCategory(Settings::Category::Renderer); | ||
| 229 | ReadCategory(Settings::Category::RendererAdvanced); | ||
| 230 | ReadCategory(Settings::Category::RendererDebug); | ||
| 231 | ReadCategory(Settings::Category::System); | ||
| 232 | ReadCategory(Settings::Category::SystemAudio); | ||
| 233 | ReadCategory(Settings::Category::DataStorage); | ||
| 234 | ReadCategory(Settings::Category::Debugging); | ||
| 235 | ReadCategory(Settings::Category::DebuggingGraphics); | ||
| 236 | ReadCategory(Settings::Category::Miscellaneous); | ||
| 237 | ReadCategory(Settings::Category::Network); | ||
| 238 | ReadCategory(Settings::Category::WebService); | ||
| 239 | |||
| 240 | // Data Storage | ||
| 241 | FS::SetYuzuPath(FS::YuzuPath::NANDDir, | ||
| 242 | sdl2_config->Get("Data Storage", "nand_directory", | ||
| 243 | FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); | ||
| 244 | FS::SetYuzuPath(FS::YuzuPath::SDMCDir, | ||
| 245 | sdl2_config->Get("Data Storage", "sdmc_directory", | ||
| 246 | FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); | ||
| 247 | FS::SetYuzuPath(FS::YuzuPath::LoadDir, | ||
| 248 | sdl2_config->Get("Data Storage", "load_directory", | ||
| 249 | FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); | ||
| 250 | FS::SetYuzuPath(FS::YuzuPath::DumpDir, | ||
| 251 | sdl2_config->Get("Data Storage", "dump_directory", | ||
| 252 | FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); | ||
| 253 | |||
| 254 | // Debugging | ||
| 255 | Settings::values.record_frame_times = | ||
| 256 | sdl2_config->GetBoolean("Debugging", "record_frame_times", false); | ||
| 257 | |||
| 258 | const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); | ||
| 259 | std::stringstream ss(title_list); | ||
| 260 | std::string line; | ||
| 261 | while (std::getline(ss, line, '|')) { | ||
| 262 | const auto title_id = std::strtoul(line.c_str(), nullptr, 16); | ||
| 263 | const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, ""); | ||
| 264 | |||
| 265 | std::stringstream inner_ss(disabled_list); | ||
| 266 | std::string inner_line; | ||
| 267 | std::vector<std::string> out; | ||
| 268 | while (std::getline(inner_ss, inner_line, '|')) { | ||
| 269 | out.push_back(inner_line); | ||
| 270 | } | ||
| 271 | |||
| 272 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | void Config::Reload() { | ||
| 277 | LoadINI(DefaultINI::sdl2_config_file); | ||
| 278 | ReadValues(); | ||
| 279 | } | ||
diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h deleted file mode 100644 index 512591a39..000000000 --- a/src/yuzu_cmd/config.h +++ /dev/null | |||
| @@ -1,38 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <filesystem> | ||
| 7 | #include <memory> | ||
| 8 | #include <optional> | ||
| 9 | #include <string> | ||
| 10 | |||
| 11 | #include "common/settings.h" | ||
| 12 | |||
| 13 | class INIReader; | ||
| 14 | |||
| 15 | class Config { | ||
| 16 | std::filesystem::path sdl2_config_loc; | ||
| 17 | std::unique_ptr<INIReader> sdl2_config; | ||
| 18 | |||
| 19 | bool LoadINI(const std::string& default_contents = "", bool retry = true); | ||
| 20 | void ReadValues(); | ||
| 21 | |||
| 22 | public: | ||
| 23 | explicit Config(std::optional<std::filesystem::path> config_path); | ||
| 24 | ~Config(); | ||
| 25 | |||
| 26 | void Reload(); | ||
| 27 | |||
| 28 | private: | ||
| 29 | /** | ||
| 30 | * Applies a value read from the sdl2_config to a Setting. | ||
| 31 | * | ||
| 32 | * @param group The name of the INI group | ||
| 33 | * @param setting The yuzu setting to modify | ||
| 34 | */ | ||
| 35 | template <typename Type, bool ranged> | ||
| 36 | void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); | ||
| 37 | void ReadCategory(Settings::Category category); | ||
| 38 | }; | ||
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h deleted file mode 100644 index 119e22183..000000000 --- a/src/yuzu_cmd/default_ini.h +++ /dev/null | |||
| @@ -1,553 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2014 Citra Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | namespace DefaultINI { | ||
| 7 | |||
| 8 | const char* sdl2_config_file = | ||
| 9 | R"( | ||
| 10 | [ControlsP0] | ||
| 11 | # The input devices and parameters for each Switch native input | ||
| 12 | # The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... | ||
| 13 | # It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." | ||
| 14 | # Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values | ||
| 15 | |||
| 16 | # Indicates if this player should be connected at boot | ||
| 17 | # 0 (default): Disabled, 1: Enabled | ||
| 18 | connected= | ||
| 19 | |||
| 20 | # for button input, the following devices are available: | ||
| 21 | # - "keyboard" (default) for keyboard input. Required parameters: | ||
| 22 | # - "code": the code of the key to bind | ||
| 23 | # - "sdl" for joystick input using SDL. Required parameters: | ||
| 24 | # - "guid": SDL identification GUID of the joystick | ||
| 25 | # - "port": the index of the joystick to bind | ||
| 26 | # - "button"(optional): the index of the button to bind | ||
| 27 | # - "hat"(optional): the index of the hat to bind as direction buttons | ||
| 28 | # - "axis"(optional): the index of the axis to bind | ||
| 29 | # - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" | ||
| 30 | # - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is | ||
| 31 | # triggered if the axis value crosses | ||
| 32 | # - "direction"(only used for axis): "+" means the button is triggered when the axis value | ||
| 33 | # is greater than the threshold; "-" means the button is triggered when the axis value | ||
| 34 | # is smaller than the threshold | ||
| 35 | button_a= | ||
| 36 | button_b= | ||
| 37 | button_x= | ||
| 38 | button_y= | ||
| 39 | button_lstick= | ||
| 40 | button_rstick= | ||
| 41 | button_l= | ||
| 42 | button_r= | ||
| 43 | button_zl= | ||
| 44 | button_zr= | ||
| 45 | button_plus= | ||
| 46 | button_minus= | ||
| 47 | button_dleft= | ||
| 48 | button_dup= | ||
| 49 | button_dright= | ||
| 50 | button_ddown= | ||
| 51 | button_lstick_left= | ||
| 52 | button_lstick_up= | ||
| 53 | button_lstick_right= | ||
| 54 | button_lstick_down= | ||
| 55 | button_sl= | ||
| 56 | button_sr= | ||
| 57 | button_home= | ||
| 58 | button_screenshot= | ||
| 59 | |||
| 60 | # for analog input, the following devices are available: | ||
| 61 | # - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: | ||
| 62 | # - "up", "down", "left", "right": sub-devices for each direction. | ||
| 63 | # Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" | ||
| 64 | # - "modifier": sub-devices as a modifier. | ||
| 65 | # - "modifier_scale": a float number representing the applied modifier scale to the analog input. | ||
| 66 | # Must be in range of 0.0-1.0. Defaults to 0.5 | ||
| 67 | # - "sdl" for joystick input using SDL. Required parameters: | ||
| 68 | # - "guid": SDL identification GUID of the joystick | ||
| 69 | # - "port": the index of the joystick to bind | ||
| 70 | # - "axis_x": the index of the axis to bind as x-axis (default to 0) | ||
| 71 | # - "axis_y": the index of the axis to bind as y-axis (default to 1) | ||
| 72 | lstick= | ||
| 73 | rstick= | ||
| 74 | |||
| 75 | # for motion input, the following devices are available: | ||
| 76 | # - "keyboard" (default) for emulating random motion input from buttons. Required parameters: | ||
| 77 | # - "code": the code of the key to bind | ||
| 78 | # - "sdl" for motion input using SDL. Required parameters: | ||
| 79 | # - "guid": SDL identification GUID of the joystick | ||
| 80 | # - "port": the index of the joystick to bind | ||
| 81 | # - "motion": the index of the motion sensor to bind | ||
| 82 | # - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters: | ||
| 83 | # - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001" | ||
| 84 | # - "port": the port of the cemu hook server | ||
| 85 | # - "pad": the index of the joystick | ||
| 86 | # - "motion": the index of the motion sensor of the joystick to bind | ||
| 87 | motionleft= | ||
| 88 | motionright= | ||
| 89 | |||
| 90 | [ControlsGeneral] | ||
| 91 | # To use the debug_pad, prepend `debug_pad_` before each button setting above. | ||
| 92 | # i.e. debug_pad_button_a= | ||
| 93 | |||
| 94 | # Enable debug pad inputs to the guest | ||
| 95 | # 0 (default): Disabled, 1: Enabled | ||
| 96 | debug_pad_enabled = | ||
| 97 | |||
| 98 | # Enable sdl raw input. Allows to configure up to 8 xinput controllers. | ||
| 99 | # 0 (default): Disabled, 1: Enabled | ||
| 100 | enable_raw_input = | ||
| 101 | |||
| 102 | # Enable yuzu joycon driver instead of SDL drive. | ||
| 103 | # 0: Disabled, 1 (default): Enabled | ||
| 104 | enable_joycon_driver = | ||
| 105 | |||
| 106 | # Emulates an analog input from buttons. Allowing to dial any angle. | ||
| 107 | # 0 (default): Disabled, 1: Enabled | ||
| 108 | emulate_analog_keyboard = | ||
| 109 | |||
| 110 | # Whether to enable or disable vibration | ||
| 111 | # 0: Disabled, 1 (default): Enabled | ||
| 112 | vibration_enabled= | ||
| 113 | |||
| 114 | # Whether to enable or disable accurate vibrations | ||
| 115 | # 0 (default): Disabled, 1: Enabled | ||
| 116 | enable_accurate_vibrations= | ||
| 117 | |||
| 118 | # Enables controller motion inputs | ||
| 119 | # 0: Disabled, 1 (default): Enabled | ||
| 120 | motion_enabled = | ||
| 121 | |||
| 122 | # Defines the udp device's touch screen coordinate system for cemuhookudp devices | ||
| 123 | # - "min_x", "min_y", "max_x", "max_y" | ||
| 124 | touch_device= | ||
| 125 | |||
| 126 | # for mapping buttons to touch inputs. | ||
| 127 | #touch_from_button_map=1 | ||
| 128 | #touch_from_button_maps_0_name=default | ||
| 129 | #touch_from_button_maps_0_count=2 | ||
| 130 | #touch_from_button_maps_0_bind_0=foo | ||
| 131 | #touch_from_button_maps_0_bind_1=bar | ||
| 132 | # etc. | ||
| 133 | |||
| 134 | # List of Cemuhook UDP servers, delimited by ','. | ||
| 135 | # Default: 127.0.0.1:26760 | ||
| 136 | # Example: 127.0.0.1:26760,123.4.5.67:26761 | ||
| 137 | udp_input_servers = | ||
| 138 | |||
| 139 | # Enable controlling an axis via a mouse input. | ||
| 140 | # 0 (default): Off, 1: On | ||
| 141 | mouse_panning = | ||
| 142 | |||
| 143 | # Set mouse panning horizontal sensitivity. | ||
| 144 | # Default: 50.0 | ||
| 145 | mouse_panning_x_sensitivity = | ||
| 146 | |||
| 147 | # Set mouse panning vertical sensitivity. | ||
| 148 | # Default: 50.0 | ||
| 149 | mouse_panning_y_sensitivity = | ||
| 150 | |||
| 151 | # Set mouse panning deadzone horizontal counterweight. | ||
| 152 | # Default: 0.0 | ||
| 153 | mouse_panning_deadzone_x_counterweight = | ||
| 154 | |||
| 155 | # Set mouse panning deadzone vertical counterweight. | ||
| 156 | # Default: 0.0 | ||
| 157 | mouse_panning_deadzone_y_counterweight = | ||
| 158 | |||
| 159 | # Set mouse panning stick decay strength. | ||
| 160 | # Default: 22.0 | ||
| 161 | mouse_panning_decay_strength = | ||
| 162 | |||
| 163 | # Set mouse panning stick minimum decay. | ||
| 164 | # Default: 5.0 | ||
| 165 | mouse_panning_minimum_decay = | ||
| 166 | |||
| 167 | # Emulate an analog control stick from keyboard inputs. | ||
| 168 | # 0 (default): Disabled, 1: Enabled | ||
| 169 | emulate_analog_keyboard = | ||
| 170 | |||
| 171 | # Enable mouse inputs to the guest | ||
| 172 | # 0 (default): Disabled, 1: Enabled | ||
| 173 | mouse_enabled = | ||
| 174 | |||
| 175 | # Enable keyboard inputs to the guest | ||
| 176 | # 0 (default): Disabled, 1: Enabled | ||
| 177 | keyboard_enabled = | ||
| 178 | |||
| 179 | )" | ||
| 180 | R"( | ||
| 181 | [Core] | ||
| 182 | # Whether to use multi-core for CPU emulation | ||
| 183 | # 0: Disabled, 1 (default): Enabled | ||
| 184 | use_multi_core = | ||
| 185 | |||
| 186 | # Enable unsafe extended guest system memory layout (8GB DRAM) | ||
| 187 | # 0 (default): Disabled, 1: Enabled | ||
| 188 | use_unsafe_extended_memory_layout = | ||
| 189 | |||
| 190 | [Cpu] | ||
| 191 | # Adjusts various optimizations. | ||
| 192 | # Auto-select mode enables choice unsafe optimizations. | ||
| 193 | # Accurate enables only safe optimizations. | ||
| 194 | # Unsafe allows any unsafe optimizations. | ||
| 195 | # 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations | ||
| 196 | cpu_accuracy = | ||
| 197 | |||
| 198 | # Allow disabling safe optimizations. | ||
| 199 | # 0 (default): Disabled, 1: Enabled | ||
| 200 | cpu_debug_mode = | ||
| 201 | |||
| 202 | # Enable inline page tables optimization (faster guest memory access) | ||
| 203 | # 0: Disabled, 1 (default): Enabled | ||
| 204 | cpuopt_page_tables = | ||
| 205 | |||
| 206 | # Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps) | ||
| 207 | # 0: Disabled, 1 (default): Enabled | ||
| 208 | cpuopt_block_linking = | ||
| 209 | |||
| 210 | # Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns) | ||
| 211 | # 0: Disabled, 1 (default): Enabled | ||
| 212 | cpuopt_return_stack_buffer = | ||
| 213 | |||
| 214 | # Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture) | ||
| 215 | # 0: Disabled, 1 (default): Enabled | ||
| 216 | cpuopt_fast_dispatcher = | ||
| 217 | |||
| 218 | # Enable context elimination CPU Optimization (reduce host memory use for guest context) | ||
| 219 | # 0: Disabled, 1 (default): Enabled | ||
| 220 | cpuopt_context_elimination = | ||
| 221 | |||
| 222 | # Enable constant propagation CPU optimization (basic IR optimization) | ||
| 223 | # 0: Disabled, 1 (default): Enabled | ||
| 224 | cpuopt_const_prop = | ||
| 225 | |||
| 226 | # Enable miscellaneous CPU optimizations (basic IR optimization) | ||
| 227 | # 0: Disabled, 1 (default): Enabled | ||
| 228 | cpuopt_misc_ir = | ||
| 229 | |||
| 230 | # Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access) | ||
| 231 | # 0: Disabled, 1 (default): Enabled | ||
| 232 | cpuopt_reduce_misalign_checks = | ||
| 233 | |||
| 234 | # Enable Host MMU Emulation (faster guest memory access) | ||
| 235 | # 0: Disabled, 1 (default): Enabled | ||
| 236 | cpuopt_fastmem = | ||
| 237 | |||
| 238 | # Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access) | ||
| 239 | # 0: Disabled, 1 (default): Enabled | ||
| 240 | cpuopt_fastmem_exclusives = | ||
| 241 | |||
| 242 | # Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access) | ||
| 243 | # 0: Disabled, 1 (default): Enabled | ||
| 244 | cpuopt_recompile_exclusives = | ||
| 245 | |||
| 246 | # Enable optimization to ignore invalid memory accesses (faster guest memory access) | ||
| 247 | # 0: Disabled, 1 (default): Enabled | ||
| 248 | cpuopt_ignore_memory_aborts = | ||
| 249 | |||
| 250 | # Enable unfuse FMA (improve performance on CPUs without FMA) | ||
| 251 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | ||
| 252 | # 0: Disabled, 1 (default): Enabled | ||
| 253 | cpuopt_unsafe_unfuse_fma = | ||
| 254 | |||
| 255 | # Enable faster FRSQRTE and FRECPE | ||
| 256 | # Only enabled if cpu_accuracy is set to Unsafe. | ||
| 257 | # 0: Disabled, 1 (default): Enabled | ||
| 258 | cpuopt_unsafe_reduce_fp_error = | ||
| 259 | |||
| 260 | # Enable faster ASIMD instructions (32 bits only) | ||
| 261 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | ||
| 262 | # 0: Disabled, 1 (default): Enabled | ||
| 263 | cpuopt_unsafe_ignore_standard_fpcr = | ||
| 264 | |||
| 265 | # Enable inaccurate NaN handling | ||
| 266 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | ||
| 267 | # 0: Disabled, 1 (default): Enabled | ||
| 268 | cpuopt_unsafe_inaccurate_nan = | ||
| 269 | |||
| 270 | # Disable address space checks (64 bits only) | ||
| 271 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | ||
| 272 | # 0: Disabled, 1 (default): Enabled | ||
| 273 | cpuopt_unsafe_fastmem_check = | ||
| 274 | |||
| 275 | # Enable faster exclusive instructions | ||
| 276 | # Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. | ||
| 277 | # 0: Disabled, 1 (default): Enabled | ||
| 278 | cpuopt_unsafe_ignore_global_monitor = | ||
| 279 | |||
| 280 | )" | ||
| 281 | R"( | ||
| 282 | [Renderer] | ||
| 283 | # Which backend API to use. | ||
| 284 | # 0: OpenGL, 1 (default): Vulkan | ||
| 285 | backend = | ||
| 286 | |||
| 287 | # Whether to enable asynchronous presentation (Vulkan only) | ||
| 288 | # 0 (default): Off, 1: On | ||
| 289 | async_presentation = | ||
| 290 | |||
| 291 | # Enable graphics API debugging mode. | ||
| 292 | # 0 (default): Disabled, 1: Enabled | ||
| 293 | debug = | ||
| 294 | |||
| 295 | # Enable shader feedback. | ||
| 296 | # 0 (default): Disabled, 1: Enabled | ||
| 297 | renderer_shader_feedback = | ||
| 298 | |||
| 299 | # Enable Nsight Aftermath crash dumps | ||
| 300 | # 0 (default): Disabled, 1: Enabled | ||
| 301 | nsight_aftermath = | ||
| 302 | |||
| 303 | # Disable shader loop safety checks, executing the shader without loop logic changes | ||
| 304 | # 0 (default): Disabled, 1: Enabled | ||
| 305 | disable_shader_loop_safety_checks = | ||
| 306 | |||
| 307 | # Which Vulkan physical device to use (defaults to 0) | ||
| 308 | vulkan_device = | ||
| 309 | |||
| 310 | # 0: 0.5x (360p/540p) [EXPERIMENTAL] | ||
| 311 | # 1: 0.75x (540p/810p) [EXPERIMENTAL] | ||
| 312 | # 2 (default): 1x (720p/1080p) | ||
| 313 | # 3: 1.5x (1080p/1620p) [EXPERIMENTAL] | ||
| 314 | # 4: 2x (1440p/2160p) | ||
| 315 | # 5: 3x (2160p/3240p) | ||
| 316 | # 6: 4x (2880p/4320p) | ||
| 317 | # 7: 5x (3600p/5400p) | ||
| 318 | # 8: 6x (4320p/6480p) | ||
| 319 | # 9: 7x (5040p/7560p) | ||
| 320 | # 10: 8x (5760/8640p) | ||
| 321 | resolution_setup = | ||
| 322 | |||
| 323 | # Pixel filter to use when up- or down-sampling rendered frames. | ||
| 324 | # 0: Nearest Neighbor | ||
| 325 | # 1 (default): Bilinear | ||
| 326 | # 2: Bicubic | ||
| 327 | # 3: Gaussian | ||
| 328 | # 4: ScaleForce | ||
| 329 | # 5: AMD FidelityFXâ„¢ï¸ Super Resolution | ||
| 330 | scaling_filter = | ||
| 331 | |||
| 332 | # Anti-Aliasing (AA) | ||
| 333 | # 0 (default): None, 1: FXAA, 2: SMAA | ||
| 334 | anti_aliasing = | ||
| 335 | |||
| 336 | # Whether to use fullscreen or borderless window mode | ||
| 337 | # 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen | ||
| 338 | fullscreen_mode = | ||
| 339 | |||
| 340 | # Aspect ratio | ||
| 341 | # 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window | ||
| 342 | aspect_ratio = | ||
| 343 | |||
| 344 | # Anisotropic filtering | ||
| 345 | # 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x | ||
| 346 | max_anisotropy = | ||
| 347 | |||
| 348 | # Whether to enable VSync or not. | ||
| 349 | # OpenGL: Values other than 0 enable VSync | ||
| 350 | # Vulkan: FIFO is selected if the requested mode is not supported by the driver. | ||
| 351 | # FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. | ||
| 352 | # FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down. | ||
| 353 | # Mailbox can have lower latency than FIFO and does not tear but may drop frames. | ||
| 354 | # Immediate (no synchronization) just presents whatever is available and can exhibit tearing. | ||
| 355 | # 0: Immediate (Off), 1: Mailbox, 2 (Default): FIFO (On), 3: FIFO Relaxed | ||
| 356 | use_vsync = | ||
| 357 | |||
| 358 | # Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is | ||
| 359 | # not available and GLASM is selected, GLSL will be used. | ||
| 360 | # 0: GLSL, 1 (default): GLASM, 2: SPIR-V | ||
| 361 | shader_backend = | ||
| 362 | |||
| 363 | # Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory. | ||
| 364 | # 0: Off, 1 (default): On | ||
| 365 | use_reactive_flushing = | ||
| 366 | |||
| 367 | # Whether to allow asynchronous shader building. | ||
| 368 | # 0 (default): Off, 1: On | ||
| 369 | use_asynchronous_shaders = | ||
| 370 | |||
| 371 | # NVDEC emulation. | ||
| 372 | # 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding | ||
| 373 | nvdec_emulation = | ||
| 374 | |||
| 375 | # Accelerate ASTC texture decoding. | ||
| 376 | # 0: Off, 1 (default): On | ||
| 377 | accelerate_astc = | ||
| 378 | |||
| 379 | # Decode ASTC textures asynchronously. | ||
| 380 | # 0 (default): Off, 1: On | ||
| 381 | async_astc = | ||
| 382 | |||
| 383 | # Recompress ASTC textures to a different format. | ||
| 384 | # 0 (default): Uncompressed, 1: BC1 (Low quality), 2: BC3: (Medium quality) | ||
| 385 | async_astc = | ||
| 386 | |||
| 387 | # Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value | ||
| 388 | # 0: Off, 1: On (default) | ||
| 389 | use_speed_limit = | ||
| 390 | |||
| 391 | # Limits the speed of the game to run no faster than this value as a percentage of target speed | ||
| 392 | # 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) | ||
| 393 | speed_limit = | ||
| 394 | |||
| 395 | # Whether to use disk based shader cache | ||
| 396 | # 0: Off, 1 (default): On | ||
| 397 | use_disk_shader_cache = | ||
| 398 | |||
| 399 | # Which gpu accuracy level to use | ||
| 400 | # 0: Normal, 1 (default): High, 2: Extreme (Very slow) | ||
| 401 | gpu_accuracy = | ||
| 402 | |||
| 403 | # Whether to use asynchronous GPU emulation | ||
| 404 | # 0 : Off (slow), 1 (default): On (fast) | ||
| 405 | use_asynchronous_gpu_emulation = | ||
| 406 | |||
| 407 | # Inform the guest that GPU operations completed more quickly than they did. | ||
| 408 | # 0: Off, 1 (default): On | ||
| 409 | use_fast_gpu_time = | ||
| 410 | |||
| 411 | # Whether to use garbage collection or not for GPU caches. | ||
| 412 | # 0 (default): Off, 1: On | ||
| 413 | use_caches_gc = | ||
| 414 | |||
| 415 | # The clear color for the renderer. What shows up on the sides of the bottom screen. | ||
| 416 | # Must be in range of 0-255. Defaults to 0 for all. | ||
| 417 | bg_red = | ||
| 418 | bg_blue = | ||
| 419 | bg_green = | ||
| 420 | |||
| 421 | )" | ||
| 422 | R"( | ||
| 423 | [Audio] | ||
| 424 | # Which audio output engine to use. | ||
| 425 | # auto (default): Auto-select | ||
| 426 | # cubeb: Cubeb audio engine (if available) | ||
| 427 | # sdl2: SDL2 audio engine (if available) | ||
| 428 | # null: No audio output | ||
| 429 | output_engine = | ||
| 430 | |||
| 431 | # Which audio device to use. | ||
| 432 | # auto (default): Auto-select | ||
| 433 | output_device = | ||
| 434 | |||
| 435 | # Output volume. | ||
| 436 | # 100 (default): 100%, 0; mute | ||
| 437 | volume = | ||
| 438 | |||
| 439 | [Data Storage] | ||
| 440 | # Whether to create a virtual SD card. | ||
| 441 | # 1 (default): Yes, 0: No | ||
| 442 | use_virtual_sd = | ||
| 443 | |||
| 444 | # Whether or not to enable gamecard emulation | ||
| 445 | # 1: Yes, 0 (default): No | ||
| 446 | gamecard_inserted = | ||
| 447 | |||
| 448 | # Whether or not the gamecard should be emulated as the current game | ||
| 449 | # If 'gamecard_inserted' is 0 this setting is irrelevant | ||
| 450 | # 1: Yes, 0 (default): No | ||
| 451 | gamecard_current_game = | ||
| 452 | |||
| 453 | # Path to an XCI file to use as the gamecard | ||
| 454 | # If 'gamecard_inserted' is 0 this setting is irrelevant | ||
| 455 | # If 'gamecard_current_game' is 1 this setting is irrelevant | ||
| 456 | gamecard_path = | ||
| 457 | |||
| 458 | [System] | ||
| 459 | # Whether the system is docked | ||
| 460 | # 1 (default): Yes, 0: No | ||
| 461 | use_docked_mode = | ||
| 462 | |||
| 463 | # Sets the seed for the RNG generator built into the switch | ||
| 464 | # rng_seed will be ignored and randomly generated if rng_seed_enabled is false | ||
| 465 | rng_seed_enabled = | ||
| 466 | rng_seed = | ||
| 467 | |||
| 468 | # Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service | ||
| 469 | # This will auto-increment, with the time set being the time the game is started | ||
| 470 | # This override will only occur if custom_rtc_enabled is true, otherwise the current time is used | ||
| 471 | custom_rtc_enabled = | ||
| 472 | custom_rtc = | ||
| 473 | |||
| 474 | # Sets the systems language index | ||
| 475 | # 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, | ||
| 476 | # 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, | ||
| 477 | # 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese | ||
| 478 | language_index = | ||
| 479 | |||
| 480 | # The system region that yuzu will use during emulation | ||
| 481 | # -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan | ||
| 482 | region_index = | ||
| 483 | |||
| 484 | # The system time zone that yuzu will use during emulation | ||
| 485 | # 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone | ||
| 486 | time_zone_index = | ||
| 487 | |||
| 488 | # Sets the sound output mode. | ||
| 489 | # 0: Mono, 1 (default): Stereo, 2: Surround | ||
| 490 | sound_index = | ||
| 491 | |||
| 492 | [Miscellaneous] | ||
| 493 | # A filter which removes logs below a certain logging level. | ||
| 494 | # Examples: *:Debug Kernel.SVC:Trace Service.*:Critical | ||
| 495 | log_filter = *:Trace | ||
| 496 | |||
| 497 | # Use developer keys | ||
| 498 | # 0 (default): Disabled, 1: Enabled | ||
| 499 | use_dev_keys = | ||
| 500 | |||
| 501 | [Debugging] | ||
| 502 | # Record frame time data, can be found in the log directory. Boolean value | ||
| 503 | record_frame_times = | ||
| 504 | # Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them | ||
| 505 | dump_exefs=false | ||
| 506 | # Determines whether or not yuzu will dump all NSOs it attempts to load while loading them | ||
| 507 | dump_nso=false | ||
| 508 | # Determines whether or not yuzu will save the filesystem access log. | ||
| 509 | enable_fs_access_log=false | ||
| 510 | # Enables verbose reporting services | ||
| 511 | reporting_services = | ||
| 512 | # Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode | ||
| 513 | # false: Retail/Normal Mode (default), true: Kiosk Mode | ||
| 514 | quest_flag = | ||
| 515 | # Determines whether debug asserts should be enabled, which will throw an exception on asserts. | ||
| 516 | # false: Disabled (default), true: Enabled | ||
| 517 | use_debug_asserts = | ||
| 518 | # Determines whether unimplemented HLE service calls should be automatically stubbed. | ||
| 519 | # false: Disabled (default), true: Enabled | ||
| 520 | use_auto_stub = | ||
| 521 | # Enables/Disables the macro JIT compiler | ||
| 522 | disable_macro_jit=false | ||
| 523 | # Determines whether to enable the GDB stub and wait for the debugger to attach before running. | ||
| 524 | # false: Disabled (default), true: Enabled | ||
| 525 | use_gdbstub=false | ||
| 526 | # The port to use for the GDB server, if it is enabled. | ||
| 527 | gdbstub_port=6543 | ||
| 528 | |||
| 529 | [WebService] | ||
| 530 | # Whether or not to enable telemetry | ||
| 531 | # 0: No, 1 (default): Yes | ||
| 532 | enable_telemetry = | ||
| 533 | # URL for Web API | ||
| 534 | web_api_url = https://api.yuzu-emu.org | ||
| 535 | # Username and token for yuzu Web Service | ||
| 536 | # See https://profile.yuzu-emu.org/ for more info | ||
| 537 | yuzu_username = | ||
| 538 | yuzu_token = | ||
| 539 | |||
| 540 | [Network] | ||
| 541 | # Name of the network interface device to use with yuzu LAN play. | ||
| 542 | # e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo' | ||
| 543 | # e.g. On Windows: 'Ethernet', 'Wi-Fi' | ||
| 544 | network_interface = | ||
| 545 | |||
| 546 | [AddOns] | ||
| 547 | # Used to disable add-ons | ||
| 548 | # List of title IDs of games that will have add-ons disabled (separated by '|'): | ||
| 549 | title_ids = | ||
| 550 | # For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|') | ||
| 551 | # e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey | ||
| 552 | )"; | ||
| 553 | } // namespace DefaultINI | ||
diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp new file mode 100644 index 000000000..39fd8050c --- /dev/null +++ b/src/yuzu_cmd/sdl_config.cpp | |||
| @@ -0,0 +1,257 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // SDL will break our main function in yuzu-cmd if we don't define this before adding SDL.h | ||
| 5 | #define SDL_MAIN_HANDLED | ||
| 6 | #include <SDL.h> | ||
| 7 | |||
| 8 | #include "input_common/main.h" | ||
| 9 | #include "sdl_config.h" | ||
| 10 | |||
| 11 | const std::array<int, Settings::NativeButton::NumButtons> SdlConfig::default_buttons = { | ||
| 12 | SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T, | ||
| 13 | SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W, | ||
| 14 | SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, | ||
| 15 | }; | ||
| 16 | |||
| 17 | const std::array<int, Settings::NativeMotion::NumMotions> SdlConfig::default_motions = { | ||
| 18 | SDL_SCANCODE_7, | ||
| 19 | SDL_SCANCODE_8, | ||
| 20 | }; | ||
| 21 | |||
| 22 | const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> SdlConfig::default_analogs{ | ||
| 23 | { | ||
| 24 | { | ||
| 25 | SDL_SCANCODE_UP, | ||
| 26 | SDL_SCANCODE_DOWN, | ||
| 27 | SDL_SCANCODE_LEFT, | ||
| 28 | SDL_SCANCODE_RIGHT, | ||
| 29 | }, | ||
| 30 | { | ||
| 31 | SDL_SCANCODE_I, | ||
| 32 | SDL_SCANCODE_K, | ||
| 33 | SDL_SCANCODE_J, | ||
| 34 | SDL_SCANCODE_L, | ||
| 35 | }, | ||
| 36 | }}; | ||
| 37 | |||
| 38 | const std::array<int, 2> SdlConfig::default_stick_mod = { | ||
| 39 | SDL_SCANCODE_D, | ||
| 40 | 0, | ||
| 41 | }; | ||
| 42 | |||
| 43 | const std::array<int, 2> SdlConfig::default_ringcon_analogs{{ | ||
| 44 | 0, | ||
| 45 | 0, | ||
| 46 | }}; | ||
| 47 | |||
| 48 | SdlConfig::SdlConfig(const std::optional<std::string> config_path) { | ||
| 49 | Initialize(config_path); | ||
| 50 | ReadSdlValues(); | ||
| 51 | SaveSdlValues(); | ||
| 52 | } | ||
| 53 | |||
| 54 | SdlConfig::~SdlConfig() { | ||
| 55 | if (global) { | ||
| 56 | SdlConfig::SaveAllValues(); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | void SdlConfig::ReloadAllValues() { | ||
| 61 | Reload(); | ||
| 62 | ReadSdlValues(); | ||
| 63 | SaveSdlValues(); | ||
| 64 | } | ||
| 65 | |||
| 66 | void SdlConfig::SaveAllValues() { | ||
| 67 | Save(); | ||
| 68 | SaveSdlValues(); | ||
| 69 | } | ||
| 70 | |||
| 71 | void SdlConfig::ReadSdlValues() { | ||
| 72 | ReadSdlControlValues(); | ||
| 73 | } | ||
| 74 | |||
| 75 | void SdlConfig::ReadSdlControlValues() { | ||
| 76 | BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); | ||
| 77 | |||
| 78 | Settings::values.players.SetGlobal(!IsCustomConfig()); | ||
| 79 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 80 | ReadSdlPlayerValues(p); | ||
| 81 | } | ||
| 82 | if (IsCustomConfig()) { | ||
| 83 | EndGroup(); | ||
| 84 | return; | ||
| 85 | } | ||
| 86 | ReadDebugControlValues(); | ||
| 87 | ReadHidbusValues(); | ||
| 88 | |||
| 89 | EndGroup(); | ||
| 90 | } | ||
| 91 | |||
| 92 | void SdlConfig::ReadSdlPlayerValues(const std::size_t player_index) { | ||
| 93 | std::string player_prefix; | ||
| 94 | if (type != ConfigType::InputProfile) { | ||
| 95 | player_prefix.append("player_").append(ToString(player_index)).append("_"); | ||
| 96 | } | ||
| 97 | |||
| 98 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 99 | if (IsCustomConfig()) { | ||
| 100 | const auto profile_name = | ||
| 101 | ReadStringSetting(std::string(player_prefix).append("profile_name")); | ||
| 102 | if (profile_name.empty()) { | ||
| 103 | // Use the global input config | ||
| 104 | player = Settings::values.players.GetValue(true)[player_index]; | ||
| 105 | return; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 110 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 111 | auto& player_buttons = player.buttons[i]; | ||
| 112 | |||
| 113 | player_buttons = ReadStringSetting( | ||
| 114 | std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param); | ||
| 115 | if (player_buttons.empty()) { | ||
| 116 | player_buttons = default_param; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 121 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 122 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 123 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 124 | auto& player_analogs = player.analogs[i]; | ||
| 125 | |||
| 126 | player_analogs = ReadStringSetting( | ||
| 127 | std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param); | ||
| 128 | if (player_analogs.empty()) { | ||
| 129 | player_analogs = default_param; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | ||
| 134 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); | ||
| 135 | auto& player_motions = player.motions[i]; | ||
| 136 | |||
| 137 | player_motions = ReadStringSetting( | ||
| 138 | std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param); | ||
| 139 | if (player_motions.empty()) { | ||
| 140 | player_motions = default_param; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | void SdlConfig::ReadDebugControlValues() { | ||
| 146 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 147 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 148 | auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; | ||
| 149 | debug_pad_buttons = ReadStringSetting( | ||
| 150 | std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param); | ||
| 151 | if (debug_pad_buttons.empty()) { | ||
| 152 | debug_pad_buttons = default_param; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 156 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 157 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 158 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 159 | auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; | ||
| 160 | debug_pad_analogs = ReadStringSetting( | ||
| 161 | std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param); | ||
| 162 | if (debug_pad_analogs.empty()) { | ||
| 163 | debug_pad_analogs = default_param; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | void SdlConfig::ReadHidbusValues() { | ||
| 169 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 170 | 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); | ||
| 171 | auto& ringcon_analogs = Settings::values.ringcon_analogs; | ||
| 172 | |||
| 173 | ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param); | ||
| 174 | if (ringcon_analogs.empty()) { | ||
| 175 | ringcon_analogs = default_param; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | void SdlConfig::SaveSdlValues() { | ||
| 180 | SaveSdlControlValues(); | ||
| 181 | |||
| 182 | WriteToIni(); | ||
| 183 | } | ||
| 184 | |||
| 185 | void SdlConfig::SaveSdlControlValues() { | ||
| 186 | BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); | ||
| 187 | |||
| 188 | Settings::values.players.SetGlobal(!IsCustomConfig()); | ||
| 189 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 190 | SaveSdlPlayerValues(p); | ||
| 191 | } | ||
| 192 | if (IsCustomConfig()) { | ||
| 193 | EndGroup(); | ||
| 194 | return; | ||
| 195 | } | ||
| 196 | SaveDebugControlValues(); | ||
| 197 | SaveHidbusValues(); | ||
| 198 | |||
| 199 | EndGroup(); | ||
| 200 | } | ||
| 201 | |||
| 202 | void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) { | ||
| 203 | std::string player_prefix; | ||
| 204 | if (type != ConfigType::InputProfile) { | ||
| 205 | player_prefix = std::string("player_").append(ToString(player_index)).append("_"); | ||
| 206 | } | ||
| 207 | |||
| 208 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 209 | if (IsCustomConfig() && player.profile_name.empty()) { | ||
| 210 | // No custom profile selected | ||
| 211 | return; | ||
| 212 | } | ||
| 213 | |||
| 214 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 215 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 216 | WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), | ||
| 217 | player.buttons[i], std::make_optional(default_param)); | ||
| 218 | } | ||
| 219 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 220 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 221 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 222 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 223 | WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), | ||
| 224 | player.analogs[i], std::make_optional(default_param)); | ||
| 225 | } | ||
| 226 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | ||
| 227 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); | ||
| 228 | WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), | ||
| 229 | player.motions[i], std::make_optional(default_param)); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | void SdlConfig::SaveDebugControlValues() { | ||
| 234 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 235 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 236 | WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), | ||
| 237 | Settings::values.debug_pad_buttons[i], std::make_optional(default_param)); | ||
| 238 | } | ||
| 239 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 240 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 241 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 242 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 243 | WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), | ||
| 244 | Settings::values.debug_pad_analogs[i], std::make_optional(default_param)); | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | void SdlConfig::SaveHidbusValues() { | ||
| 249 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 250 | 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); | ||
| 251 | WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs, | ||
| 252 | std::make_optional(default_param)); | ||
| 253 | } | ||
| 254 | |||
| 255 | std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) { | ||
| 256 | return Settings::values.linkage.by_category[category]; | ||
| 257 | } | ||
diff --git a/src/yuzu_cmd/sdl_config.h b/src/yuzu_cmd/sdl_config.h new file mode 100644 index 000000000..1fd1c692d --- /dev/null +++ b/src/yuzu_cmd/sdl_config.h | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "frontend_common/config.h" | ||
| 7 | |||
| 8 | class SdlConfig final : public Config { | ||
| 9 | public: | ||
| 10 | explicit SdlConfig(std::optional<std::string> config_path); | ||
| 11 | ~SdlConfig() override; | ||
| 12 | |||
| 13 | void ReloadAllValues() override; | ||
| 14 | void SaveAllValues() override; | ||
| 15 | |||
| 16 | protected: | ||
| 17 | void ReadSdlValues(); | ||
| 18 | void ReadSdlPlayerValues(std::size_t player_index); | ||
| 19 | void ReadSdlControlValues(); | ||
| 20 | void ReadHidbusValues() override; | ||
| 21 | void ReadDebugControlValues() override; | ||
| 22 | void ReadPathValues() override {} | ||
| 23 | void ReadShortcutValues() override {} | ||
| 24 | void ReadUIValues() override {} | ||
| 25 | void ReadUIGamelistValues() override {} | ||
| 26 | void ReadUILayoutValues() override {} | ||
| 27 | void ReadMultiplayerValues() override {} | ||
| 28 | |||
| 29 | void SaveSdlValues(); | ||
| 30 | void SaveSdlPlayerValues(std::size_t player_index); | ||
| 31 | void SaveSdlControlValues(); | ||
| 32 | void SaveHidbusValues() override; | ||
| 33 | void SaveDebugControlValues() override; | ||
| 34 | void SavePathValues() override {} | ||
| 35 | void SaveShortcutValues() override {} | ||
| 36 | void SaveUIValues() override {} | ||
| 37 | void SaveUIGamelistValues() override {} | ||
| 38 | void SaveUILayoutValues() override {} | ||
| 39 | void SaveMultiplayerValues() override {} | ||
| 40 | |||
| 41 | std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override; | ||
| 42 | |||
| 43 | public: | ||
| 44 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; | ||
| 45 | static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; | ||
| 46 | static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; | ||
| 47 | static const std::array<int, 2> default_stick_mod; | ||
| 48 | static const std::array<int, 2> default_ringcon_analogs; | ||
| 49 | }; | ||
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 087cfaa26..0416d5951 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -29,10 +29,11 @@ | |||
| 29 | #include "core/hle/service/filesystem/filesystem.h" | 29 | #include "core/hle/service/filesystem/filesystem.h" |
| 30 | #include "core/loader/loader.h" | 30 | #include "core/loader/loader.h" |
| 31 | #include "core/telemetry_session.h" | 31 | #include "core/telemetry_session.h" |
| 32 | #include "frontend_common/config.h" | ||
| 32 | #include "input_common/main.h" | 33 | #include "input_common/main.h" |
| 33 | #include "network/network.h" | 34 | #include "network/network.h" |
| 35 | #include "sdl_config.h" | ||
| 34 | #include "video_core/renderer_base.h" | 36 | #include "video_core/renderer_base.h" |
| 35 | #include "yuzu_cmd/config.h" | ||
| 36 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 37 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 37 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" | 38 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" |
| 38 | #include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" | 39 | #include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" |
| @@ -300,7 +301,7 @@ int main(int argc, char** argv) { | |||
| 300 | } | 301 | } |
| 301 | } | 302 | } |
| 302 | 303 | ||
| 303 | Config config{config_path}; | 304 | SdlConfig config{config_path}; |
| 304 | 305 | ||
| 305 | // apply the log_filter setting | 306 | // apply the log_filter setting |
| 306 | // the logger was initialized before and doesn't pick up the filter on its own | 307 | // the logger was initialized before and doesn't pick up the filter on its own |