diff options
Diffstat (limited to 'src')
257 files changed, 10284 insertions, 6337 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d2ca4904a..e04d2418b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -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 021b070e0..5721327e7 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts | |||
| @@ -219,7 +219,6 @@ dependencies { | |||
| 219 | implementation("io.coil-kt:coil:2.2.2") | 219 | implementation("io.coil-kt:coil:2.2.2") |
| 220 | implementation("androidx.core:core-splashscreen:1.0.1") | 220 | implementation("androidx.core:core-splashscreen:1.0.1") |
| 221 | implementation("androidx.window:window:1.2.0-beta03") | 221 | implementation("androidx.window:window:1.2.0-beta03") |
| 222 | implementation("org.ini4j:ini4j:0.5.4") | ||
| 223 | implementation("androidx.constraintlayout:constraintlayout:2.1.4") | 222 | implementation("androidx.constraintlayout:constraintlayout:2.1.4") |
| 224 | implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") | 223 | implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") |
| 225 | 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/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 9ebd6c732..e0f01127c 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) |
| @@ -302,6 +300,11 @@ object NativeLibrary { | |||
| 302 | external fun getPerfStats(): DoubleArray | 300 | external fun getPerfStats(): DoubleArray |
| 303 | 301 | ||
| 304 | /** | 302 | /** |
| 303 | * Returns the current CPU backend. | ||
| 304 | */ | ||
| 305 | external fun getCpuBackend(): String | ||
| 306 | |||
| 307 | /** | ||
| 305 | * Notifies the core emulation that the orientation has changed. | 308 | * Notifies the core emulation that the orientation has changed. |
| 306 | */ | 309 | */ |
| 307 | external fun notifyOrientationChange(layout_option: Int, rotation: Int) | 310 | external fun notifyOrientationChange(layout_option: Int, rotation: Int) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt new file mode 100644 index 000000000..ab657a7b9 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.adapters | ||
| 5 | |||
| 6 | import android.net.Uri | ||
| 7 | import android.text.TextUtils | ||
| 8 | import android.view.LayoutInflater | ||
| 9 | import android.view.ViewGroup | ||
| 10 | import androidx.fragment.app.FragmentActivity | ||
| 11 | import androidx.recyclerview.widget.AsyncDifferConfig | ||
| 12 | import androidx.recyclerview.widget.DiffUtil | ||
| 13 | import androidx.recyclerview.widget.ListAdapter | ||
| 14 | import androidx.recyclerview.widget.RecyclerView | ||
| 15 | import org.yuzu.yuzu_emu.databinding.CardFolderBinding | ||
| 16 | import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment | ||
| 17 | import org.yuzu.yuzu_emu.model.GameDir | ||
| 18 | import org.yuzu.yuzu_emu.model.GamesViewModel | ||
| 19 | |||
| 20 | class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) : | ||
| 21 | ListAdapter<GameDir, FolderAdapter.FolderViewHolder>( | ||
| 22 | AsyncDifferConfig.Builder(DiffCallback()).build() | ||
| 23 | ) { | ||
| 24 | override fun onCreateViewHolder( | ||
| 25 | parent: ViewGroup, | ||
| 26 | viewType: Int | ||
| 27 | ): FolderAdapter.FolderViewHolder { | ||
| 28 | CardFolderBinding.inflate(LayoutInflater.from(parent.context), parent, false) | ||
| 29 | .also { return FolderViewHolder(it) } | ||
| 30 | } | ||
| 31 | |||
| 32 | override fun onBindViewHolder(holder: FolderAdapter.FolderViewHolder, position: Int) = | ||
| 33 | holder.bind(currentList[position]) | ||
| 34 | |||
| 35 | inner class FolderViewHolder(val binding: CardFolderBinding) : | ||
| 36 | RecyclerView.ViewHolder(binding.root) { | ||
| 37 | private lateinit var gameDir: GameDir | ||
| 38 | |||
| 39 | fun bind(gameDir: GameDir) { | ||
| 40 | this.gameDir = gameDir | ||
| 41 | |||
| 42 | binding.apply { | ||
| 43 | path.text = Uri.parse(gameDir.uriString).path | ||
| 44 | path.postDelayed( | ||
| 45 | { | ||
| 46 | path.isSelected = true | ||
| 47 | path.ellipsize = TextUtils.TruncateAt.MARQUEE | ||
| 48 | }, | ||
| 49 | 3000 | ||
| 50 | ) | ||
| 51 | |||
| 52 | buttonEdit.setOnClickListener { | ||
| 53 | GameFolderPropertiesDialogFragment.newInstance(this@FolderViewHolder.gameDir) | ||
| 54 | .show( | ||
| 55 | activity.supportFragmentManager, | ||
| 56 | GameFolderPropertiesDialogFragment.TAG | ||
| 57 | ) | ||
| 58 | } | ||
| 59 | |||
| 60 | buttonDelete.setOnClickListener { | ||
| 61 | gamesViewModel.removeFolder(this@FolderViewHolder.gameDir) | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | private class DiffCallback : DiffUtil.ItemCallback<GameDir>() { | ||
| 68 | override fun areItemsTheSame(oldItem: GameDir, newItem: GameDir): Boolean { | ||
| 69 | return oldItem == newItem | ||
| 70 | } | ||
| 71 | |||
| 72 | override fun areContentsTheSame(oldItem: GameDir, newItem: GameDir): Boolean { | ||
| 73 | return oldItem == newItem | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt index 151362124..ef10b209f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt | |||
| @@ -10,6 +10,7 @@ enum class IntSetting( | |||
| 10 | override val category: Settings.Category, | 10 | override val category: Settings.Category, |
| 11 | override val androidDefault: Int? = null | 11 | override val androidDefault: Int? = null |
| 12 | ) : AbstractIntSetting { | 12 | ) : AbstractIntSetting { |
| 13 | CPU_BACKEND("cpu_backend", Settings.Category.Cpu), | ||
| 13 | CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu), | 14 | CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu), |
| 14 | REGION_INDEX("region_index", Settings.Category.System), | 15 | REGION_INDEX("region_index", Settings.Category.System), |
| 15 | LANGUAGE_INDEX("language_index", Settings.Category.System), | 16 | LANGUAGE_INDEX("language_index", Settings.Category.System), |
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 2bf0e1b0d..e3cd66185 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 | |||
| @@ -3,33 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model | 4 | package org.yuzu.yuzu_emu.features.settings.model |
| 5 | 5 | ||
| 6 | import android.text.TextUtils | ||
| 7 | import android.widget.Toast | ||
| 8 | import org.yuzu.yuzu_emu.R | 6 | import org.yuzu.yuzu_emu.R |
| 9 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 10 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | ||
| 11 | 7 | ||
| 12 | object Settings { | 8 | object Settings { |
| 13 | private val context get() = YuzuApplication.appContext | ||
| 14 | |||
| 15 | fun saveSettings(gameId: String = "") { | ||
| 16 | if (TextUtils.isEmpty(gameId)) { | ||
| 17 | Toast.makeText( | ||
| 18 | context, | ||
| 19 | context.getString(R.string.ini_saved), | ||
| 20 | Toast.LENGTH_SHORT | ||
| 21 | ).show() | ||
| 22 | SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG) | ||
| 23 | } else { | ||
| 24 | // TODO: Save custom game settings | ||
| 25 | Toast.makeText( | ||
| 26 | context, | ||
| 27 | context.getString(R.string.gameid_saved, gameId), | ||
| 28 | Toast.LENGTH_SHORT | ||
| 29 | ).show() | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | enum class Category { | 9 | enum class Category { |
| 34 | Android, | 10 | Android, |
| 35 | Audio, | 11 | Audio, |
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..e198b18a0 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,12 +73,21 @@ 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 | ) |
| 80 | put( | 80 | put( |
| 81 | SingleChoiceSetting( | 81 | SingleChoiceSetting( |
| 82 | IntSetting.CPU_BACKEND, | ||
| 83 | R.string.cpu_backend, | ||
| 84 | 0, | ||
| 85 | R.array.cpuBackendArm64Names, | ||
| 86 | R.array.cpuBackendArm64Values | ||
| 87 | ) | ||
| 88 | ) | ||
| 89 | put( | ||
| 90 | SingleChoiceSetting( | ||
| 82 | IntSetting.CPU_ACCURACY, | 91 | IntSetting.CPU_ACCURACY, |
| 83 | R.string.cpu_accuracy, | 92 | R.string.cpu_accuracy, |
| 84 | 0, | 93 | 0, |
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..64bfc6dd0 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 | |||
| @@ -19,13 +19,13 @@ import androidx.lifecycle.repeatOnLifecycle | |||
| 19 | import androidx.navigation.fragment.NavHostFragment | 19 | import androidx.navigation.fragment.NavHostFragment |
| 20 | import androidx.navigation.navArgs | 20 | import androidx.navigation.navArgs |
| 21 | import com.google.android.material.color.MaterialColors | 21 | import com.google.android.material.color.MaterialColors |
| 22 | import kotlinx.coroutines.CoroutineScope | ||
| 23 | import kotlinx.coroutines.Dispatchers | ||
| 22 | import kotlinx.coroutines.flow.collectLatest | 24 | import kotlinx.coroutines.flow.collectLatest |
| 23 | import kotlinx.coroutines.launch | 25 | import kotlinx.coroutines.launch |
| 24 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 25 | import java.io.IOException | 26 | import java.io.IOException |
| 26 | import org.yuzu.yuzu_emu.R | 27 | import org.yuzu.yuzu_emu.R |
| 27 | import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding | 28 | import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding |
| 28 | import org.yuzu.yuzu_emu.features.settings.model.Settings | ||
| 29 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | 29 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile |
| 30 | import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment | 30 | import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment |
| 31 | import org.yuzu.yuzu_emu.model.SettingsViewModel | 31 | import org.yuzu.yuzu_emu.model.SettingsViewModel |
| @@ -54,10 +54,6 @@ class SettingsActivity : AppCompatActivity() { | |||
| 54 | 54 | ||
| 55 | WindowCompat.setDecorFitsSystemWindows(window, false) | 55 | WindowCompat.setDecorFitsSystemWindows(window, false) |
| 56 | 56 | ||
| 57 | if (savedInstanceState != null) { | ||
| 58 | settingsViewModel.shouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE) | ||
| 59 | } | ||
| 60 | |||
| 61 | if (InsetsHelper.getSystemGestureType(applicationContext) != | 57 | if (InsetsHelper.getSystemGestureType(applicationContext) != |
| 62 | InsetsHelper.GESTURE_NAVIGATION | 58 | InsetsHelper.GESTURE_NAVIGATION |
| 63 | ) { | 59 | ) { |
| @@ -128,12 +124,6 @@ class SettingsActivity : AppCompatActivity() { | |||
| 128 | } | 124 | } |
| 129 | } | 125 | } |
| 130 | 126 | ||
| 131 | override fun onSaveInstanceState(outState: Bundle) { | ||
| 132 | // Critical: If super method is not called, rotations will be busted. | ||
| 133 | super.onSaveInstanceState(outState) | ||
| 134 | outState.putBoolean(KEY_SHOULD_SAVE, settingsViewModel.shouldSave) | ||
| 135 | } | ||
| 136 | |||
| 137 | override fun onStart() { | 127 | override fun onStart() { |
| 138 | super.onStart() | 128 | super.onStart() |
| 139 | // TODO: Load custom settings contextually | 129 | // TODO: Load custom settings contextually |
| @@ -142,16 +132,10 @@ class SettingsActivity : AppCompatActivity() { | |||
| 142 | } | 132 | } |
| 143 | } | 133 | } |
| 144 | 134 | ||
| 145 | /** | ||
| 146 | * If this is called, the user has left the settings screen (potentially through the | ||
| 147 | * home button) and will expect their changes to be persisted. So we kick off an | ||
| 148 | * IntentService which will do so on a background thread. | ||
| 149 | */ | ||
| 150 | override fun onStop() { | 135 | override fun onStop() { |
| 151 | super.onStop() | 136 | super.onStop() |
| 152 | if (isFinishing && settingsViewModel.shouldSave) { | 137 | CoroutineScope(Dispatchers.IO).launch { |
| 153 | Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...") | 138 | NativeConfig.saveSettings() |
| 154 | Settings.saveSettings() | ||
| 155 | } | 139 | } |
| 156 | } | 140 | } |
| 157 | 141 | ||
| @@ -161,15 +145,13 @@ class SettingsActivity : AppCompatActivity() { | |||
| 161 | } | 145 | } |
| 162 | 146 | ||
| 163 | fun onSettingsReset() { | 147 | fun onSettingsReset() { |
| 164 | // Prevents saving to a non-existent settings file | ||
| 165 | settingsViewModel.shouldSave = false | ||
| 166 | |||
| 167 | // Delete settings file because the user may have changed values that do not exist in the UI | 148 | // Delete settings file because the user may have changed values that do not exist in the UI |
| 149 | NativeConfig.unloadConfig() | ||
| 168 | val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) | 150 | val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) |
| 169 | if (!settingsFile.delete()) { | 151 | if (!settingsFile.delete()) { |
| 170 | throw IOException("Failed to delete $settingsFile") | 152 | throw IOException("Failed to delete $settingsFile") |
| 171 | } | 153 | } |
| 172 | NativeLibrary.reloadSettings() | 154 | NativeConfig.initializeConfig() |
| 173 | 155 | ||
| 174 | Toast.makeText( | 156 | Toast.makeText( |
| 175 | applicationContext, | 157 | applicationContext, |
| @@ -194,8 +176,4 @@ class SettingsActivity : AppCompatActivity() { | |||
| 194 | windowInsets | 176 | windowInsets |
| 195 | } | 177 | } |
| 196 | } | 178 | } |
| 197 | |||
| 198 | companion object { | ||
| 199 | private const val KEY_SHOULD_SAVE = "should_save" | ||
| 200 | } | ||
| 201 | } | 179 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt index a7a029fc1..af2c1e582 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt | |||
| @@ -105,7 +105,6 @@ class SettingsAdapter( | |||
| 105 | fun onBooleanClick(item: SwitchSetting, checked: Boolean) { | 105 | fun onBooleanClick(item: SwitchSetting, checked: Boolean) { |
| 106 | item.checked = checked | 106 | item.checked = checked |
| 107 | settingsViewModel.setShouldReloadSettingsList(true) | 107 | settingsViewModel.setShouldReloadSettingsList(true) |
| 108 | settingsViewModel.shouldSave = true | ||
| 109 | } | 108 | } |
| 110 | 109 | ||
| 111 | fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) { | 110 | fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) { |
| @@ -161,7 +160,6 @@ class SettingsAdapter( | |||
| 161 | epochTime += timePicker.hour.toLong() * 60 * 60 | 160 | epochTime += timePicker.hour.toLong() * 60 * 60 |
| 162 | epochTime += timePicker.minute.toLong() * 60 | 161 | epochTime += timePicker.minute.toLong() * 60 |
| 163 | if (item.value != epochTime) { | 162 | if (item.value != epochTime) { |
| 164 | settingsViewModel.shouldSave = true | ||
| 165 | notifyItemChanged(position) | 163 | notifyItemChanged(position) |
| 166 | item.value = epochTime | 164 | item.value = epochTime |
| 167 | } | 165 | } |
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 8b71e32f3..7425728c6 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 | |||
| @@ -269,6 +269,7 @@ class SettingsFragmentPresenter( | |||
| 269 | add(BooleanSetting.RENDERER_DEBUG.key) | 269 | add(BooleanSetting.RENDERER_DEBUG.key) |
| 270 | 270 | ||
| 271 | add(HeaderSetting(R.string.cpu)) | 271 | add(HeaderSetting(R.string.cpu)) |
| 272 | add(IntSetting.CPU_BACKEND.key) | ||
| 272 | add(IntSetting.CPU_ACCURACY.key) | 273 | add(IntSetting.CPU_ACCURACY.key) |
| 273 | add(BooleanSetting.CPU_DEBUG_MODE.key) | 274 | add(BooleanSetting.CPU_DEBUG_MODE.key) |
| 274 | add(SettingsItem.FASTMEM_COMBINED) | 275 | add(SettingsItem.FASTMEM_COMBINED) |
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/AddGameFolderDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt new file mode 100644 index 000000000..dec2b7cf1 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.fragments | ||
| 5 | |||
| 6 | import android.app.Dialog | ||
| 7 | import android.content.DialogInterface | ||
| 8 | import android.net.Uri | ||
| 9 | import android.os.Bundle | ||
| 10 | import androidx.fragment.app.DialogFragment | ||
| 11 | import androidx.fragment.app.activityViewModels | ||
| 12 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| 13 | import org.yuzu.yuzu_emu.R | ||
| 14 | import org.yuzu.yuzu_emu.databinding.DialogAddFolderBinding | ||
| 15 | import org.yuzu.yuzu_emu.model.GameDir | ||
| 16 | import org.yuzu.yuzu_emu.model.GamesViewModel | ||
| 17 | |||
| 18 | class AddGameFolderDialogFragment : DialogFragment() { | ||
| 19 | private val gamesViewModel: GamesViewModel by activityViewModels() | ||
| 20 | |||
| 21 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { | ||
| 22 | val binding = DialogAddFolderBinding.inflate(layoutInflater) | ||
| 23 | val folderUriString = requireArguments().getString(FOLDER_URI_STRING) | ||
| 24 | if (folderUriString == null) { | ||
| 25 | dismiss() | ||
| 26 | } | ||
| 27 | binding.path.text = Uri.parse(folderUriString).path | ||
| 28 | |||
| 29 | return MaterialAlertDialogBuilder(requireContext()) | ||
| 30 | .setTitle(R.string.add_game_folder) | ||
| 31 | .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> | ||
| 32 | val newGameDir = GameDir(folderUriString!!, binding.deepScanSwitch.isChecked) | ||
| 33 | gamesViewModel.addFolder(newGameDir) | ||
| 34 | } | ||
| 35 | .setNegativeButton(android.R.string.cancel, null) | ||
| 36 | .setView(binding.root) | ||
| 37 | .show() | ||
| 38 | } | ||
| 39 | |||
| 40 | companion object { | ||
| 41 | const val TAG = "AddGameFolderDialogFragment" | ||
| 42 | |||
| 43 | private const val FOLDER_URI_STRING = "FolderUriString" | ||
| 44 | |||
| 45 | fun newInstance(folderUriString: String): AddGameFolderDialogFragment { | ||
| 46 | val args = Bundle() | ||
| 47 | args.putString(FOLDER_URI_STRING, folderUriString) | ||
| 48 | val fragment = AddGameFolderDialogFragment() | ||
| 49 | fragment.arguments = args | ||
| 50 | return fragment | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
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 c32fa0d7e..734c1d5ca 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 | |||
| @@ -414,8 +414,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 414 | perfStatsUpdater = { | 414 | perfStatsUpdater = { |
| 415 | if (emulationViewModel.emulationStarted.value) { | 415 | if (emulationViewModel.emulationStarted.value) { |
| 416 | val perfStats = NativeLibrary.getPerfStats() | 416 | val perfStats = NativeLibrary.getPerfStats() |
| 417 | val cpuBackend = NativeLibrary.getCpuBackend() | ||
| 417 | if (_binding != null) { | 418 | if (_binding != null) { |
| 418 | binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) | 419 | binding.showFpsText.text = |
| 420 | String.format("FPS: %.1f\n%s", perfStats[FPS], cpuBackend) | ||
| 419 | } | 421 | } |
| 420 | perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800) | 422 | perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800) |
| 421 | } | 423 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt new file mode 100644 index 000000000..b6c2e4635 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.fragments | ||
| 5 | |||
| 6 | import android.app.Dialog | ||
| 7 | import android.content.DialogInterface | ||
| 8 | import android.os.Bundle | ||
| 9 | import androidx.fragment.app.DialogFragment | ||
| 10 | import androidx.fragment.app.activityViewModels | ||
| 11 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| 12 | import org.yuzu.yuzu_emu.R | ||
| 13 | import org.yuzu.yuzu_emu.databinding.DialogFolderPropertiesBinding | ||
| 14 | import org.yuzu.yuzu_emu.model.GameDir | ||
| 15 | import org.yuzu.yuzu_emu.model.GamesViewModel | ||
| 16 | import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable | ||
| 17 | |||
| 18 | class GameFolderPropertiesDialogFragment : DialogFragment() { | ||
| 19 | private val gamesViewModel: GamesViewModel by activityViewModels() | ||
| 20 | |||
| 21 | private var deepScan = false | ||
| 22 | |||
| 23 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { | ||
| 24 | val binding = DialogFolderPropertiesBinding.inflate(layoutInflater) | ||
| 25 | val gameDir = requireArguments().parcelable<GameDir>(GAME_DIR)!! | ||
| 26 | |||
| 27 | // Restore checkbox state | ||
| 28 | binding.deepScanSwitch.isChecked = | ||
| 29 | savedInstanceState?.getBoolean(DEEP_SCAN) ?: gameDir.deepScan | ||
| 30 | |||
| 31 | // Ensure that we can get the checkbox state even if the view is destroyed | ||
| 32 | deepScan = binding.deepScanSwitch.isChecked | ||
| 33 | binding.deepScanSwitch.setOnClickListener { | ||
| 34 | deepScan = binding.deepScanSwitch.isChecked | ||
| 35 | } | ||
| 36 | |||
| 37 | return MaterialAlertDialogBuilder(requireContext()) | ||
| 38 | .setView(binding.root) | ||
| 39 | .setTitle(R.string.game_folder_properties) | ||
| 40 | .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> | ||
| 41 | val folderIndex = gamesViewModel.folders.value.indexOf(gameDir) | ||
| 42 | if (folderIndex != -1) { | ||
| 43 | gamesViewModel.folders.value[folderIndex].deepScan = | ||
| 44 | binding.deepScanSwitch.isChecked | ||
| 45 | gamesViewModel.updateGameDirs() | ||
| 46 | } | ||
| 47 | } | ||
| 48 | .setNegativeButton(android.R.string.cancel, null) | ||
| 49 | .show() | ||
| 50 | } | ||
| 51 | |||
| 52 | override fun onSaveInstanceState(outState: Bundle) { | ||
| 53 | super.onSaveInstanceState(outState) | ||
| 54 | outState.putBoolean(DEEP_SCAN, deepScan) | ||
| 55 | } | ||
| 56 | |||
| 57 | companion object { | ||
| 58 | const val TAG = "GameFolderPropertiesDialogFragment" | ||
| 59 | |||
| 60 | private const val GAME_DIR = "GameDir" | ||
| 61 | |||
| 62 | private const val DEEP_SCAN = "DeepScan" | ||
| 63 | |||
| 64 | fun newInstance(gameDir: GameDir): GameFolderPropertiesDialogFragment { | ||
| 65 | val args = Bundle() | ||
| 66 | args.putParcelable(GAME_DIR, gameDir) | ||
| 67 | val fragment = GameFolderPropertiesDialogFragment() | ||
| 68 | fragment.arguments = args | ||
| 69 | return fragment | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt new file mode 100644 index 000000000..341a37fdb --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.fragments | ||
| 5 | |||
| 6 | import android.content.Intent | ||
| 7 | import android.os.Bundle | ||
| 8 | import android.view.LayoutInflater | ||
| 9 | import android.view.View | ||
| 10 | import android.view.ViewGroup | ||
| 11 | import androidx.core.view.ViewCompat | ||
| 12 | import androidx.core.view.WindowInsetsCompat | ||
| 13 | import androidx.core.view.updatePadding | ||
| 14 | import androidx.fragment.app.Fragment | ||
| 15 | import androidx.fragment.app.activityViewModels | ||
| 16 | import androidx.lifecycle.Lifecycle | ||
| 17 | import androidx.lifecycle.lifecycleScope | ||
| 18 | import androidx.lifecycle.repeatOnLifecycle | ||
| 19 | import androidx.navigation.findNavController | ||
| 20 | import androidx.recyclerview.widget.GridLayoutManager | ||
| 21 | import com.google.android.material.transition.MaterialSharedAxis | ||
| 22 | import kotlinx.coroutines.launch | ||
| 23 | import org.yuzu.yuzu_emu.R | ||
| 24 | import org.yuzu.yuzu_emu.adapters.FolderAdapter | ||
| 25 | import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding | ||
| 26 | import org.yuzu.yuzu_emu.model.GamesViewModel | ||
| 27 | import org.yuzu.yuzu_emu.model.HomeViewModel | ||
| 28 | import org.yuzu.yuzu_emu.ui.main.MainActivity | ||
| 29 | |||
| 30 | class GameFoldersFragment : Fragment() { | ||
| 31 | private var _binding: FragmentFoldersBinding? = null | ||
| 32 | private val binding get() = _binding!! | ||
| 33 | |||
| 34 | private val homeViewModel: HomeViewModel by activityViewModels() | ||
| 35 | private val gamesViewModel: GamesViewModel by activityViewModels() | ||
| 36 | |||
| 37 | override fun onCreate(savedInstanceState: Bundle?) { | ||
| 38 | super.onCreate(savedInstanceState) | ||
| 39 | enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) | ||
| 40 | returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) | ||
| 41 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) | ||
| 42 | |||
| 43 | gamesViewModel.onOpenGameFoldersFragment() | ||
| 44 | } | ||
| 45 | |||
| 46 | override fun onCreateView( | ||
| 47 | inflater: LayoutInflater, | ||
| 48 | container: ViewGroup?, | ||
| 49 | savedInstanceState: Bundle? | ||
| 50 | ): View { | ||
| 51 | _binding = FragmentFoldersBinding.inflate(inflater) | ||
| 52 | return binding.root | ||
| 53 | } | ||
| 54 | |||
| 55 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
| 56 | super.onViewCreated(view, savedInstanceState) | ||
| 57 | homeViewModel.setNavigationVisibility(visible = false, animated = true) | ||
| 58 | homeViewModel.setStatusBarShadeVisibility(visible = false) | ||
| 59 | |||
| 60 | binding.toolbarFolders.setNavigationOnClickListener { | ||
| 61 | binding.root.findNavController().popBackStack() | ||
| 62 | } | ||
| 63 | |||
| 64 | binding.listFolders.apply { | ||
| 65 | layoutManager = GridLayoutManager( | ||
| 66 | requireContext(), | ||
| 67 | resources.getInteger(R.integer.grid_columns) | ||
| 68 | ) | ||
| 69 | adapter = FolderAdapter(requireActivity(), gamesViewModel) | ||
| 70 | } | ||
| 71 | |||
| 72 | viewLifecycleOwner.lifecycleScope.launch { | ||
| 73 | repeatOnLifecycle(Lifecycle.State.CREATED) { | ||
| 74 | gamesViewModel.folders.collect { | ||
| 75 | (binding.listFolders.adapter as FolderAdapter).submitList(it) | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | val mainActivity = requireActivity() as MainActivity | ||
| 81 | binding.buttonAdd.setOnClickListener { | ||
| 82 | mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) | ||
| 83 | } | ||
| 84 | |||
| 85 | setInsets() | ||
| 86 | } | ||
| 87 | |||
| 88 | override fun onStop() { | ||
| 89 | super.onStop() | ||
| 90 | gamesViewModel.onCloseGameFoldersFragment() | ||
| 91 | } | ||
| 92 | |||
| 93 | private fun setInsets() = | ||
| 94 | ViewCompat.setOnApplyWindowInsetsListener( | ||
| 95 | binding.root | ||
| 96 | ) { _: View, windowInsets: WindowInsetsCompat -> | ||
| 97 | val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) | ||
| 98 | val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) | ||
| 99 | |||
| 100 | val leftInsets = barInsets.left + cutoutInsets.left | ||
| 101 | val rightInsets = barInsets.right + cutoutInsets.right | ||
| 102 | |||
| 103 | val mlpToolbar = binding.toolbarFolders.layoutParams as ViewGroup.MarginLayoutParams | ||
| 104 | mlpToolbar.leftMargin = leftInsets | ||
| 105 | mlpToolbar.rightMargin = rightInsets | ||
| 106 | binding.toolbarFolders.layoutParams = mlpToolbar | ||
| 107 | |||
| 108 | val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) | ||
| 109 | val mlpFab = | ||
| 110 | binding.buttonAdd.layoutParams as ViewGroup.MarginLayoutParams | ||
| 111 | mlpFab.leftMargin = leftInsets + fabSpacing | ||
| 112 | mlpFab.rightMargin = rightInsets + fabSpacing | ||
| 113 | mlpFab.bottomMargin = barInsets.bottom + fabSpacing | ||
| 114 | binding.buttonAdd.layoutParams = mlpFab | ||
| 115 | |||
| 116 | val mlpListFolders = binding.listFolders.layoutParams as ViewGroup.MarginLayoutParams | ||
| 117 | mlpListFolders.leftMargin = leftInsets | ||
| 118 | mlpListFolders.rightMargin = rightInsets | ||
| 119 | binding.listFolders.layoutParams = mlpListFolders | ||
| 120 | |||
| 121 | binding.listFolders.updatePadding( | ||
| 122 | bottom = barInsets.bottom + | ||
| 123 | resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab) | ||
| 124 | ) | ||
| 125 | |||
| 126 | windowInsets | ||
| 127 | } | ||
| 128 | } | ||
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 4720daec4..3addc2e63 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 | |||
| @@ -127,18 +127,13 @@ class HomeSettingsFragment : Fragment() { | |||
| 127 | ) | 127 | ) |
| 128 | add( | 128 | add( |
| 129 | HomeSetting( | 129 | HomeSetting( |
| 130 | R.string.select_games_folder, | 130 | R.string.manage_game_folders, |
| 131 | R.string.select_games_folder_description, | 131 | R.string.select_games_folder_description, |
| 132 | R.drawable.ic_add, | 132 | R.drawable.ic_add, |
| 133 | { | 133 | { |
| 134 | mainActivity.getGamesDirectory.launch( | 134 | binding.root.findNavController() |
| 135 | Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data | 135 | .navigate(R.id.action_homeSettingsFragment_to_gameFoldersFragment) |
| 136 | ) | 136 | } |
| 137 | }, | ||
| 138 | { true }, | ||
| 139 | 0, | ||
| 140 | 0, | ||
| 141 | homeViewModel.gamesDir | ||
| 142 | ) | 137 | ) |
| 143 | ) | 138 | ) |
| 144 | add( | 139 | add( |
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/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt index d18ec6974..b88d2c038 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt | |||
| @@ -52,7 +52,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener | |||
| 52 | .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> | 52 | .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> |
| 53 | settingsViewModel.clickedItem!!.setting.reset() | 53 | settingsViewModel.clickedItem!!.setting.reset() |
| 54 | settingsViewModel.setAdapterItemChanged(position) | 54 | settingsViewModel.setAdapterItemChanged(position) |
| 55 | settingsViewModel.shouldSave = true | ||
| 56 | } | 55 | } |
| 57 | .setNegativeButton(android.R.string.cancel, null) | 56 | .setNegativeButton(android.R.string.cancel, null) |
| 58 | .create() | 57 | .create() |
| @@ -137,24 +136,17 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener | |||
| 137 | is SingleChoiceSetting -> { | 136 | is SingleChoiceSetting -> { |
| 138 | val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting | 137 | val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting |
| 139 | val value = getValueForSingleChoiceSelection(scSetting, which) | 138 | val value = getValueForSingleChoiceSelection(scSetting, which) |
| 140 | if (scSetting.selectedValue != value) { | ||
| 141 | settingsViewModel.shouldSave = true | ||
| 142 | } | ||
| 143 | scSetting.selectedValue = value | 139 | scSetting.selectedValue = value |
| 144 | } | 140 | } |
| 145 | 141 | ||
| 146 | is StringSingleChoiceSetting -> { | 142 | is StringSingleChoiceSetting -> { |
| 147 | val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting | 143 | val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting |
| 148 | val value = scSetting.getValueAt(which) | 144 | val value = scSetting.getValueAt(which) |
| 149 | if (scSetting.selectedValue != value) settingsViewModel.shouldSave = true | ||
| 150 | scSetting.selectedValue = value | 145 | scSetting.selectedValue = value |
| 151 | } | 146 | } |
| 152 | 147 | ||
| 153 | is SliderSetting -> { | 148 | is SliderSetting -> { |
| 154 | val sliderSetting = settingsViewModel.clickedItem as SliderSetting | 149 | val sliderSetting = settingsViewModel.clickedItem as SliderSetting |
| 155 | if (sliderSetting.selectedValue != settingsViewModel.sliderProgress.value) { | ||
| 156 | settingsViewModel.shouldSave = true | ||
| 157 | } | ||
| 158 | sliderSetting.selectedValue = settingsViewModel.sliderProgress.value | 150 | sliderSetting.selectedValue = settingsViewModel.sliderProgress.value |
| 159 | } | 151 | } |
| 160 | } | 152 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt index c66bb635a..c4277735d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt | |||
| @@ -42,7 +42,7 @@ import org.yuzu.yuzu_emu.model.SetupPage | |||
| 42 | import org.yuzu.yuzu_emu.model.StepState | 42 | import org.yuzu.yuzu_emu.model.StepState |
| 43 | import org.yuzu.yuzu_emu.ui.main.MainActivity | 43 | import org.yuzu.yuzu_emu.ui.main.MainActivity |
| 44 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization | 44 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization |
| 45 | import org.yuzu.yuzu_emu.utils.GameHelper | 45 | import org.yuzu.yuzu_emu.utils.NativeConfig |
| 46 | import org.yuzu.yuzu_emu.utils.ViewUtils | 46 | import org.yuzu.yuzu_emu.utils.ViewUtils |
| 47 | 47 | ||
| 48 | class SetupFragment : Fragment() { | 48 | class SetupFragment : Fragment() { |
| @@ -184,11 +184,7 @@ class SetupFragment : Fragment() { | |||
| 184 | R.string.add_games_warning_description, | 184 | R.string.add_games_warning_description, |
| 185 | R.string.add_games_warning_help, | 185 | R.string.add_games_warning_help, |
| 186 | { | 186 | { |
| 187 | val preferences = | 187 | if (NativeConfig.getGameDirs().isNotEmpty()) { |
| 188 | PreferenceManager.getDefaultSharedPreferences( | ||
| 189 | YuzuApplication.appContext | ||
| 190 | ) | ||
| 191 | if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) { | ||
| 192 | StepState.COMPLETE | 188 | StepState.COMPLETE |
| 193 | } else { | 189 | } else { |
| 194 | StepState.INCOMPLETE | 190 | StepState.INCOMPLETE |
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/GameDir.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameDir.kt new file mode 100644 index 000000000..274bc1c7b --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameDir.kt | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.model | ||
| 5 | |||
| 6 | import android.os.Parcelable | ||
| 7 | import kotlinx.parcelize.Parcelize | ||
| 8 | |||
| 9 | @Parcelize | ||
| 10 | data class GameDir( | ||
| 11 | val uriString: String, | ||
| 12 | var deepScan: Boolean | ||
| 13 | ) : Parcelable | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt index 8512ed17c..752d98c10 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt | |||
| @@ -12,6 +12,7 @@ import java.util.Locale | |||
| 12 | import kotlinx.coroutines.Dispatchers | 12 | import kotlinx.coroutines.Dispatchers |
| 13 | import kotlinx.coroutines.flow.MutableStateFlow | 13 | import kotlinx.coroutines.flow.MutableStateFlow |
| 14 | import kotlinx.coroutines.flow.StateFlow | 14 | import kotlinx.coroutines.flow.StateFlow |
| 15 | import kotlinx.coroutines.flow.asStateFlow | ||
| 15 | import kotlinx.coroutines.launch | 16 | import kotlinx.coroutines.launch |
| 16 | import kotlinx.coroutines.withContext | 17 | import kotlinx.coroutines.withContext |
| 17 | import kotlinx.serialization.decodeFromString | 18 | import kotlinx.serialization.decodeFromString |
| @@ -20,6 +21,7 @@ import org.yuzu.yuzu_emu.NativeLibrary | |||
| 20 | import org.yuzu.yuzu_emu.YuzuApplication | 21 | import org.yuzu.yuzu_emu.YuzuApplication |
| 21 | import org.yuzu.yuzu_emu.utils.GameHelper | 22 | import org.yuzu.yuzu_emu.utils.GameHelper |
| 22 | import org.yuzu.yuzu_emu.utils.GameMetadata | 23 | import org.yuzu.yuzu_emu.utils.GameMetadata |
| 24 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 23 | 25 | ||
| 24 | class GamesViewModel : ViewModel() { | 26 | class GamesViewModel : ViewModel() { |
| 25 | val games: StateFlow<List<Game>> get() = _games | 27 | val games: StateFlow<List<Game>> get() = _games |
| @@ -40,6 +42,9 @@ class GamesViewModel : ViewModel() { | |||
| 40 | val searchFocused: StateFlow<Boolean> get() = _searchFocused | 42 | val searchFocused: StateFlow<Boolean> get() = _searchFocused |
| 41 | private val _searchFocused = MutableStateFlow(false) | 43 | private val _searchFocused = MutableStateFlow(false) |
| 42 | 44 | ||
| 45 | private val _folders = MutableStateFlow(mutableListOf<GameDir>()) | ||
| 46 | val folders = _folders.asStateFlow() | ||
| 47 | |||
| 43 | init { | 48 | init { |
| 44 | // Ensure keys are loaded so that ROM metadata can be decrypted. | 49 | // Ensure keys are loaded so that ROM metadata can be decrypted. |
| 45 | NativeLibrary.reloadKeys() | 50 | NativeLibrary.reloadKeys() |
| @@ -50,6 +55,7 @@ class GamesViewModel : ViewModel() { | |||
| 50 | 55 | ||
| 51 | viewModelScope.launch { | 56 | viewModelScope.launch { |
| 52 | withContext(Dispatchers.IO) { | 57 | withContext(Dispatchers.IO) { |
| 58 | getGameDirs() | ||
| 53 | if (storedGames!!.isNotEmpty()) { | 59 | if (storedGames!!.isNotEmpty()) { |
| 54 | val deserializedGames = mutableSetOf<Game>() | 60 | val deserializedGames = mutableSetOf<Game>() |
| 55 | storedGames.forEach { | 61 | storedGames.forEach { |
| @@ -104,7 +110,7 @@ class GamesViewModel : ViewModel() { | |||
| 104 | _searchFocused.value = searchFocused | 110 | _searchFocused.value = searchFocused |
| 105 | } | 111 | } |
| 106 | 112 | ||
| 107 | fun reloadGames(directoryChanged: Boolean) { | 113 | fun reloadGames(directoriesChanged: Boolean) { |
| 108 | if (isReloading.value) { | 114 | if (isReloading.value) { |
| 109 | return | 115 | return |
| 110 | } | 116 | } |
| @@ -116,10 +122,61 @@ class GamesViewModel : ViewModel() { | |||
| 116 | setGames(GameHelper.getGames()) | 122 | setGames(GameHelper.getGames()) |
| 117 | _isReloading.value = false | 123 | _isReloading.value = false |
| 118 | 124 | ||
| 119 | if (directoryChanged) { | 125 | if (directoriesChanged) { |
| 120 | setShouldSwapData(true) | 126 | setShouldSwapData(true) |
| 121 | } | 127 | } |
| 122 | } | 128 | } |
| 123 | } | 129 | } |
| 124 | } | 130 | } |
| 131 | |||
| 132 | fun addFolder(gameDir: GameDir) = | ||
| 133 | viewModelScope.launch { | ||
| 134 | withContext(Dispatchers.IO) { | ||
| 135 | NativeConfig.addGameDir(gameDir) | ||
| 136 | getGameDirs() | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | fun removeFolder(gameDir: GameDir) = | ||
| 141 | viewModelScope.launch { | ||
| 142 | withContext(Dispatchers.IO) { | ||
| 143 | val gameDirs = _folders.value.toMutableList() | ||
| 144 | val removedDirIndex = gameDirs.indexOf(gameDir) | ||
| 145 | if (removedDirIndex != -1) { | ||
| 146 | gameDirs.removeAt(removedDirIndex) | ||
| 147 | NativeConfig.setGameDirs(gameDirs.toTypedArray()) | ||
| 148 | getGameDirs() | ||
| 149 | } | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | fun updateGameDirs() = | ||
| 154 | viewModelScope.launch { | ||
| 155 | withContext(Dispatchers.IO) { | ||
| 156 | NativeConfig.setGameDirs(_folders.value.toTypedArray()) | ||
| 157 | getGameDirs() | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | fun onOpenGameFoldersFragment() = | ||
| 162 | viewModelScope.launch { | ||
| 163 | withContext(Dispatchers.IO) { | ||
| 164 | getGameDirs() | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | fun onCloseGameFoldersFragment() = | ||
| 169 | viewModelScope.launch { | ||
| 170 | withContext(Dispatchers.IO) { | ||
| 171 | getGameDirs(true) | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | private fun getGameDirs(reloadList: Boolean = false) { | ||
| 176 | val gameDirs = NativeConfig.getGameDirs() | ||
| 177 | _folders.value = gameDirs.toMutableList() | ||
| 178 | if (reloadList) { | ||
| 179 | reloadGames(true) | ||
| 180 | } | ||
| 181 | } | ||
| 125 | } | 182 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt index 756f76721..251b5a667 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt | |||
| @@ -3,15 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.model | 4 | package org.yuzu.yuzu_emu.model |
| 5 | 5 | ||
| 6 | import android.net.Uri | ||
| 7 | import androidx.fragment.app.FragmentActivity | ||
| 8 | import androidx.lifecycle.ViewModel | 6 | import androidx.lifecycle.ViewModel |
| 9 | import androidx.lifecycle.ViewModelProvider | ||
| 10 | import androidx.preference.PreferenceManager | ||
| 11 | import kotlinx.coroutines.flow.MutableStateFlow | 7 | import kotlinx.coroutines.flow.MutableStateFlow |
| 12 | import kotlinx.coroutines.flow.StateFlow | 8 | import kotlinx.coroutines.flow.StateFlow |
| 13 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 14 | import org.yuzu.yuzu_emu.utils.GameHelper | ||
| 15 | 9 | ||
| 16 | class HomeViewModel : ViewModel() { | 10 | class HomeViewModel : ViewModel() { |
| 17 | val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible | 11 | val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible |
| @@ -23,14 +17,6 @@ class HomeViewModel : ViewModel() { | |||
| 23 | val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward | 17 | val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward |
| 24 | private val _shouldPageForward = MutableStateFlow(false) | 18 | private val _shouldPageForward = MutableStateFlow(false) |
| 25 | 19 | ||
| 26 | val gamesDir: StateFlow<String> get() = _gamesDir | ||
| 27 | private val _gamesDir = MutableStateFlow( | ||
| 28 | Uri.parse( | ||
| 29 | PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | ||
| 30 | .getString(GameHelper.KEY_GAME_PATH, "") | ||
| 31 | ).path ?: "" | ||
| 32 | ) | ||
| 33 | |||
| 34 | var navigatedToSetup = false | 20 | var navigatedToSetup = false |
| 35 | 21 | ||
| 36 | fun setNavigationVisibility(visible: Boolean, animated: Boolean) { | 22 | fun setNavigationVisibility(visible: Boolean, animated: Boolean) { |
| @@ -50,9 +36,4 @@ class HomeViewModel : ViewModel() { | |||
| 50 | fun setShouldPageForward(pageForward: Boolean) { | 36 | fun setShouldPageForward(pageForward: Boolean) { |
| 51 | _shouldPageForward.value = pageForward | 37 | _shouldPageForward.value = pageForward |
| 52 | } | 38 | } |
| 53 | |||
| 54 | fun setGamesDir(activity: FragmentActivity, dir: String) { | ||
| 55 | ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true) | ||
| 56 | _gamesDir.value = dir | ||
| 57 | } | ||
| 58 | } | 39 | } |
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 6f947674e..ccc981e95 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 | |||
| @@ -13,8 +13,6 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | |||
| 13 | class SettingsViewModel : ViewModel() { | 13 | class SettingsViewModel : ViewModel() { |
| 14 | var game: Game? = null | 14 | var game: Game? = null |
| 15 | 15 | ||
| 16 | var shouldSave = false | ||
| 17 | |||
| 18 | var clickedItem: SettingsItem? = null | 16 | var clickedItem: SettingsItem? = null |
| 19 | 17 | ||
| 20 | val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate | 18 | val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate |
| @@ -73,6 +71,5 @@ class SettingsViewModel : ViewModel() { | |||
| 73 | 71 | ||
| 74 | fun clear() { | 72 | fun clear() { |
| 75 | game = null | 73 | game = null |
| 76 | shouldSave = false | ||
| 77 | } | 74 | } |
| 78 | } | 75 | } |
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 211b7cf69..16323a316 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,8 +39,8 @@ 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 |
| 43 | import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment | ||
| 46 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment | 44 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment |
| 47 | import org.yuzu.yuzu_emu.fragments.MessageDialogFragment | 45 | import org.yuzu.yuzu_emu.fragments.MessageDialogFragment |
| 48 | import org.yuzu.yuzu_emu.getPublicFilesDir | 46 | import org.yuzu.yuzu_emu.getPublicFilesDir |
| @@ -53,9 +51,6 @@ import org.yuzu.yuzu_emu.model.TaskViewModel | |||
| 53 | import org.yuzu.yuzu_emu.utils.* | 51 | import org.yuzu.yuzu_emu.utils.* |
| 54 | import java.io.BufferedInputStream | 52 | import java.io.BufferedInputStream |
| 55 | import java.io.BufferedOutputStream | 53 | 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 | 54 | import java.util.zip.ZipEntry |
| 60 | import java.util.zip.ZipInputStream | 55 | import java.util.zip.ZipInputStream |
| 61 | 56 | ||
| @@ -73,7 +68,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 73 | 68 | ||
| 74 | // Get first subfolder in saves folder (should be the user folder) | 69 | // Get first subfolder in saves folder (should be the user folder) |
| 75 | val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" | 70 | val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" |
| 76 | private var lastZipCreated: File? = null | ||
| 77 | 71 | ||
| 78 | override fun onCreate(savedInstanceState: Bundle?) { | 72 | override fun onCreate(savedInstanceState: Bundle?) { |
| 79 | val splashScreen = installSplashScreen() | 73 | val splashScreen = installSplashScreen() |
| @@ -259,6 +253,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 259 | super.onResume() | 253 | super.onResume() |
| 260 | } | 254 | } |
| 261 | 255 | ||
| 256 | override fun onStop() { | ||
| 257 | super.onStop() | ||
| 258 | CoroutineScope(Dispatchers.IO).launch { | ||
| 259 | NativeConfig.saveSettings() | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 262 | override fun onDestroy() { | 263 | override fun onDestroy() { |
| 263 | EmulationActivity.stopForegroundService(this) | 264 | EmulationActivity.stopForegroundService(this) |
| 264 | super.onDestroy() | 265 | super.onDestroy() |
| @@ -300,20 +301,19 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 300 | Intent.FLAG_GRANT_READ_URI_PERMISSION | 301 | Intent.FLAG_GRANT_READ_URI_PERMISSION |
| 301 | ) | 302 | ) |
| 302 | 303 | ||
| 303 | // When a new directory is picked, we currently will reset the existing games | 304 | val uriString = result.toString() |
| 304 | // database. This effectively means that only one game directory is supported. | 305 | val folder = gamesViewModel.folders.value.firstOrNull { it.uriString == uriString } |
| 305 | PreferenceManager.getDefaultSharedPreferences(applicationContext).edit() | 306 | if (folder != null) { |
| 306 | .putString(GameHelper.KEY_GAME_PATH, result.toString()) | 307 | Toast.makeText( |
| 307 | .apply() | 308 | applicationContext, |
| 308 | 309 | R.string.folder_already_added, | |
| 309 | Toast.makeText( | 310 | Toast.LENGTH_SHORT |
| 310 | applicationContext, | 311 | ).show() |
| 311 | R.string.games_dir_selected, | 312 | return |
| 312 | Toast.LENGTH_LONG | 313 | } |
| 313 | ).show() | ||
| 314 | 314 | ||
| 315 | gamesViewModel.reloadGames(true) | 315 | AddGameFolderDialogFragment.newInstance(uriString) |
| 316 | homeViewModel.setGamesDir(this, result.path!!) | 316 | .show(supportFragmentManager, AddGameFolderDialogFragment.TAG) |
| 317 | } | 317 | } |
| 318 | 318 | ||
| 319 | val getProdKey = | 319 | val getProdKey = |
| @@ -632,6 +632,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 632 | } | 632 | } |
| 633 | 633 | ||
| 634 | // Clear existing user data | 634 | // Clear existing user data |
| 635 | NativeConfig.unloadConfig() | ||
| 635 | File(DirectoryInitialization.userDirectory!!).deleteRecursively() | 636 | File(DirectoryInitialization.userDirectory!!).deleteRecursively() |
| 636 | 637 | ||
| 637 | // Copy archive to internal storage | 638 | // Copy archive to internal storage |
| @@ -650,6 +651,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 650 | 651 | ||
| 651 | // Reinitialize relevant data | 652 | // Reinitialize relevant data |
| 652 | NativeLibrary.initializeSystem(true) | 653 | NativeLibrary.initializeSystem(true) |
| 654 | NativeConfig.initializeConfig() | ||
| 653 | gamesViewModel.reloadGames(false) | 655 | gamesViewModel.reloadGames(false) |
| 654 | 656 | ||
| 655 | return@newInstance getString(R.string.user_data_import_success) | 657 | return@newInstance getString(R.string.user_data_import_success) |
| @@ -657,74 +659,30 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 657 | } | 659 | } |
| 658 | 660 | ||
| 659 | /** | 661 | /** |
| 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. | 662 | * Exports the save file located in the given folder path by creating a zip file and sharing it via intent. |
| 692 | */ | 663 | */ |
| 693 | fun exportSave() { | 664 | val exportSaves = registerForActivityResult( |
| 694 | CoroutineScope(Dispatchers.IO).launch { | 665 | ActivityResultContracts.CreateDocument("application/zip") |
| 695 | val wasZipCreated = zipSave() | 666 | ) { result -> |
| 696 | val lastZipFile = lastZipCreated | 667 | if (result == null) { |
| 697 | if (!wasZipCreated || lastZipFile == null) { | 668 | return@registerForActivityResult |
| 698 | withContext(Dispatchers.Main) { | 669 | } |
| 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 | 670 | ||
| 708 | withContext(Dispatchers.Main) { | 671 | IndeterminateProgressDialogFragment.newInstance( |
| 709 | val file = DocumentFile.fromSingleUri( | 672 | this, |
| 710 | this@MainActivity, | 673 | R.string.save_files_exporting, |
| 711 | DocumentsContract.buildDocumentUri( | 674 | false |
| 712 | DocumentProvider.AUTHORITY, | 675 | ) { |
| 713 | "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}" | 676 | val zipResult = FileUtil.zipFromInternalStorage( |
| 714 | ) | 677 | File(savesFolderRoot), |
| 715 | )!! | 678 | savesFolderRoot, |
| 716 | val intent = Intent(Intent.ACTION_SEND) | 679 | BufferedOutputStream(contentResolver.openOutputStream(result)) |
| 717 | .setDataAndType(file.uri, "application/zip") | 680 | ) |
| 718 | .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) | 681 | return@newInstance when (zipResult) { |
| 719 | .putExtra(Intent.EXTRA_STREAM, file.uri) | 682 | TaskState.Completed -> getString(R.string.export_success) |
| 720 | startForResultExportSave.launch( | 683 | TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed) |
| 721 | Intent.createChooser( | ||
| 722 | intent, | ||
| 723 | getString(R.string.share_save_file) | ||
| 724 | ) | ||
| 725 | ) | ||
| 726 | } | 684 | } |
| 727 | } | 685 | }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) |
| 728 | } | 686 | } |
| 729 | 687 | ||
| 730 | private val startForResultExportSave = | 688 | 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 5e9a1176a..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 | |||
| @@ -16,6 +16,7 @@ object DirectoryInitialization { | |||
| 16 | if (!areDirectoriesReady) { | 16 | if (!areDirectoriesReady) { |
| 17 | initializeInternalStorage() | 17 | initializeInternalStorage() |
| 18 | NativeLibrary.initializeSystem(false) | 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/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt index 8c3268e9c..bbe7bfa92 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt | |||
| @@ -364,6 +364,27 @@ object FileUtil { | |||
| 364 | .lowercase() | 364 | .lowercase() |
| 365 | } | 365 | } |
| 366 | 366 | ||
| 367 | fun isTreeUriValid(uri: Uri): Boolean { | ||
| 368 | val resolver = context.contentResolver | ||
| 369 | val columns = arrayOf( | ||
| 370 | DocumentsContract.Document.COLUMN_DOCUMENT_ID, | ||
| 371 | DocumentsContract.Document.COLUMN_DISPLAY_NAME, | ||
| 372 | DocumentsContract.Document.COLUMN_MIME_TYPE | ||
| 373 | ) | ||
| 374 | return try { | ||
| 375 | val docId: String = if (isRootTreeUri(uri)) { | ||
| 376 | DocumentsContract.getTreeDocumentId(uri) | ||
| 377 | } else { | ||
| 378 | DocumentsContract.getDocumentId(uri) | ||
| 379 | } | ||
| 380 | val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, docId) | ||
| 381 | resolver.query(childrenUri, columns, null, null, null) | ||
| 382 | true | ||
| 383 | } catch (_: Exception) { | ||
| 384 | false | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 367 | @Throws(IOException::class) | 388 | @Throws(IOException::class) |
| 368 | fun getStringFromFile(file: File): String = | 389 | fun getStringFromFile(file: File): String = |
| 369 | String(file.readBytes(), StandardCharsets.UTF_8) | 390 | String(file.readBytes(), StandardCharsets.UTF_8) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index e6aca6b44..55010dc59 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt | |||
| @@ -11,10 +11,11 @@ import kotlinx.serialization.json.Json | |||
| 11 | import org.yuzu.yuzu_emu.NativeLibrary | 11 | import org.yuzu.yuzu_emu.NativeLibrary |
| 12 | import org.yuzu.yuzu_emu.YuzuApplication | 12 | import org.yuzu.yuzu_emu.YuzuApplication |
| 13 | import org.yuzu.yuzu_emu.model.Game | 13 | import org.yuzu.yuzu_emu.model.Game |
| 14 | import org.yuzu.yuzu_emu.model.GameDir | ||
| 14 | import org.yuzu.yuzu_emu.model.MinimalDocumentFile | 15 | import org.yuzu.yuzu_emu.model.MinimalDocumentFile |
| 15 | 16 | ||
| 16 | object GameHelper { | 17 | object GameHelper { |
| 17 | const val KEY_GAME_PATH = "game_path" | 18 | private const val KEY_OLD_GAME_PATH = "game_path" |
| 18 | const val KEY_GAMES = "Games" | 19 | const val KEY_GAMES = "Games" |
| 19 | 20 | ||
| 20 | private lateinit var preferences: SharedPreferences | 21 | private lateinit var preferences: SharedPreferences |
| @@ -22,15 +23,43 @@ object GameHelper { | |||
| 22 | fun getGames(): List<Game> { | 23 | fun getGames(): List<Game> { |
| 23 | val games = mutableListOf<Game>() | 24 | val games = mutableListOf<Game>() |
| 24 | val context = YuzuApplication.appContext | 25 | val context = YuzuApplication.appContext |
| 25 | val gamesDir = | ||
| 26 | PreferenceManager.getDefaultSharedPreferences(context).getString(KEY_GAME_PATH, "") | ||
| 27 | val gamesUri = Uri.parse(gamesDir) | ||
| 28 | preferences = PreferenceManager.getDefaultSharedPreferences(context) | 26 | preferences = PreferenceManager.getDefaultSharedPreferences(context) |
| 29 | 27 | ||
| 28 | val gameDirs = mutableListOf<GameDir>() | ||
| 29 | val oldGamesDir = preferences.getString(KEY_OLD_GAME_PATH, "") ?: "" | ||
| 30 | if (oldGamesDir.isNotEmpty()) { | ||
| 31 | gameDirs.add(GameDir(oldGamesDir, true)) | ||
| 32 | preferences.edit().remove(KEY_OLD_GAME_PATH).apply() | ||
| 33 | } | ||
| 34 | gameDirs.addAll(NativeConfig.getGameDirs()) | ||
| 35 | |||
| 30 | // Ensure keys are loaded so that ROM metadata can be decrypted. | 36 | // Ensure keys are loaded so that ROM metadata can be decrypted. |
| 31 | NativeLibrary.reloadKeys() | 37 | NativeLibrary.reloadKeys() |
| 32 | 38 | ||
| 33 | addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3) | 39 | val badDirs = mutableListOf<Int>() |
| 40 | gameDirs.forEachIndexed { index: Int, gameDir: GameDir -> | ||
| 41 | val gameDirUri = Uri.parse(gameDir.uriString) | ||
| 42 | val isValid = FileUtil.isTreeUriValid(gameDirUri) | ||
| 43 | if (isValid) { | ||
| 44 | addGamesRecursive( | ||
| 45 | games, | ||
| 46 | FileUtil.listFiles(gameDirUri), | ||
| 47 | if (gameDir.deepScan) 3 else 1 | ||
| 48 | ) | ||
| 49 | } else { | ||
| 50 | badDirs.add(index) | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | // Remove all game dirs with insufficient permissions from config | ||
| 55 | if (badDirs.isNotEmpty()) { | ||
| 56 | var offset = 0 | ||
| 57 | badDirs.forEach { | ||
| 58 | gameDirs.removeAt(it - offset) | ||
| 59 | offset++ | ||
| 60 | } | ||
| 61 | } | ||
| 62 | NativeConfig.setGameDirs(gameDirs.toTypedArray()) | ||
| 34 | 63 | ||
| 35 | // Cache list of games found on disk | 64 | // Cache list of games found on disk |
| 36 | val serializedGames = mutableSetOf<String>() | 65 | val serializedGames = mutableSetOf<String>() |
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 47bde5081..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 | ||
| @@ -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/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt index 9425f8b99..f4e1bb13f 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 | |||
| @@ -3,7 +3,33 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.utils | 4 | package org.yuzu.yuzu_emu.utils |
| 5 | 5 | ||
| 6 | import org.yuzu.yuzu_emu.model.GameDir | ||
| 7 | |||
| 6 | object NativeConfig { | 8 | object NativeConfig { |
| 9 | /** | ||
| 10 | * Creates a Config object and opens the emulation config. | ||
| 11 | */ | ||
| 12 | @Synchronized | ||
| 13 | external fun initializeConfig() | ||
| 14 | |||
| 15 | /** | ||
| 16 | * Destroys the stored config object. This automatically saves the existing config. | ||
| 17 | */ | ||
| 18 | @Synchronized | ||
| 19 | external fun unloadConfig() | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Reads values saved to the config file and saves them. | ||
| 23 | */ | ||
| 24 | @Synchronized | ||
| 25 | external fun reloadSettings() | ||
| 26 | |||
| 27 | /** | ||
| 28 | * Saves settings values in memory to disk. | ||
| 29 | */ | ||
| 30 | @Synchronized | ||
| 31 | external fun saveSettings() | ||
| 32 | |||
| 7 | external fun getBoolean(key: String, getDefault: Boolean): Boolean | 33 | external fun getBoolean(key: String, getDefault: Boolean): Boolean |
| 8 | external fun setBoolean(key: String, value: Boolean) | 34 | external fun setBoolean(key: String, value: Boolean) |
| 9 | 35 | ||
| @@ -30,4 +56,22 @@ object NativeConfig { | |||
| 30 | external fun getConfigHeader(category: Int): String | 56 | external fun getConfigHeader(category: Int): String |
| 31 | 57 | ||
| 32 | external fun getPairedSettingKey(key: String): String | 58 | external fun getPairedSettingKey(key: String): String |
| 59 | |||
| 60 | /** | ||
| 61 | * Gets every [GameDir] in AndroidSettings::values.game_dirs | ||
| 62 | */ | ||
| 63 | @Synchronized | ||
| 64 | external fun getGameDirs(): Array<GameDir> | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Clears the AndroidSettings::values.game_dirs array and replaces them with the provided array | ||
| 68 | */ | ||
| 69 | @Synchronized | ||
| 70 | external fun setGameDirs(dirs: Array<GameDir>) | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Adds a single [GameDir] to the AndroidSettings::values.game_dirs array | ||
| 74 | */ | ||
| 75 | @Synchronized | ||
| 76 | external fun addGameDir(dir: GameDir) | ||
| 33 | } | 77 | } |
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 88a570f68..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,15 +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 |
| 21 | native_log.cpp | 18 | native_log.cpp |
| 19 | android_config.cpp | ||
| 20 | android_config.h | ||
| 22 | ) | 21 | ) |
| 23 | 22 | ||
| 24 | set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) | 23 | set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) |
| 25 | 24 | ||
| 26 | 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) |
| 27 | 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) |
| 28 | if (ARCHITECTURE_arm64) | 27 | if (ARCHITECTURE_arm64) |
| 29 | target_link_libraries(yuzu-android PRIVATE adrenotools) | 28 | target_link_libraries(yuzu-android PRIVATE adrenotools) |
| 30 | 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..767d8ea83 --- /dev/null +++ b/src/android/app/src/main/jni/android_config.cpp | |||
| @@ -0,0 +1,120 @@ | |||
| 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 | ReadUIValues(); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | void AndroidConfig::ReadAndroidUIValues() { | ||
| 42 | BeginGroup(Settings::TranslateCategory(Settings::Category::Android)); | ||
| 43 | |||
| 44 | ReadCategory(Settings::Category::Android); | ||
| 45 | |||
| 46 | EndGroup(); | ||
| 47 | } | ||
| 48 | |||
| 49 | void AndroidConfig::ReadUIValues() { | ||
| 50 | BeginGroup(Settings::TranslateCategory(Settings::Category::Ui)); | ||
| 51 | |||
| 52 | ReadPathValues(); | ||
| 53 | |||
| 54 | EndGroup(); | ||
| 55 | } | ||
| 56 | |||
| 57 | void AndroidConfig::ReadPathValues() { | ||
| 58 | BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); | ||
| 59 | |||
| 60 | const int gamedirs_size = BeginArray(std::string("gamedirs")); | ||
| 61 | for (int i = 0; i < gamedirs_size; ++i) { | ||
| 62 | SetArrayIndex(i); | ||
| 63 | AndroidSettings::GameDir game_dir; | ||
| 64 | game_dir.path = ReadStringSetting(std::string("path")); | ||
| 65 | game_dir.deep_scan = | ||
| 66 | ReadBooleanSetting(std::string("deep_scan"), std::make_optional(false)); | ||
| 67 | AndroidSettings::values.game_dirs.push_back(game_dir); | ||
| 68 | } | ||
| 69 | EndArray(); | ||
| 70 | |||
| 71 | EndGroup(); | ||
| 72 | } | ||
| 73 | |||
| 74 | void AndroidConfig::SaveAndroidValues() { | ||
| 75 | if (global) { | ||
| 76 | SaveAndroidUIValues(); | ||
| 77 | SaveUIValues(); | ||
| 78 | } | ||
| 79 | |||
| 80 | WriteToIni(); | ||
| 81 | } | ||
| 82 | |||
| 83 | void AndroidConfig::SaveAndroidUIValues() { | ||
| 84 | BeginGroup(Settings::TranslateCategory(Settings::Category::Android)); | ||
| 85 | |||
| 86 | WriteCategory(Settings::Category::Android); | ||
| 87 | |||
| 88 | EndGroup(); | ||
| 89 | } | ||
| 90 | |||
| 91 | void AndroidConfig::SaveUIValues() { | ||
| 92 | BeginGroup(Settings::TranslateCategory(Settings::Category::Ui)); | ||
| 93 | |||
| 94 | SavePathValues(); | ||
| 95 | |||
| 96 | EndGroup(); | ||
| 97 | } | ||
| 98 | |||
| 99 | void AndroidConfig::SavePathValues() { | ||
| 100 | BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); | ||
| 101 | |||
| 102 | BeginArray(std::string("gamedirs")); | ||
| 103 | for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) { | ||
| 104 | SetArrayIndex(i); | ||
| 105 | const auto& game_dir = AndroidSettings::values.game_dirs[i]; | ||
| 106 | WriteSetting(std::string("path"), game_dir.path); | ||
| 107 | WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false)); | ||
| 108 | } | ||
| 109 | EndArray(); | ||
| 110 | |||
| 111 | EndGroup(); | ||
| 112 | } | ||
| 113 | |||
| 114 | std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) { | ||
| 115 | auto& map = Settings::values.linkage.by_category; | ||
| 116 | if (map.contains(category)) { | ||
| 117 | return Settings::values.linkage.by_category[category]; | ||
| 118 | } | ||
| 119 | return AndroidSettings::values.linkage.by_category[category]; | ||
| 120 | } | ||
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..f490be016 --- /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 37bc33918..fc0523206 100644 --- a/src/android/app/src/main/jni/uisettings.h +++ b/src/android/app/src/main/jni/android_settings.h | |||
| @@ -9,9 +9,17 @@ | |||
| 9 | 9 | ||
| 10 | namespace AndroidSettings { | 10 | namespace AndroidSettings { |
| 11 | 11 | ||
| 12 | struct GameDir { | ||
| 13 | std::string path; | ||
| 14 | bool deep_scan = false; | ||
| 15 | }; | ||
| 16 | |||
| 12 | struct Values { | 17 | struct Values { |
| 13 | Settings::Linkage linkage; | 18 | Settings::Linkage linkage; |
| 14 | 19 | ||
| 20 | // Path settings | ||
| 21 | std::vector<GameDir> game_dirs; | ||
| 22 | |||
| 15 | // Android | 23 | // Android |
| 16 | Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture", | 24 | Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture", |
| 17 | Settings::Category::Android}; | 25 | Settings::Category::Android}; |
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/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp index 960abf95a..a56ed5662 100644 --- a/src/android/app/src/main/jni/id_cache.cpp +++ b/src/android/app/src/main/jni/id_cache.cpp | |||
| @@ -13,6 +13,8 @@ static JavaVM* s_java_vm; | |||
| 13 | static jclass s_native_library_class; | 13 | static jclass s_native_library_class; |
| 14 | static jclass s_disk_cache_progress_class; | 14 | static jclass s_disk_cache_progress_class; |
| 15 | static jclass s_load_callback_stage_class; | 15 | static jclass s_load_callback_stage_class; |
| 16 | static jclass s_game_dir_class; | ||
| 17 | static jmethodID s_game_dir_constructor; | ||
| 16 | static jmethodID s_exit_emulation_activity; | 18 | static jmethodID s_exit_emulation_activity; |
| 17 | static jmethodID s_disk_cache_load_progress; | 19 | static jmethodID s_disk_cache_load_progress; |
| 18 | static jmethodID s_on_emulation_started; | 20 | static jmethodID s_on_emulation_started; |
| @@ -53,6 +55,14 @@ jclass GetDiskCacheLoadCallbackStageClass() { | |||
| 53 | return s_load_callback_stage_class; | 55 | return s_load_callback_stage_class; |
| 54 | } | 56 | } |
| 55 | 57 | ||
| 58 | jclass GetGameDirClass() { | ||
| 59 | return s_game_dir_class; | ||
| 60 | } | ||
| 61 | |||
| 62 | jmethodID GetGameDirConstructor() { | ||
| 63 | return s_game_dir_constructor; | ||
| 64 | } | ||
| 65 | |||
| 56 | jmethodID GetExitEmulationActivity() { | 66 | jmethodID GetExitEmulationActivity() { |
| 57 | return s_exit_emulation_activity; | 67 | return s_exit_emulation_activity; |
| 58 | } | 68 | } |
| @@ -90,6 +100,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { | |||
| 90 | s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass( | 100 | s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass( |
| 91 | "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); | 101 | "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); |
| 92 | 102 | ||
| 103 | const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir"); | ||
| 104 | s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class)); | ||
| 105 | s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>", "(Ljava/lang/String;Z)V"); | ||
| 106 | env->DeleteLocalRef(game_dir_class); | ||
| 107 | |||
| 93 | // Initialize methods | 108 | // Initialize methods |
| 94 | s_exit_emulation_activity = | 109 | s_exit_emulation_activity = |
| 95 | env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); | 110 | env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); |
| @@ -120,6 +135,7 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) { | |||
| 120 | env->DeleteGlobalRef(s_native_library_class); | 135 | env->DeleteGlobalRef(s_native_library_class); |
| 121 | env->DeleteGlobalRef(s_disk_cache_progress_class); | 136 | env->DeleteGlobalRef(s_disk_cache_progress_class); |
| 122 | env->DeleteGlobalRef(s_load_callback_stage_class); | 137 | env->DeleteGlobalRef(s_load_callback_stage_class); |
| 138 | env->DeleteGlobalRef(s_game_dir_class); | ||
| 123 | 139 | ||
| 124 | // UnInitialize applets | 140 | // UnInitialize applets |
| 125 | SoftwareKeyboard::CleanupJNI(env); | 141 | SoftwareKeyboard::CleanupJNI(env); |
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h index b76158928..855649efa 100644 --- a/src/android/app/src/main/jni/id_cache.h +++ b/src/android/app/src/main/jni/id_cache.h | |||
| @@ -13,6 +13,8 @@ JNIEnv* GetEnvForThread(); | |||
| 13 | jclass GetNativeLibraryClass(); | 13 | jclass GetNativeLibraryClass(); |
| 14 | jclass GetDiskCacheProgressClass(); | 14 | jclass GetDiskCacheProgressClass(); |
| 15 | jclass GetDiskCacheLoadCallbackStageClass(); | 15 | jclass GetDiskCacheLoadCallbackStageClass(); |
| 16 | jclass GetGameDirClass(); | ||
| 17 | jmethodID GetGameDirConstructor(); | ||
| 16 | jmethodID GetExitEmulationActivity(); | 18 | jmethodID GetExitEmulationActivity(); |
| 17 | jmethodID GetDiskCacheLoadProgress(); | 19 | jmethodID GetDiskCacheLoadProgress(); |
| 18 | jmethodID GetOnEmulationStarted(); | 20 | jmethodID GetOnEmulationStarted(); |
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 64663b084..3d795b57f 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" |
| @@ -123,9 +123,6 @@ int EmulationSession::InstallFileToNand(std::string filename, std::string file_e | |||
| 123 | ErrorFilenameExtension = 4, | 123 | ErrorFilenameExtension = 4, |
| 124 | }; | 124 | }; |
| 125 | 125 | ||
| 126 | m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | ||
| 127 | m_system.GetFileSystemController().CreateFactories(*m_vfs); | ||
| 128 | |||
| 129 | [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp; | 126 | [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp; |
| 130 | if (file_extension == "nsp") { | 127 | if (file_extension == "nsp") { |
| 131 | nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); | 128 | nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); |
| @@ -664,8 +661,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c | |||
| 664 | 661 | ||
| 665 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, | 662 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, |
| 666 | jboolean reload) { | 663 | jboolean reload) { |
| 667 | // Create the default config.ini. | ||
| 668 | Config{}; | ||
| 669 | // Initialize the emulated system. | 664 | // Initialize the emulated system. |
| 670 | if (!reload) { | 665 | if (!reload) { |
| 671 | EmulationSession::GetInstance().System().Initialize(); | 666 | EmulationSession::GetInstance().System().Initialize(); |
| @@ -680,17 +675,6 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl | |||
| 680 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( | 675 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( |
| 681 | JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} | 676 | JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} |
| 682 | 677 | ||
| 683 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) { | ||
| 684 | Config{}; | ||
| 685 | } | ||
| 686 | |||
| 687 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz, | ||
| 688 | jstring j_game_id) { | ||
| 689 | std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); | ||
| 690 | |||
| 691 | env->ReleaseStringUTFChars(j_game_id, game_id.data()); | ||
| 692 | } | ||
| 693 | |||
| 694 | jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { | 678 | jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { |
| 695 | jdoubleArray j_stats = env->NewDoubleArray(4); | 679 | jdoubleArray j_stats = env->NewDoubleArray(4); |
| 696 | 680 | ||
| @@ -707,6 +691,14 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jcl | |||
| 707 | return j_stats; | 691 | return j_stats; |
| 708 | } | 692 | } |
| 709 | 693 | ||
| 694 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) { | ||
| 695 | if (Settings::IsNceEnabled()) { | ||
| 696 | return ToJString(env, "NCE"); | ||
| 697 | } | ||
| 698 | |||
| 699 | return ToJString(env, "JIT"); | ||
| 700 | } | ||
| 701 | |||
| 710 | void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env, | 702 | void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env, |
| 711 | jclass clazz, | 703 | jclass clazz, |
| 712 | jstring j_path) {} | 704 | jstring j_path) {} |
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp index 8a704960c..763b2164c 100644 --- a/src/android/app/src/main/jni/native_config.cpp +++ b/src/android/app/src/main/jni/native_config.cpp | |||
| @@ -5,11 +5,15 @@ | |||
| 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 | #include "jni/id_cache.h" |
| 12 | #include "uisettings.h" | 15 | |
| 16 | std::unique_ptr<AndroidConfig> config; | ||
| 13 | 17 | ||
| 14 | template <typename T> | 18 | template <typename T> |
| 15 | Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { | 19 | Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { |
| @@ -28,6 +32,22 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { | |||
| 28 | 32 | ||
| 29 | extern "C" { | 33 | extern "C" { |
| 30 | 34 | ||
| 35 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) { | ||
| 36 | config = std::make_unique<AndroidConfig>(); | ||
| 37 | } | ||
| 38 | |||
| 39 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) { | ||
| 40 | config.reset(); | ||
| 41 | } | ||
| 42 | |||
| 43 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) { | ||
| 44 | config->AndroidConfig::ReloadAllValues(); | ||
| 45 | } | ||
| 46 | |||
| 47 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) { | ||
| 48 | config->AndroidConfig::SaveAllValues(); | ||
| 49 | } | ||
| 50 | |||
| 31 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, | 51 | jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, |
| 32 | jstring jkey, jboolean getDefault) { | 52 | jstring jkey, jboolean getDefault) { |
| 33 | auto setting = getSetting<bool>(env, jkey); | 53 | auto setting = getSetting<bool>(env, jkey); |
| @@ -234,4 +254,55 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* e | |||
| 234 | return ToJString(env, setting->PairedSetting()->GetLabel()); | 254 | return ToJString(env, setting->PairedSetting()->GetLabel()); |
| 235 | } | 255 | } |
| 236 | 256 | ||
| 257 | jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) { | ||
| 258 | jclass gameDirClass = IDCache::GetGameDirClass(); | ||
| 259 | jmethodID gameDirConstructor = IDCache::GetGameDirConstructor(); | ||
| 260 | jobjectArray jgameDirArray = | ||
| 261 | env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr); | ||
| 262 | for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) { | ||
| 263 | jobject jgameDir = | ||
| 264 | env->NewObject(gameDirClass, gameDirConstructor, | ||
| 265 | ToJString(env, AndroidSettings::values.game_dirs[i].path), | ||
| 266 | static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan)); | ||
| 267 | env->SetObjectArrayElement(jgameDirArray, i, jgameDir); | ||
| 268 | } | ||
| 269 | return jgameDirArray; | ||
| 270 | } | ||
| 271 | |||
| 272 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject obj, | ||
| 273 | jobjectArray gameDirs) { | ||
| 274 | AndroidSettings::values.game_dirs.clear(); | ||
| 275 | int size = env->GetArrayLength(gameDirs); | ||
| 276 | |||
| 277 | if (size == 0) { | ||
| 278 | return; | ||
| 279 | } | ||
| 280 | |||
| 281 | jobject dir = env->GetObjectArrayElement(gameDirs, 0); | ||
| 282 | jclass gameDirClass = IDCache::GetGameDirClass(); | ||
| 283 | jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;"); | ||
| 284 | jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); | ||
| 285 | for (int i = 0; i < size; ++i) { | ||
| 286 | dir = env->GetObjectArrayElement(gameDirs, i); | ||
| 287 | jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField)); | ||
| 288 | jboolean jdeepScanBoolean = env->GetBooleanField(dir, deepScanBooleanField); | ||
| 289 | std::string uriString = GetJString(env, juriString); | ||
| 290 | AndroidSettings::values.game_dirs.push_back( | ||
| 291 | AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject obj, | ||
| 296 | jobject gameDir) { | ||
| 297 | jclass gameDirClass = IDCache::GetGameDirClass(); | ||
| 298 | jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;"); | ||
| 299 | jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); | ||
| 300 | |||
| 301 | jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField)); | ||
| 302 | jboolean jdeepScanBoolean = env->GetBooleanField(gameDir, deepScanBooleanField); | ||
| 303 | std::string uriString = GetJString(env, juriString); | ||
| 304 | AndroidSettings::values.game_dirs.push_back( | ||
| 305 | AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); | ||
| 306 | } | ||
| 307 | |||
| 237 | } // extern "C" | 308 | } // extern "C" |
diff --git a/src/android/app/src/main/res/layout/card_folder.xml b/src/android/app/src/main/res/layout/card_folder.xml new file mode 100644 index 000000000..4e0c04b6b --- /dev/null +++ b/src/android/app/src/main/res/layout/card_folder.xml | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <com.google.android.material.card.MaterialCardView 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 | style="?attr/materialCardViewOutlinedStyle" | ||
| 6 | android:layout_width="match_parent" | ||
| 7 | android:layout_height="wrap_content" | ||
| 8 | android:layout_marginHorizontal="16dp" | ||
| 9 | android:layout_marginVertical="12dp" | ||
| 10 | android:focusable="true"> | ||
| 11 | |||
| 12 | <androidx.constraintlayout.widget.ConstraintLayout | ||
| 13 | android:layout_width="match_parent" | ||
| 14 | android:layout_height="wrap_content" | ||
| 15 | android:orientation="horizontal" | ||
| 16 | android:padding="16dp" | ||
| 17 | android:layout_gravity="center_vertical" | ||
| 18 | android:animateLayoutChanges="true"> | ||
| 19 | |||
| 20 | <com.google.android.material.textview.MaterialTextView | ||
| 21 | android:id="@+id/path" | ||
| 22 | style="@style/TextAppearance.Material3.BodyLarge" | ||
| 23 | android:layout_width="0dp" | ||
| 24 | android:layout_height="wrap_content" | ||
| 25 | android:layout_gravity="center_vertical|start" | ||
| 26 | android:ellipsize="none" | ||
| 27 | android:marqueeRepeatLimit="marquee_forever" | ||
| 28 | android:requiresFadingEdge="horizontal" | ||
| 29 | android:singleLine="true" | ||
| 30 | android:textAlignment="viewStart" | ||
| 31 | app:layout_constraintBottom_toBottomOf="parent" | ||
| 32 | app:layout_constraintEnd_toStartOf="@+id/button_layout" | ||
| 33 | app:layout_constraintStart_toStartOf="parent" | ||
| 34 | app:layout_constraintTop_toTopOf="parent" | ||
| 35 | tools:text="@string/select_gpu_driver_default" /> | ||
| 36 | |||
| 37 | <LinearLayout | ||
| 38 | android:id="@+id/button_layout" | ||
| 39 | android:layout_width="wrap_content" | ||
| 40 | android:layout_height="wrap_content" | ||
| 41 | android:orientation="horizontal" | ||
| 42 | app:layout_constraintBottom_toBottomOf="parent" | ||
| 43 | app:layout_constraintEnd_toEndOf="parent" | ||
| 44 | app:layout_constraintTop_toTopOf="parent"> | ||
| 45 | |||
| 46 | <Button | ||
| 47 | android:id="@+id/button_edit" | ||
| 48 | style="@style/Widget.Material3.Button.IconButton" | ||
| 49 | android:layout_width="wrap_content" | ||
| 50 | android:layout_height="wrap_content" | ||
| 51 | android:contentDescription="@string/delete" | ||
| 52 | android:tooltipText="@string/edit" | ||
| 53 | app:icon="@drawable/ic_edit" | ||
| 54 | app:iconTint="?attr/colorControlNormal" /> | ||
| 55 | |||
| 56 | <Button | ||
| 57 | android:id="@+id/button_delete" | ||
| 58 | style="@style/Widget.Material3.Button.IconButton" | ||
| 59 | android:layout_width="wrap_content" | ||
| 60 | android:layout_height="wrap_content" | ||
| 61 | android:contentDescription="@string/delete" | ||
| 62 | android:tooltipText="@string/delete" | ||
| 63 | app:icon="@drawable/ic_delete" | ||
| 64 | app:iconTint="?attr/colorControlNormal" /> | ||
| 65 | |||
| 66 | </LinearLayout> | ||
| 67 | |||
| 68 | </androidx.constraintlayout.widget.ConstraintLayout> | ||
| 69 | |||
| 70 | </com.google.android.material.card.MaterialCardView> | ||
diff --git a/src/android/app/src/main/res/layout/dialog_add_folder.xml b/src/android/app/src/main/res/layout/dialog_add_folder.xml new file mode 100644 index 000000000..01f95e868 --- /dev/null +++ b/src/android/app/src/main/res/layout/dialog_add_folder.xml | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | xmlns:tools="http://schemas.android.com/tools" | ||
| 4 | android:layout_width="match_parent" | ||
| 5 | android:layout_height="wrap_content" | ||
| 6 | android:padding="24dp" | ||
| 7 | android:orientation="vertical"> | ||
| 8 | |||
| 9 | <com.google.android.material.textview.MaterialTextView | ||
| 10 | android:id="@+id/path" | ||
| 11 | style="@style/TextAppearance.Material3.BodyLarge" | ||
| 12 | android:layout_width="match_parent" | ||
| 13 | android:layout_height="0dp" | ||
| 14 | android:layout_gravity="center_vertical|start" | ||
| 15 | android:layout_weight="1" | ||
| 16 | android:ellipsize="marquee" | ||
| 17 | android:marqueeRepeatLimit="marquee_forever" | ||
| 18 | android:requiresFadingEdge="horizontal" | ||
| 19 | android:singleLine="true" | ||
| 20 | android:textAlignment="viewStart" | ||
| 21 | tools:text="folder/folder/folder/folder" /> | ||
| 22 | |||
| 23 | <LinearLayout | ||
| 24 | android:layout_width="match_parent" | ||
| 25 | android:layout_height="wrap_content" | ||
| 26 | android:orientation="horizontal" | ||
| 27 | android:paddingTop="8dp"> | ||
| 28 | |||
| 29 | <com.google.android.material.textview.MaterialTextView | ||
| 30 | style="@style/TextAppearance.Material3.BodyMedium" | ||
| 31 | android:layout_width="0dp" | ||
| 32 | android:layout_height="wrap_content" | ||
| 33 | android:layout_gravity="center_vertical|start" | ||
| 34 | android:layout_weight="1" | ||
| 35 | android:text="@string/deep_scan" | ||
| 36 | android:textAlignment="viewStart" /> | ||
| 37 | |||
| 38 | <com.google.android.material.checkbox.MaterialCheckBox | ||
| 39 | android:id="@+id/deep_scan_switch" | ||
| 40 | android:layout_width="wrap_content" | ||
| 41 | android:layout_height="wrap_content" /> | ||
| 42 | |||
| 43 | </LinearLayout> | ||
| 44 | |||
| 45 | </LinearLayout> | ||
diff --git a/src/android/app/src/main/res/layout/dialog_folder_properties.xml b/src/android/app/src/main/res/layout/dialog_folder_properties.xml new file mode 100644 index 000000000..248d048cb --- /dev/null +++ b/src/android/app/src/main/res/layout/dialog_folder_properties.xml | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | android:layout_width="match_parent" | ||
| 4 | android:layout_height="wrap_content" | ||
| 5 | android:padding="24dp" | ||
| 6 | android:orientation="vertical"> | ||
| 7 | |||
| 8 | <LinearLayout | ||
| 9 | android:id="@+id/deep_scan_layout" | ||
| 10 | android:layout_width="match_parent" | ||
| 11 | android:layout_height="wrap_content" | ||
| 12 | android:orientation="horizontal"> | ||
| 13 | |||
| 14 | <com.google.android.material.textview.MaterialTextView | ||
| 15 | style="@style/TextAppearance.Material3.BodyMedium" | ||
| 16 | android:layout_width="0dp" | ||
| 17 | android:layout_height="wrap_content" | ||
| 18 | android:layout_gravity="center_vertical|start" | ||
| 19 | android:layout_weight="1" | ||
| 20 | android:text="@string/deep_scan" | ||
| 21 | android:textAlignment="viewStart" /> | ||
| 22 | |||
| 23 | <com.google.android.material.checkbox.MaterialCheckBox | ||
| 24 | android:id="@+id/deep_scan_switch" | ||
| 25 | android:layout_width="wrap_content" | ||
| 26 | android:layout_height="wrap_content" /> | ||
| 27 | |||
| 28 | </LinearLayout> | ||
| 29 | |||
| 30 | </LinearLayout> | ||
diff --git a/src/android/app/src/main/res/layout/fragment_folders.xml b/src/android/app/src/main/res/layout/fragment_folders.xml new file mode 100644 index 000000000..74f2f3754 --- /dev/null +++ b/src/android/app/src/main/res/layout/fragment_folders.xml | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| 4 | android:id="@+id/coordinator_folders" | ||
| 5 | android:layout_width="match_parent" | ||
| 6 | android:layout_height="match_parent" | ||
| 7 | android:background="?attr/colorSurface"> | ||
| 8 | |||
| 9 | <androidx.coordinatorlayout.widget.CoordinatorLayout | ||
| 10 | android:layout_width="match_parent" | ||
| 11 | android:layout_height="match_parent"> | ||
| 12 | |||
| 13 | <com.google.android.material.appbar.AppBarLayout | ||
| 14 | android:id="@+id/appbar_folders" | ||
| 15 | android:layout_width="match_parent" | ||
| 16 | android:layout_height="wrap_content" | ||
| 17 | android:fitsSystemWindows="true" | ||
| 18 | app:liftOnScrollTargetViewId="@id/list_folders"> | ||
| 19 | |||
| 20 | <com.google.android.material.appbar.MaterialToolbar | ||
| 21 | android:id="@+id/toolbar_folders" | ||
| 22 | android:layout_width="match_parent" | ||
| 23 | android:layout_height="?attr/actionBarSize" | ||
| 24 | app:navigationIcon="@drawable/ic_back" | ||
| 25 | app:title="@string/game_folders" /> | ||
| 26 | |||
| 27 | </com.google.android.material.appbar.AppBarLayout> | ||
| 28 | |||
| 29 | <androidx.recyclerview.widget.RecyclerView | ||
| 30 | android:id="@+id/list_folders" | ||
| 31 | android:layout_width="match_parent" | ||
| 32 | android:layout_height="wrap_content" | ||
| 33 | android:clipToPadding="false" | ||
| 34 | app:layout_behavior="@string/appbar_scrolling_view_behavior" /> | ||
| 35 | |||
| 36 | </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||
| 37 | |||
| 38 | <com.google.android.material.floatingactionbutton.FloatingActionButton | ||
| 39 | android:id="@+id/button_add" | ||
| 40 | android:layout_width="wrap_content" | ||
| 41 | android:layout_height="wrap_content" | ||
| 42 | android:layout_gravity="bottom|end" | ||
| 43 | android:contentDescription="@string/add_games" | ||
| 44 | app:srcCompat="@drawable/ic_add" | ||
| 45 | app:layout_constraintBottom_toBottomOf="parent" | ||
| 46 | app:layout_constraintEnd_toEndOf="parent" /> | ||
| 47 | |||
| 48 | </androidx.constraintlayout.widget.ConstraintLayout> | ||
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/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml index 6d4c1f86d..cf70b4bc4 100644 --- a/src/android/app/src/main/res/navigation/home_navigation.xml +++ b/src/android/app/src/main/res/navigation/home_navigation.xml | |||
| @@ -28,6 +28,9 @@ | |||
| 28 | <action | 28 | <action |
| 29 | android:id="@+id/action_homeSettingsFragment_to_appletLauncherFragment" | 29 | android:id="@+id/action_homeSettingsFragment_to_appletLauncherFragment" |
| 30 | app:destination="@id/appletLauncherFragment" /> | 30 | app:destination="@id/appletLauncherFragment" /> |
| 31 | <action | ||
| 32 | android:id="@+id/action_homeSettingsFragment_to_gameFoldersFragment" | ||
| 33 | app:destination="@id/gameFoldersFragment" /> | ||
| 31 | </fragment> | 34 | </fragment> |
| 32 | 35 | ||
| 33 | <fragment | 36 | <fragment |
| @@ -117,5 +120,9 @@ | |||
| 117 | android:id="@+id/cabinetLauncherDialogFragment" | 120 | android:id="@+id/cabinetLauncherDialogFragment" |
| 118 | android:name="org.yuzu.yuzu_emu.fragments.CabinetLauncherDialogFragment" | 121 | android:name="org.yuzu.yuzu_emu.fragments.CabinetLauncherDialogFragment" |
| 119 | android:label="CabinetLauncherDialogFragment" /> | 122 | android:label="CabinetLauncherDialogFragment" /> |
| 123 | <fragment | ||
| 124 | android:id="@+id/gameFoldersFragment" | ||
| 125 | android:name="org.yuzu.yuzu_emu.fragments.GameFoldersFragment" | ||
| 126 | android:label="GameFoldersFragment" /> | ||
| 120 | 127 | ||
| 121 | </navigation> | 128 | </navigation> |
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 51bcc49a3..ab435dce9 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml | |||
| @@ -175,6 +175,24 @@ | |||
| 175 | <item>2</item> | 175 | <item>2</item> |
| 176 | </integer-array> | 176 | </integer-array> |
| 177 | 177 | ||
| 178 | <string-array name="cpuBackendArm64Names"> | ||
| 179 | <item>@string/cpu_backend_dynarmic</item> | ||
| 180 | <item>@string/cpu_backend_nce</item> | ||
| 181 | </string-array> | ||
| 182 | |||
| 183 | <integer-array name="cpuBackendArm64Values"> | ||
| 184 | <item>0</item> | ||
| 185 | <item>1</item> | ||
| 186 | </integer-array> | ||
| 187 | |||
| 188 | <string-array name="cpuBackendX86Names"> | ||
| 189 | <item>@string/cpu_backend_dynarmic</item> | ||
| 190 | </string-array> | ||
| 191 | |||
| 192 | <integer-array name="cpuBackendX86Values"> | ||
| 193 | <item>0</item> | ||
| 194 | </integer-array> | ||
| 195 | |||
| 178 | <string-array name="cpuAccuracyNames"> | 196 | <string-array name="cpuAccuracyNames"> |
| 179 | <item>@string/auto</item> | 197 | <item>@string/auto</item> |
| 180 | <item>@string/cpu_accuracy_accurate</item> | 198 | <item>@string/cpu_accuracy_accurate</item> |
diff --git a/src/android/app/src/main/res/values/dimens.xml b/src/android/app/src/main/res/values/dimens.xml index ef855ea6f..380d14213 100644 --- a/src/android/app/src/main/res/values/dimens.xml +++ b/src/android/app/src/main/res/values/dimens.xml | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | <dimen name="menu_width">256dp</dimen> | 13 | <dimen name="menu_width">256dp</dimen> |
| 14 | <dimen name="card_width">165dp</dimen> | 14 | <dimen name="card_width">165dp</dimen> |
| 15 | <dimen name="icon_inset">24dp</dimen> | 15 | <dimen name="icon_inset">24dp</dimen> |
| 16 | <dimen name="spacing_bottom_list_fab">72dp</dimen> | 16 | <dimen name="spacing_bottom_list_fab">76dp</dimen> |
| 17 | <dimen name="spacing_fab">24dp</dimen> | 17 | <dimen name="spacing_fab">24dp</dimen> |
| 18 | 18 | ||
| 19 | <dimen name="dialog_margin">20dp</dimen> | 19 | <dimen name="dialog_margin">20dp</dimen> |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 98c3f20f8..a6ccef8a1 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -38,6 +38,7 @@ | |||
| 38 | <string name="empty_gamelist">No files were found or no game directory has been selected yet.</string> | 38 | <string name="empty_gamelist">No files were found or no game directory has been selected yet.</string> |
| 39 | <string name="search_and_filter_games">Search and filter games</string> | 39 | <string name="search_and_filter_games">Search and filter games</string> |
| 40 | <string name="select_games_folder">Select games folder</string> | 40 | <string name="select_games_folder">Select games folder</string> |
| 41 | <string name="manage_game_folders">Manage game folders</string> | ||
| 41 | <string name="select_games_folder_description">Allows yuzu to populate the games list</string> | 42 | <string name="select_games_folder_description">Allows yuzu to populate the games list</string> |
| 42 | <string name="add_games_warning">Skip selecting games folder?</string> | 43 | <string name="add_games_warning">Skip selecting games folder?</string> |
| 43 | <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string> | 44 | <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string> |
| @@ -91,6 +92,7 @@ | |||
| 91 | <string name="manage_save_data">Manage save data</string> | 92 | <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> | 93 | <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> | 94 | <string name="import_export_saves_description">Import or export save files</string> |
| 95 | <string name="save_files_exporting">Exporting save files…</string> | ||
| 94 | <string name="save_file_imported_success">Imported successfully</string> | 96 | <string name="save_file_imported_success">Imported successfully</string> |
| 95 | <string name="save_file_invalid_zip_structure">Invalid save directory structure</string> | 97 | <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> | 98 | <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string> |
| @@ -123,6 +125,11 @@ | |||
| 123 | <string name="manage_yuzu_data_description">Import/export firmware, keys, user data, and more!</string> | 125 | <string name="manage_yuzu_data_description">Import/export firmware, keys, user data, and more!</string> |
| 124 | <string name="share_save_file">Share save file</string> | 126 | <string name="share_save_file">Share save file</string> |
| 125 | <string name="export_save_failed">Failed to export save</string> | 127 | <string name="export_save_failed">Failed to export save</string> |
| 128 | <string name="game_folders">Game folders</string> | ||
| 129 | <string name="deep_scan">Deep scan</string> | ||
| 130 | <string name="add_game_folder">Add game folder</string> | ||
| 131 | <string name="folder_already_added">This folder was already added!</string> | ||
| 132 | <string name="game_folder_properties">Game folder properties</string> | ||
| 126 | 133 | ||
| 127 | <!-- Applet launcher strings --> | 134 | <!-- Applet launcher strings --> |
| 128 | <string name="applets">Applet launcher</string> | 135 | <string name="applets">Applet launcher</string> |
| @@ -184,6 +191,7 @@ | |||
| 184 | <string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string> | 191 | <string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string> |
| 185 | <string name="frame_limit_slider">Limit speed percent</string> | 192 | <string name="frame_limit_slider">Limit speed percent</string> |
| 186 | <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string> | 193 | <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string> |
| 194 | <string name="cpu_backend">CPU backend</string> | ||
| 187 | <string name="cpu_accuracy">CPU accuracy</string> | 195 | <string name="cpu_accuracy">CPU accuracy</string> |
| 188 | <string name="value_with_units">%1$s%2$s</string> | 196 | <string name="value_with_units">%1$s%2$s</string> |
| 189 | 197 | ||
| @@ -256,6 +264,8 @@ | |||
| 256 | <string name="cancelling">Cancelling</string> | 264 | <string name="cancelling">Cancelling</string> |
| 257 | <string name="install">Install</string> | 265 | <string name="install">Install</string> |
| 258 | <string name="delete">Delete</string> | 266 | <string name="delete">Delete</string> |
| 267 | <string name="edit">Edit</string> | ||
| 268 | <string name="export_success">Exported successfully</string> | ||
| 259 | 269 | ||
| 260 | <!-- GPU driver installation --> | 270 | <!-- GPU driver installation --> |
| 261 | <string name="select_gpu_driver">Select GPU driver</string> | 271 | <string name="select_gpu_driver">Select GPU driver</string> |
| @@ -414,6 +424,10 @@ | |||
| 414 | <string name="ratio_force_sixteen_ten">Force 16:10</string> | 424 | <string name="ratio_force_sixteen_ten">Force 16:10</string> |
| 415 | <string name="ratio_stretch">Stretch to window</string> | 425 | <string name="ratio_stretch">Stretch to window</string> |
| 416 | 426 | ||
| 427 | <!-- CPU Backend --> | ||
| 428 | <string name="cpu_backend_dynarmic">Dynarmic (Slow)</string> | ||
| 429 | <string name="cpu_backend_nce">Native code execution (NCE)</string> | ||
| 430 | |||
| 417 | <!-- CPU Accuracy --> | 431 | <!-- CPU Accuracy --> |
| 418 | <string name="cpu_accuracy_accurate">Accurate</string> | 432 | <string name="cpu_accuracy_accurate">Accurate</string> |
| 419 | <string name="cpu_accuracy_unsafe">Unsafe</string> | 433 | <string name="cpu_accuracy_unsafe">Unsafe</string> |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index e216eb3de..b58a7073f 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -52,6 +52,7 @@ add_library(common STATIC | |||
| 52 | fiber.cpp | 52 | fiber.cpp |
| 53 | fiber.h | 53 | fiber.h |
| 54 | fixed_point.h | 54 | fixed_point.h |
| 55 | free_region_manager.h | ||
| 55 | fs/file.cpp | 56 | fs/file.cpp |
| 56 | fs/file.h | 57 | fs/file.h |
| 57 | fs/fs.cpp | 58 | fs/fs.cpp |
| @@ -166,6 +167,13 @@ if (WIN32) | |||
| 166 | target_link_libraries(common PRIVATE ntdll) | 167 | target_link_libraries(common PRIVATE ntdll) |
| 167 | endif() | 168 | endif() |
| 168 | 169 | ||
| 170 | if (NOT WIN32) | ||
| 171 | target_sources(common PRIVATE | ||
| 172 | signal_chain.cpp | ||
| 173 | signal_chain.h | ||
| 174 | ) | ||
| 175 | endif() | ||
| 176 | |||
| 169 | if(ANDROID) | 177 | if(ANDROID) |
| 170 | target_sources(common | 178 | target_sources(common |
| 171 | PRIVATE | 179 | PRIVATE |
| @@ -174,6 +182,15 @@ if(ANDROID) | |||
| 174 | ) | 182 | ) |
| 175 | endif() | 183 | endif() |
| 176 | 184 | ||
| 185 | if (UNIX AND NOT APPLE) | ||
| 186 | target_sources(common PRIVATE | ||
| 187 | linux/gamemode.cpp | ||
| 188 | linux/gamemode.h | ||
| 189 | ) | ||
| 190 | |||
| 191 | target_link_libraries(common PRIVATE gamemode::headers) | ||
| 192 | endif() | ||
| 193 | |||
| 177 | if(ARCHITECTURE_x86_64) | 194 | if(ARCHITECTURE_x86_64) |
| 178 | target_sources(common | 195 | target_sources(common |
| 179 | PRIVATE | 196 | PRIVATE |
| @@ -191,7 +208,7 @@ if(ARCHITECTURE_x86_64) | |||
| 191 | target_link_libraries(common PRIVATE xbyak::xbyak) | 208 | target_link_libraries(common PRIVATE xbyak::xbyak) |
| 192 | endif() | 209 | endif() |
| 193 | 210 | ||
| 194 | if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX)) | 211 | if (HAS_NCE) |
| 195 | target_sources(common | 212 | target_sources(common |
| 196 | PRIVATE | 213 | PRIVATE |
| 197 | arm64/native_clock.cpp | 214 | arm64/native_clock.cpp |
diff --git a/src/common/free_region_manager.h b/src/common/free_region_manager.h new file mode 100644 index 000000000..2e590d609 --- /dev/null +++ b/src/common/free_region_manager.h | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <mutex> | ||
| 7 | #include <boost/icl/interval_set.hpp> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | class FreeRegionManager { | ||
| 12 | public: | ||
| 13 | explicit FreeRegionManager() = default; | ||
| 14 | ~FreeRegionManager() = default; | ||
| 15 | |||
| 16 | void SetAddressSpace(void* start, size_t size) { | ||
| 17 | this->FreeBlock(start, size); | ||
| 18 | } | ||
| 19 | |||
| 20 | std::pair<void*, size_t> FreeBlock(void* block_ptr, size_t size) { | ||
| 21 | std::scoped_lock lk(m_mutex); | ||
| 22 | |||
| 23 | // Check to see if we are adjacent to any regions. | ||
| 24 | auto start_address = reinterpret_cast<uintptr_t>(block_ptr); | ||
| 25 | auto end_address = start_address + size; | ||
| 26 | auto it = m_free_regions.find({start_address - 1, end_address + 1}); | ||
| 27 | |||
| 28 | // If we are, join with them, ensuring we stay in bounds. | ||
| 29 | if (it != m_free_regions.end()) { | ||
| 30 | start_address = std::min(start_address, it->lower()); | ||
| 31 | end_address = std::max(end_address, it->upper()); | ||
| 32 | } | ||
| 33 | |||
| 34 | // Free the relevant region. | ||
| 35 | m_free_regions.insert({start_address, end_address}); | ||
| 36 | |||
| 37 | // Return the adjusted pointers. | ||
| 38 | block_ptr = reinterpret_cast<void*>(start_address); | ||
| 39 | size = end_address - start_address; | ||
| 40 | return {block_ptr, size}; | ||
| 41 | } | ||
| 42 | |||
| 43 | void AllocateBlock(void* block_ptr, size_t size) { | ||
| 44 | std::scoped_lock lk(m_mutex); | ||
| 45 | |||
| 46 | auto address = reinterpret_cast<uintptr_t>(block_ptr); | ||
| 47 | m_free_regions.subtract({address, address + size}); | ||
| 48 | } | ||
| 49 | |||
| 50 | private: | ||
| 51 | std::mutex m_mutex; | ||
| 52 | boost::icl::interval_set<uintptr_t> m_free_regions; | ||
| 53 | }; | ||
| 54 | |||
| 55 | } // namespace Common | ||
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index ba22595e0..4bfc64f2d 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp | |||
| @@ -21,15 +21,22 @@ | |||
| 21 | #include <boost/icl/interval_set.hpp> | 21 | #include <boost/icl/interval_set.hpp> |
| 22 | #include <fcntl.h> | 22 | #include <fcntl.h> |
| 23 | #include <sys/mman.h> | 23 | #include <sys/mman.h> |
| 24 | #include <sys/random.h> | ||
| 24 | #include <unistd.h> | 25 | #include <unistd.h> |
| 25 | #include "common/scope_exit.h" | 26 | #include "common/scope_exit.h" |
| 26 | 27 | ||
| 28 | #ifndef MAP_NORESERVE | ||
| 29 | #define MAP_NORESERVE 0 | ||
| 30 | #endif | ||
| 31 | |||
| 27 | #endif // ^^^ Linux ^^^ | 32 | #endif // ^^^ Linux ^^^ |
| 28 | 33 | ||
| 29 | #include <mutex> | 34 | #include <mutex> |
| 35 | #include <random> | ||
| 30 | 36 | ||
| 31 | #include "common/alignment.h" | 37 | #include "common/alignment.h" |
| 32 | #include "common/assert.h" | 38 | #include "common/assert.h" |
| 39 | #include "common/free_region_manager.h" | ||
| 33 | #include "common/host_memory.h" | 40 | #include "common/host_memory.h" |
| 34 | #include "common/logging/log.h" | 41 | #include "common/logging/log.h" |
| 35 | 42 | ||
| @@ -141,7 +148,7 @@ public: | |||
| 141 | Release(); | 148 | Release(); |
| 142 | } | 149 | } |
| 143 | 150 | ||
| 144 | void Map(size_t virtual_offset, size_t host_offset, size_t length) { | 151 | void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) { |
| 145 | std::unique_lock lock{placeholder_mutex}; | 152 | std::unique_lock lock{placeholder_mutex}; |
| 146 | if (!IsNiechePlaceholder(virtual_offset, length)) { | 153 | if (!IsNiechePlaceholder(virtual_offset, length)) { |
| 147 | Split(virtual_offset, length); | 154 | Split(virtual_offset, length); |
| @@ -160,7 +167,7 @@ public: | |||
| 160 | } | 167 | } |
| 161 | } | 168 | } |
| 162 | 169 | ||
| 163 | void Protect(size_t virtual_offset, size_t length, bool read, bool write) { | 170 | void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) { |
| 164 | DWORD new_flags{}; | 171 | DWORD new_flags{}; |
| 165 | if (read && write) { | 172 | if (read && write) { |
| 166 | new_flags = PAGE_READWRITE; | 173 | new_flags = PAGE_READWRITE; |
| @@ -186,6 +193,11 @@ public: | |||
| 186 | } | 193 | } |
| 187 | } | 194 | } |
| 188 | 195 | ||
| 196 | void EnableDirectMappedAddress() { | ||
| 197 | // TODO | ||
| 198 | UNREACHABLE(); | ||
| 199 | } | ||
| 200 | |||
| 189 | const size_t backing_size; ///< Size of the backing memory in bytes | 201 | const size_t backing_size; ///< Size of the backing memory in bytes |
| 190 | const size_t virtual_size; ///< Size of the virtual address placeholder in bytes | 202 | const size_t virtual_size; ///< Size of the virtual address placeholder in bytes |
| 191 | 203 | ||
| @@ -353,6 +365,65 @@ private: | |||
| 353 | 365 | ||
| 354 | #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv | 366 | #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv |
| 355 | 367 | ||
| 368 | #ifdef ARCHITECTURE_arm64 | ||
| 369 | |||
| 370 | static void* ChooseVirtualBase(size_t virtual_size) { | ||
| 371 | constexpr uintptr_t Map39BitSize = (1ULL << 39); | ||
| 372 | constexpr uintptr_t Map36BitSize = (1ULL << 36); | ||
| 373 | |||
| 374 | // This is not a cryptographic application, we just want something random. | ||
| 375 | std::mt19937_64 rng; | ||
| 376 | |||
| 377 | // We want to ensure we are allocating at an address aligned to the L2 block size. | ||
| 378 | // For Qualcomm devices, we must also allocate memory above 36 bits. | ||
| 379 | const size_t lower = Map36BitSize / HugePageSize; | ||
| 380 | const size_t upper = (Map39BitSize - virtual_size) / HugePageSize; | ||
| 381 | const size_t range = upper - lower; | ||
| 382 | |||
| 383 | // Try up to 64 times to allocate memory at random addresses in the range. | ||
| 384 | for (int i = 0; i < 64; i++) { | ||
| 385 | // Calculate a possible location. | ||
| 386 | uintptr_t hint_address = ((rng() % range) + lower) * HugePageSize; | ||
| 387 | |||
| 388 | // Try to map. | ||
| 389 | // Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here. | ||
| 390 | void* map_pointer = | ||
| 391 | mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE, | ||
| 392 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); | ||
| 393 | |||
| 394 | // If we successfully mapped, we're done. | ||
| 395 | if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) { | ||
| 396 | return map_pointer; | ||
| 397 | } | ||
| 398 | |||
| 399 | // Unmap if necessary, and try again. | ||
| 400 | if (map_pointer != MAP_FAILED) { | ||
| 401 | munmap(map_pointer, virtual_size); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | return MAP_FAILED; | ||
| 406 | } | ||
| 407 | |||
| 408 | #else | ||
| 409 | |||
| 410 | static void* ChooseVirtualBase(size_t virtual_size) { | ||
| 411 | #if defined(__FreeBSD__) | ||
| 412 | void* virtual_base = | ||
| 413 | mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, | ||
| 414 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0); | ||
| 415 | |||
| 416 | if (virtual_base != MAP_FAILED) { | ||
| 417 | return virtual_base; | ||
| 418 | } | ||
| 419 | #endif | ||
| 420 | |||
| 421 | return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, | ||
| 422 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); | ||
| 423 | } | ||
| 424 | |||
| 425 | #endif | ||
| 426 | |||
| 356 | class HostMemory::Impl { | 427 | class HostMemory::Impl { |
| 357 | public: | 428 | public: |
| 358 | explicit Impl(size_t backing_size_, size_t virtual_size_) | 429 | explicit Impl(size_t backing_size_, size_t virtual_size_) |
| @@ -402,29 +473,16 @@ public: | |||
| 402 | } | 473 | } |
| 403 | 474 | ||
| 404 | // Virtual memory initialization | 475 | // Virtual memory initialization |
| 405 | #if defined(__FreeBSD__) | 476 | virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size)); |
| 406 | virtual_base = | ||
| 407 | static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE, | ||
| 408 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER, -1, 0)); | ||
| 409 | if (virtual_base == MAP_FAILED) { | ||
| 410 | virtual_base = static_cast<u8*>( | ||
| 411 | mmap(nullptr, virtual_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); | ||
| 412 | if (virtual_base == MAP_FAILED) { | ||
| 413 | LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); | ||
| 414 | throw std::bad_alloc{}; | ||
| 415 | } | ||
| 416 | } | ||
| 417 | #else | ||
| 418 | virtual_base = static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE, | ||
| 419 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0)); | ||
| 420 | if (virtual_base == MAP_FAILED) { | 477 | if (virtual_base == MAP_FAILED) { |
| 421 | LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); | 478 | LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); |
| 422 | throw std::bad_alloc{}; | 479 | throw std::bad_alloc{}; |
| 423 | } | 480 | } |
| 481 | #if defined(__linux__) | ||
| 424 | madvise(virtual_base, virtual_size, MADV_HUGEPAGE); | 482 | madvise(virtual_base, virtual_size, MADV_HUGEPAGE); |
| 425 | #endif | 483 | #endif |
| 426 | 484 | ||
| 427 | placeholders.add({0, virtual_size}); | 485 | free_manager.SetAddressSpace(virtual_base, virtual_size); |
| 428 | good = true; | 486 | good = true; |
| 429 | } | 487 | } |
| 430 | 488 | ||
| @@ -432,14 +490,29 @@ public: | |||
| 432 | Release(); | 490 | Release(); |
| 433 | } | 491 | } |
| 434 | 492 | ||
| 435 | void Map(size_t virtual_offset, size_t host_offset, size_t length) { | 493 | void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) { |
| 436 | { | 494 | // Intersect the range with our address space. |
| 437 | std::scoped_lock lock{placeholder_mutex}; | 495 | AdjustMap(&virtual_offset, &length); |
| 438 | placeholders.subtract({virtual_offset, virtual_offset + length}); | 496 | |
| 497 | // We are removing a placeholder. | ||
| 498 | free_manager.AllocateBlock(virtual_base + virtual_offset, length); | ||
| 499 | |||
| 500 | // Deduce mapping protection flags. | ||
| 501 | int flags = PROT_NONE; | ||
| 502 | if (True(perms & MemoryPermission::Read)) { | ||
| 503 | flags |= PROT_READ; | ||
| 504 | } | ||
| 505 | if (True(perms & MemoryPermission::Write)) { | ||
| 506 | flags |= PROT_WRITE; | ||
| 507 | } | ||
| 508 | #ifdef ARCHITECTURE_arm64 | ||
| 509 | if (True(perms & MemoryPermission::Execute)) { | ||
| 510 | flags |= PROT_EXEC; | ||
| 439 | } | 511 | } |
| 512 | #endif | ||
| 440 | 513 | ||
| 441 | void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE, | 514 | void* ret = mmap(virtual_base + virtual_offset, length, flags, MAP_SHARED | MAP_FIXED, fd, |
| 442 | MAP_SHARED | MAP_FIXED, fd, host_offset); | 515 | host_offset); |
| 443 | ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); | 516 | ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); |
| 444 | } | 517 | } |
| 445 | 518 | ||
| @@ -447,47 +520,54 @@ public: | |||
| 447 | // The method name is wrong. We're still talking about the virtual range. | 520 | // The method name is wrong. We're still talking about the virtual range. |
| 448 | // We don't want to unmap, we want to reserve this memory. | 521 | // We don't want to unmap, we want to reserve this memory. |
| 449 | 522 | ||
| 450 | { | 523 | // Intersect the range with our address space. |
| 451 | std::scoped_lock lock{placeholder_mutex}; | 524 | AdjustMap(&virtual_offset, &length); |
| 452 | auto it = placeholders.find({virtual_offset - 1, virtual_offset + length + 1}); | ||
| 453 | |||
| 454 | if (it != placeholders.end()) { | ||
| 455 | size_t prev_upper = virtual_offset + length; | ||
| 456 | virtual_offset = std::min(virtual_offset, it->lower()); | ||
| 457 | length = std::max(it->upper(), prev_upper) - virtual_offset; | ||
| 458 | } | ||
| 459 | 525 | ||
| 460 | placeholders.add({virtual_offset, virtual_offset + length}); | 526 | // Merge with any adjacent placeholder mappings. |
| 461 | } | 527 | auto [merged_pointer, merged_size] = |
| 528 | free_manager.FreeBlock(virtual_base + virtual_offset, length); | ||
| 462 | 529 | ||
| 463 | void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE, | 530 | void* ret = mmap(merged_pointer, merged_size, PROT_NONE, |
| 464 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); | 531 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); |
| 465 | ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); | 532 | ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); |
| 466 | } | 533 | } |
| 467 | 534 | ||
| 468 | void Protect(size_t virtual_offset, size_t length, bool read, bool write) { | 535 | void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) { |
| 469 | int flags = 0; | 536 | // Intersect the range with our address space. |
| 537 | AdjustMap(&virtual_offset, &length); | ||
| 538 | |||
| 539 | int flags = PROT_NONE; | ||
| 470 | if (read) { | 540 | if (read) { |
| 471 | flags |= PROT_READ; | 541 | flags |= PROT_READ; |
| 472 | } | 542 | } |
| 473 | if (write) { | 543 | if (write) { |
| 474 | flags |= PROT_WRITE; | 544 | flags |= PROT_WRITE; |
| 475 | } | 545 | } |
| 546 | #ifdef HAS_NCE | ||
| 547 | if (execute) { | ||
| 548 | flags |= PROT_EXEC; | ||
| 549 | } | ||
| 550 | #endif | ||
| 476 | int ret = mprotect(virtual_base + virtual_offset, length, flags); | 551 | int ret = mprotect(virtual_base + virtual_offset, length, flags); |
| 477 | ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno)); | 552 | ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno)); |
| 478 | } | 553 | } |
| 479 | 554 | ||
| 555 | void EnableDirectMappedAddress() { | ||
| 556 | virtual_base = nullptr; | ||
| 557 | } | ||
| 558 | |||
| 480 | const size_t backing_size; ///< Size of the backing memory in bytes | 559 | const size_t backing_size; ///< Size of the backing memory in bytes |
| 481 | const size_t virtual_size; ///< Size of the virtual address placeholder in bytes | 560 | const size_t virtual_size; ///< Size of the virtual address placeholder in bytes |
| 482 | 561 | ||
| 483 | u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)}; | 562 | u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)}; |
| 484 | u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)}; | 563 | u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)}; |
| 564 | u8* virtual_map_base{reinterpret_cast<u8*>(MAP_FAILED)}; | ||
| 485 | 565 | ||
| 486 | private: | 566 | private: |
| 487 | /// Release all resources in the object | 567 | /// Release all resources in the object |
| 488 | void Release() { | 568 | void Release() { |
| 489 | if (virtual_base != MAP_FAILED) { | 569 | if (virtual_map_base != MAP_FAILED) { |
| 490 | int ret = munmap(virtual_base, virtual_size); | 570 | int ret = munmap(virtual_map_base, virtual_size); |
| 491 | ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno)); | 571 | ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno)); |
| 492 | } | 572 | } |
| 493 | 573 | ||
| @@ -502,10 +582,29 @@ private: | |||
| 502 | } | 582 | } |
| 503 | } | 583 | } |
| 504 | 584 | ||
| 505 | int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create | 585 | void AdjustMap(size_t* virtual_offset, size_t* length) { |
| 586 | if (virtual_base != nullptr) { | ||
| 587 | return; | ||
| 588 | } | ||
| 589 | |||
| 590 | // If we are direct mapped, we want to make sure we are operating on a region | ||
| 591 | // that is in range of our virtual mapping. | ||
| 592 | size_t intended_start = *virtual_offset; | ||
| 593 | size_t intended_end = intended_start + *length; | ||
| 594 | size_t address_space_start = reinterpret_cast<size_t>(virtual_map_base); | ||
| 595 | size_t address_space_end = address_space_start + virtual_size; | ||
| 506 | 596 | ||
| 507 | boost::icl::interval_set<size_t> placeholders; ///< Mapped placeholders | 597 | if (address_space_start > intended_end || intended_start > address_space_end) { |
| 508 | std::mutex placeholder_mutex; ///< Mutex for placeholders | 598 | *virtual_offset = 0; |
| 599 | *length = 0; | ||
| 600 | } else { | ||
| 601 | *virtual_offset = std::max(intended_start, address_space_start); | ||
| 602 | *length = std::min(intended_end, address_space_end) - *virtual_offset; | ||
| 603 | } | ||
| 604 | } | ||
| 605 | |||
| 606 | int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create | ||
| 607 | FreeRegionManager free_manager{}; | ||
| 509 | }; | 608 | }; |
| 510 | 609 | ||
| 511 | #else // ^^^ Linux ^^^ vvv Generic vvv | 610 | #else // ^^^ Linux ^^^ vvv Generic vvv |
| @@ -518,11 +617,13 @@ public: | |||
| 518 | throw std::bad_alloc{}; | 617 | throw std::bad_alloc{}; |
| 519 | } | 618 | } |
| 520 | 619 | ||
| 521 | void Map(size_t virtual_offset, size_t host_offset, size_t length) {} | 620 | void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm) {} |
| 522 | 621 | ||
| 523 | void Unmap(size_t virtual_offset, size_t length) {} | 622 | void Unmap(size_t virtual_offset, size_t length) {} |
| 524 | 623 | ||
| 525 | void Protect(size_t virtual_offset, size_t length, bool read, bool write) {} | 624 | void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {} |
| 625 | |||
| 626 | void EnableDirectMappedAddress() {} | ||
| 526 | 627 | ||
| 527 | u8* backing_base{nullptr}; | 628 | u8* backing_base{nullptr}; |
| 528 | u8* virtual_base{nullptr}; | 629 | u8* virtual_base{nullptr}; |
| @@ -535,15 +636,16 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) | |||
| 535 | try { | 636 | try { |
| 536 | // Try to allocate a fastmem arena. | 637 | // Try to allocate a fastmem arena. |
| 537 | // The implementation will fail with std::bad_alloc on errors. | 638 | // The implementation will fail with std::bad_alloc on errors. |
| 538 | impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), | 639 | impl = |
| 539 | AlignUp(virtual_size, PageAlignment) + | 640 | std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), |
| 540 | 3 * HugePageSize); | 641 | AlignUp(virtual_size, PageAlignment) + HugePageSize); |
| 541 | backing_base = impl->backing_base; | 642 | backing_base = impl->backing_base; |
| 542 | virtual_base = impl->virtual_base; | 643 | virtual_base = impl->virtual_base; |
| 543 | 644 | ||
| 544 | if (virtual_base) { | 645 | if (virtual_base) { |
| 545 | virtual_base += 2 * HugePageSize - 1; | 646 | // Ensure the virtual base is aligned to the L2 block size. |
| 546 | virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1); | 647 | virtual_base = reinterpret_cast<u8*>( |
| 648 | Common::AlignUp(reinterpret_cast<uintptr_t>(virtual_base), HugePageSize)); | ||
| 547 | virtual_base_offset = virtual_base - impl->virtual_base; | 649 | virtual_base_offset = virtual_base - impl->virtual_base; |
| 548 | } | 650 | } |
| 549 | 651 | ||
| @@ -562,7 +664,8 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default; | |||
| 562 | 664 | ||
| 563 | HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; | 665 | HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; |
| 564 | 666 | ||
| 565 | void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) { | 667 | void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, |
| 668 | MemoryPermission perms) { | ||
| 566 | ASSERT(virtual_offset % PageAlignment == 0); | 669 | ASSERT(virtual_offset % PageAlignment == 0); |
| 567 | ASSERT(host_offset % PageAlignment == 0); | 670 | ASSERT(host_offset % PageAlignment == 0); |
| 568 | ASSERT(length % PageAlignment == 0); | 671 | ASSERT(length % PageAlignment == 0); |
| @@ -571,7 +674,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) { | |||
| 571 | if (length == 0 || !virtual_base || !impl) { | 674 | if (length == 0 || !virtual_base || !impl) { |
| 572 | return; | 675 | return; |
| 573 | } | 676 | } |
| 574 | impl->Map(virtual_offset + virtual_base_offset, host_offset, length); | 677 | impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms); |
| 575 | } | 678 | } |
| 576 | 679 | ||
| 577 | void HostMemory::Unmap(size_t virtual_offset, size_t length) { | 680 | void HostMemory::Unmap(size_t virtual_offset, size_t length) { |
| @@ -584,14 +687,22 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) { | |||
| 584 | impl->Unmap(virtual_offset + virtual_base_offset, length); | 687 | impl->Unmap(virtual_offset + virtual_base_offset, length); |
| 585 | } | 688 | } |
| 586 | 689 | ||
| 587 | void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write) { | 690 | void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write, |
| 691 | bool execute) { | ||
| 588 | ASSERT(virtual_offset % PageAlignment == 0); | 692 | ASSERT(virtual_offset % PageAlignment == 0); |
| 589 | ASSERT(length % PageAlignment == 0); | 693 | ASSERT(length % PageAlignment == 0); |
| 590 | ASSERT(virtual_offset + length <= virtual_size); | 694 | ASSERT(virtual_offset + length <= virtual_size); |
| 591 | if (length == 0 || !virtual_base || !impl) { | 695 | if (length == 0 || !virtual_base || !impl) { |
| 592 | return; | 696 | return; |
| 593 | } | 697 | } |
| 594 | impl->Protect(virtual_offset + virtual_base_offset, length, read, write); | 698 | impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); |
| 699 | } | ||
| 700 | |||
| 701 | void HostMemory::EnableDirectMappedAddress() { | ||
| 702 | if (impl) { | ||
| 703 | impl->EnableDirectMappedAddress(); | ||
| 704 | virtual_size += reinterpret_cast<uintptr_t>(virtual_base); | ||
| 705 | } | ||
| 595 | } | 706 | } |
| 596 | 707 | ||
| 597 | } // namespace Common | 708 | } // namespace Common |
diff --git a/src/common/host_memory.h b/src/common/host_memory.h index 447975ded..cebfacab2 100644 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h | |||
| @@ -4,11 +4,20 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | #include "common/virtual_buffer.h" | 9 | #include "common/virtual_buffer.h" |
| 9 | 10 | ||
| 10 | namespace Common { | 11 | namespace Common { |
| 11 | 12 | ||
| 13 | enum class MemoryPermission : u32 { | ||
| 14 | Read = 1 << 0, | ||
| 15 | Write = 1 << 1, | ||
| 16 | ReadWrite = Read | Write, | ||
| 17 | Execute = 1 << 2, | ||
| 18 | }; | ||
| 19 | DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission) | ||
| 20 | |||
| 12 | /** | 21 | /** |
| 13 | * A low level linear memory buffer, which supports multiple mappings | 22 | * A low level linear memory buffer, which supports multiple mappings |
| 14 | * Its purpose is to rebuild a given sparse memory layout, including mirrors. | 23 | * Its purpose is to rebuild a given sparse memory layout, including mirrors. |
| @@ -31,11 +40,13 @@ public: | |||
| 31 | HostMemory(HostMemory&& other) noexcept; | 40 | HostMemory(HostMemory&& other) noexcept; |
| 32 | HostMemory& operator=(HostMemory&& other) noexcept; | 41 | HostMemory& operator=(HostMemory&& other) noexcept; |
| 33 | 42 | ||
| 34 | void Map(size_t virtual_offset, size_t host_offset, size_t length); | 43 | void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms); |
| 35 | 44 | ||
| 36 | void Unmap(size_t virtual_offset, size_t length); | 45 | void Unmap(size_t virtual_offset, size_t length); |
| 37 | 46 | ||
| 38 | void Protect(size_t virtual_offset, size_t length, bool read, bool write); | 47 | void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false); |
| 48 | |||
| 49 | void EnableDirectMappedAddress(); | ||
| 39 | 50 | ||
| 40 | [[nodiscard]] u8* BackingBasePointer() noexcept { | 51 | [[nodiscard]] u8* BackingBasePointer() noexcept { |
| 41 | return backing_base; | 52 | return backing_base; |
diff --git a/src/common/linux/gamemode.cpp b/src/common/linux/gamemode.cpp new file mode 100644 index 000000000..8d3e2934a --- /dev/null +++ b/src/common/linux/gamemode.cpp | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <gamemode_client.h> | ||
| 5 | |||
| 6 | #include "common/linux/gamemode.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "common/settings.h" | ||
| 9 | |||
| 10 | namespace Common::Linux { | ||
| 11 | |||
| 12 | void StartGamemode() { | ||
| 13 | if (Settings::values.enable_gamemode) { | ||
| 14 | if (gamemode_request_start() < 0) { | ||
| 15 | LOG_WARNING(Frontend, "Failed to start gamemode: {}", gamemode_error_string()); | ||
| 16 | } else { | ||
| 17 | LOG_INFO(Frontend, "Started gamemode"); | ||
| 18 | } | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | void StopGamemode() { | ||
| 23 | if (Settings::values.enable_gamemode) { | ||
| 24 | if (gamemode_request_end() < 0) { | ||
| 25 | LOG_WARNING(Frontend, "Failed to stop gamemode: {}", gamemode_error_string()); | ||
| 26 | } else { | ||
| 27 | LOG_INFO(Frontend, "Stopped gamemode"); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | void SetGamemodeState(bool state) { | ||
| 33 | if (state) { | ||
| 34 | StartGamemode(); | ||
| 35 | } else { | ||
| 36 | StopGamemode(); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | } // namespace Common::Linux | ||
diff --git a/src/common/linux/gamemode.h b/src/common/linux/gamemode.h new file mode 100644 index 000000000..b80646ae2 --- /dev/null +++ b/src/common/linux/gamemode.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | namespace Common::Linux { | ||
| 7 | |||
| 8 | /** | ||
| 9 | * Start the (Feral Interactive) Linux gamemode if it is installed and it is activated | ||
| 10 | */ | ||
| 11 | void StartGamemode(); | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Stop the (Feral Interactive) Linux gamemode if it is installed and it is activated | ||
| 15 | */ | ||
| 16 | void StopGamemode(); | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Start or stop the (Feral Interactive) Linux gamemode if it is installed and it is activated | ||
| 20 | * @param state The new state the gamemode should have | ||
| 21 | */ | ||
| 22 | void SetGamemodeState(bool state); | ||
| 23 | |||
| 24 | } // namespace Common::Linux | ||
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 51717be06..4666bd0a0 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -41,6 +41,7 @@ SWITCHABLE(AspectRatio, true); | |||
| 41 | SWITCHABLE(AstcDecodeMode, true); | 41 | SWITCHABLE(AstcDecodeMode, true); |
| 42 | SWITCHABLE(AstcRecompression, true); | 42 | SWITCHABLE(AstcRecompression, true); |
| 43 | SWITCHABLE(AudioMode, true); | 43 | SWITCHABLE(AudioMode, true); |
| 44 | SWITCHABLE(CpuBackend, true); | ||
| 44 | SWITCHABLE(CpuAccuracy, true); | 45 | SWITCHABLE(CpuAccuracy, true); |
| 45 | SWITCHABLE(FullscreenMode, true); | 46 | SWITCHABLE(FullscreenMode, true); |
| 46 | SWITCHABLE(GpuAccuracy, true); | 47 | SWITCHABLE(GpuAccuracy, true); |
| @@ -155,6 +156,22 @@ bool IsFastmemEnabled() { | |||
| 155 | return true; | 156 | return true; |
| 156 | } | 157 | } |
| 157 | 158 | ||
| 159 | static bool is_nce_enabled = false; | ||
| 160 | |||
| 161 | void SetNceEnabled(bool is_39bit) { | ||
| 162 | const bool is_nce_selected = values.cpu_backend.GetValue() == CpuBackend::Nce; | ||
| 163 | is_nce_enabled = IsFastmemEnabled() && is_nce_selected && is_39bit; | ||
| 164 | if (is_nce_selected && !is_nce_enabled) { | ||
| 165 | LOG_WARNING( | ||
| 166 | Common, | ||
| 167 | "Program does not utilize 39-bit address space, unable to natively execute code"); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | bool IsNceEnabled() { | ||
| 172 | return is_nce_enabled; | ||
| 173 | } | ||
| 174 | |||
| 158 | bool IsDockedMode() { | 175 | bool IsDockedMode() { |
| 159 | return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked; | 176 | return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked; |
| 160 | } | 177 | } |
| @@ -206,9 +223,9 @@ const char* TranslateCategory(Category category) { | |||
| 206 | case Category::UiAudio: | 223 | case Category::UiAudio: |
| 207 | return "UiAudio"; | 224 | return "UiAudio"; |
| 208 | case Category::UiLayout: | 225 | case Category::UiLayout: |
| 209 | return "UiLayout"; | 226 | return "UILayout"; |
| 210 | case Category::UiGameList: | 227 | case Category::UiGameList: |
| 211 | return "UiGameList"; | 228 | return "UIGameList"; |
| 212 | case Category::Screenshots: | 229 | case Category::Screenshots: |
| 213 | return "Screenshots"; | 230 | return "Screenshots"; |
| 214 | case Category::Shortcuts: | 231 | case Category::Shortcuts: |
| @@ -219,6 +236,8 @@ const char* TranslateCategory(Category category) { | |||
| 219 | return "Services"; | 236 | return "Services"; |
| 220 | case Category::Paths: | 237 | case Category::Paths: |
| 221 | return "Paths"; | 238 | return "Paths"; |
| 239 | case Category::Linux: | ||
| 240 | return "Linux"; | ||
| 222 | case Category::MaxEnum: | 241 | case Category::MaxEnum: |
| 223 | break; | 242 | break; |
| 224 | } | 243 | } |
diff --git a/src/common/settings.h b/src/common/settings.h index e899f1ae6..98341ad96 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -63,6 +63,7 @@ SWITCHABLE(AspectRatio, true); | |||
| 63 | SWITCHABLE(AstcDecodeMode, true); | 63 | SWITCHABLE(AstcDecodeMode, true); |
| 64 | SWITCHABLE(AstcRecompression, true); | 64 | SWITCHABLE(AstcRecompression, true); |
| 65 | SWITCHABLE(AudioMode, true); | 65 | SWITCHABLE(AudioMode, true); |
| 66 | SWITCHABLE(CpuBackend, true); | ||
| 66 | SWITCHABLE(CpuAccuracy, true); | 67 | SWITCHABLE(CpuAccuracy, true); |
| 67 | SWITCHABLE(FullscreenMode, true); | 68 | SWITCHABLE(FullscreenMode, true); |
| 68 | SWITCHABLE(GpuAccuracy, true); | 69 | SWITCHABLE(GpuAccuracy, true); |
| @@ -179,6 +180,14 @@ struct Values { | |||
| 179 | &use_speed_limit}; | 180 | &use_speed_limit}; |
| 180 | 181 | ||
| 181 | // Cpu | 182 | // Cpu |
| 183 | SwitchableSetting<CpuBackend, true> cpu_backend{ | ||
| 184 | linkage, CpuBackend::Dynarmic, CpuBackend::Dynarmic, | ||
| 185 | #ifdef HAS_NCE | ||
| 186 | CpuBackend::Nce, | ||
| 187 | #else | ||
| 188 | CpuBackend::Dynarmic, | ||
| 189 | #endif | ||
| 190 | "cpu_backend", Category::Cpu}; | ||
| 182 | SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto, | 191 | SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto, |
| 183 | CpuAccuracy::Auto, CpuAccuracy::Paranoid, | 192 | CpuAccuracy::Auto, CpuAccuracy::Paranoid, |
| 184 | "cpu_accuracy", Category::Cpu}; | 193 | "cpu_accuracy", Category::Cpu}; |
| @@ -232,7 +241,11 @@ struct Values { | |||
| 232 | SwitchableSetting<bool> use_asynchronous_gpu_emulation{ | 241 | SwitchableSetting<bool> use_asynchronous_gpu_emulation{ |
| 233 | linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; | 242 | linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; |
| 234 | SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage, | 243 | SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage, |
| 244 | #ifdef ANDROID | ||
| 245 | AstcDecodeMode::Cpu, | ||
| 246 | #else | ||
| 235 | AstcDecodeMode::Gpu, | 247 | AstcDecodeMode::Gpu, |
| 248 | #endif | ||
| 236 | AstcDecodeMode::Cpu, | 249 | AstcDecodeMode::Cpu, |
| 237 | AstcDecodeMode::CpuAsynchronous, | 250 | AstcDecodeMode::CpuAsynchronous, |
| 238 | "accelerate_astc", | 251 | "accelerate_astc", |
| @@ -304,7 +317,11 @@ struct Values { | |||
| 304 | linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true}; | 317 | linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true}; |
| 305 | 318 | ||
| 306 | SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage, | 319 | SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage, |
| 320 | #ifdef ANDROID | ||
| 321 | GpuAccuracy::Normal, | ||
| 322 | #else | ||
| 307 | GpuAccuracy::High, | 323 | GpuAccuracy::High, |
| 324 | #endif | ||
| 308 | GpuAccuracy::Normal, | 325 | GpuAccuracy::Normal, |
| 309 | GpuAccuracy::Extreme, | 326 | GpuAccuracy::Extreme, |
| 310 | "gpu_accuracy", | 327 | "gpu_accuracy", |
| @@ -313,20 +330,38 @@ struct Values { | |||
| 313 | true, | 330 | true, |
| 314 | true}; | 331 | true}; |
| 315 | GpuAccuracy current_gpu_accuracy{GpuAccuracy::High}; | 332 | GpuAccuracy current_gpu_accuracy{GpuAccuracy::High}; |
| 316 | SwitchableSetting<AnisotropyMode, true> max_anisotropy{ | 333 | SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage, |
| 317 | linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16, | 334 | #ifdef ANDROID |
| 318 | "max_anisotropy", Category::RendererAdvanced}; | 335 | AnisotropyMode::Default, |
| 336 | #else | ||
| 337 | AnisotropyMode::Automatic, | ||
| 338 | #endif | ||
| 339 | AnisotropyMode::Automatic, | ||
| 340 | AnisotropyMode::X16, | ||
| 341 | "max_anisotropy", | ||
| 342 | Category::RendererAdvanced}; | ||
| 319 | SwitchableSetting<AstcRecompression, true> astc_recompression{linkage, | 343 | SwitchableSetting<AstcRecompression, true> astc_recompression{linkage, |
| 320 | AstcRecompression::Uncompressed, | 344 | AstcRecompression::Uncompressed, |
| 321 | AstcRecompression::Uncompressed, | 345 | AstcRecompression::Uncompressed, |
| 322 | AstcRecompression::Bc3, | 346 | AstcRecompression::Bc3, |
| 323 | "astc_recompression", | 347 | "astc_recompression", |
| 324 | Category::RendererAdvanced}; | 348 | Category::RendererAdvanced}; |
| 325 | SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation", | 349 | SwitchableSetting<bool> async_presentation{linkage, |
| 326 | Category::RendererAdvanced}; | 350 | #ifdef ANDROID |
| 351 | true, | ||
| 352 | #else | ||
| 353 | false, | ||
| 354 | #endif | ||
| 355 | "async_presentation", Category::RendererAdvanced}; | ||
| 327 | SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock", | 356 | SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock", |
| 328 | Category::RendererAdvanced}; | 357 | Category::RendererAdvanced}; |
| 329 | SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing", | 358 | SwitchableSetting<bool> use_reactive_flushing{linkage, |
| 359 | #ifdef ANDROID | ||
| 360 | false, | ||
| 361 | #else | ||
| 362 | true, | ||
| 363 | #endif | ||
| 364 | "use_reactive_flushing", | ||
| 330 | Category::RendererAdvanced}; | 365 | Category::RendererAdvanced}; |
| 331 | SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", | 366 | SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", |
| 332 | Category::RendererAdvanced}; | 367 | Category::RendererAdvanced}; |
| @@ -358,6 +393,8 @@ struct Values { | |||
| 358 | Category::RendererDebug}; | 393 | Category::RendererDebug}; |
| 359 | // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control | 394 | // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control |
| 360 | bool renderer_amdvlk_depth_bias_workaround{}; | 395 | bool renderer_amdvlk_depth_bias_workaround{}; |
| 396 | Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder", | ||
| 397 | Category::RendererDebug}; | ||
| 361 | 398 | ||
| 362 | // System | 399 | // System |
| 363 | SwitchableSetting<Language, true> language_index{linkage, | 400 | SwitchableSetting<Language, true> language_index{linkage, |
| @@ -390,13 +427,20 @@ struct Values { | |||
| 390 | Setting<s32> current_user{linkage, 0, "current_user", Category::System}; | 427 | Setting<s32> current_user{linkage, 0, "current_user", Category::System}; |
| 391 | 428 | ||
| 392 | SwitchableSetting<ConsoleMode> use_docked_mode{linkage, | 429 | SwitchableSetting<ConsoleMode> use_docked_mode{linkage, |
| 430 | #ifdef ANDROID | ||
| 431 | ConsoleMode::Handheld, | ||
| 432 | #else | ||
| 393 | ConsoleMode::Docked, | 433 | ConsoleMode::Docked, |
| 434 | #endif | ||
| 394 | "use_docked_mode", | 435 | "use_docked_mode", |
| 395 | Category::System, | 436 | Category::System, |
| 396 | Specialization::Radio, | 437 | Specialization::Radio, |
| 397 | true, | 438 | true, |
| 398 | true}; | 439 | true}; |
| 399 | 440 | ||
| 441 | // Linux | ||
| 442 | SwitchableSetting<bool> enable_gamemode{linkage, true, "enable_gamemode", Category::Linux}; | ||
| 443 | |||
| 400 | // Controls | 444 | // Controls |
| 401 | InputSetting<std::array<PlayerInput, 10>> players; | 445 | InputSetting<std::array<PlayerInput, 10>> players; |
| 402 | 446 | ||
| @@ -534,6 +578,8 @@ bool IsGPULevelExtreme(); | |||
| 534 | bool IsGPULevelHigh(); | 578 | bool IsGPULevelHigh(); |
| 535 | 579 | ||
| 536 | bool IsFastmemEnabled(); | 580 | bool IsFastmemEnabled(); |
| 581 | void SetNceEnabled(bool is_64bit); | ||
| 582 | bool IsNceEnabled(); | ||
| 537 | 583 | ||
| 538 | bool IsDockedMode(); | 584 | bool IsDockedMode(); |
| 539 | 585 | ||
diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 7943223eb..344c04439 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h | |||
| @@ -41,6 +41,7 @@ enum class Category : u32 { | |||
| 41 | Multiplayer, | 41 | Multiplayer, |
| 42 | Services, | 42 | Services, |
| 43 | Paths, | 43 | Paths, |
| 44 | Linux, | ||
| 44 | MaxEnum, | 45 | MaxEnum, |
| 45 | }; | 46 | }; |
| 46 | 47 | ||
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 11429d7a8..d6351e57e 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h | |||
| @@ -129,6 +129,8 @@ ENUM(ShaderBackend, Glsl, Glasm, SpirV); | |||
| 129 | 129 | ||
| 130 | ENUM(GpuAccuracy, Normal, High, Extreme); | 130 | ENUM(GpuAccuracy, Normal, High, Extreme); |
| 131 | 131 | ||
| 132 | ENUM(CpuBackend, Dynarmic, Nce); | ||
| 133 | |||
| 132 | ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); | 134 | ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); |
| 133 | 135 | ||
| 134 | ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); | 136 | ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); |
diff --git a/src/common/signal_chain.cpp b/src/common/signal_chain.cpp new file mode 100644 index 000000000..2e4fecc48 --- /dev/null +++ b/src/common/signal_chain.cpp | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <dlfcn.h> | ||
| 5 | |||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/dynamic_library.h" | ||
| 8 | #include "common/scope_exit.h" | ||
| 9 | #include "common/signal_chain.h" | ||
| 10 | |||
| 11 | namespace Common { | ||
| 12 | |||
| 13 | template <typename T> | ||
| 14 | T* LookupLibcSymbol(const char* name) { | ||
| 15 | #if defined(__BIONIC__) | ||
| 16 | Common::DynamicLibrary provider("libc.so"); | ||
| 17 | if (!provider.IsOpen()) { | ||
| 18 | UNREACHABLE_MSG("Failed to open libc!"); | ||
| 19 | } | ||
| 20 | #else | ||
| 21 | // For other operating environments, we assume the symbol is not overridden. | ||
| 22 | const char* base = nullptr; | ||
| 23 | Common::DynamicLibrary provider(base); | ||
| 24 | #endif | ||
| 25 | |||
| 26 | void* sym = provider.GetSymbolAddress(name); | ||
| 27 | if (sym == nullptr) { | ||
| 28 | sym = dlsym(RTLD_DEFAULT, name); | ||
| 29 | } | ||
| 30 | if (sym == nullptr) { | ||
| 31 | UNREACHABLE_MSG("Unable to find symbol {}!", name); | ||
| 32 | } | ||
| 33 | |||
| 34 | return reinterpret_cast<T*>(sym); | ||
| 35 | } | ||
| 36 | |||
| 37 | int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact) { | ||
| 38 | static auto libc_sigaction = LookupLibcSymbol<decltype(sigaction)>("sigaction"); | ||
| 39 | return libc_sigaction(signum, act, oldact); | ||
| 40 | } | ||
| 41 | |||
| 42 | } // namespace Common | ||
diff --git a/src/common/signal_chain.h b/src/common/signal_chain.h new file mode 100644 index 000000000..8d06a1bd1 --- /dev/null +++ b/src/common/signal_chain.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #ifndef _WIN32 | ||
| 7 | |||
| 8 | #include <signal.h> | ||
| 9 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | // Android's ART overrides sigaction with its own wrapper. This is problematic for SIGSEGV | ||
| 13 | // in particular, because ART's handler accesses tpidr_el0, which conflicts with NCE. | ||
| 14 | // This extracts the libc symbol and calls it directly. | ||
| 15 | int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact); | ||
| 16 | |||
| 17 | } // namespace Common | ||
| 18 | |||
| 19 | #endif | ||
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index caca9a123..012fdc1e0 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "common/x64/rdtsc.h" | 10 | #include "common/x64/rdtsc.h" |
| 11 | #endif | 11 | #endif |
| 12 | 12 | ||
| 13 | #if defined(ARCHITECTURE_arm64) && defined(__linux__) | 13 | #ifdef HAS_NCE |
| 14 | #include "common/arm64/native_clock.h" | 14 | #include "common/arm64/native_clock.h" |
| 15 | #endif | 15 | #endif |
| 16 | 16 | ||
| @@ -68,7 +68,7 @@ std::unique_ptr<WallClock> CreateOptimalClock() { | |||
| 68 | // - Is not more precise than 1 GHz (1ns resolution) | 68 | // - Is not more precise than 1 GHz (1ns resolution) |
| 69 | return std::make_unique<StandardWallClock>(); | 69 | return std::make_unique<StandardWallClock>(); |
| 70 | } | 70 | } |
| 71 | #elif defined(ARCHITECTURE_arm64) && defined(__linux__) | 71 | #elif defined(HAS_NCE) |
| 72 | return std::make_unique<Arm64::NativeClock>(); | 72 | return std::make_unique<Arm64::NativeClock>(); |
| 73 | #else | 73 | #else |
| 74 | return std::make_unique<StandardWallClock>(); | 74 | return std::make_unique<StandardWallClock>(); |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 597890655..85583941c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -529,6 +529,7 @@ add_library(core STATIC | |||
| 529 | hle/service/hid/hid_server.h | 529 | hle/service/hid/hid_server.h |
| 530 | hle/service/hid/hid_system_server.cpp | 530 | hle/service/hid/hid_system_server.cpp |
| 531 | hle/service/hid/hid_system_server.h | 531 | hle/service/hid/hid_system_server.h |
| 532 | hle/service/hid/hid_util.h | ||
| 532 | hle/service/hid/hidbus.cpp | 533 | hle/service/hid/hidbus.cpp |
| 533 | hle/service/hid/hidbus.h | 534 | hle/service/hid/hidbus.h |
| 534 | hle/service/hid/irs.cpp | 535 | hle/service/hid/irs.cpp |
| @@ -540,8 +541,8 @@ add_library(core STATIC | |||
| 540 | hle/service/hid/xcd.cpp | 541 | hle/service/hid/xcd.cpp |
| 541 | hle/service/hid/xcd.h | 542 | hle/service/hid/xcd.h |
| 542 | hle/service/hid/errors.h | 543 | hle/service/hid/errors.h |
| 543 | hle/service/hid/controllers/console_sixaxis.cpp | 544 | hle/service/hid/controllers/console_six_axis.cpp |
| 544 | hle/service/hid/controllers/console_sixaxis.h | 545 | hle/service/hid/controllers/console_six_axis.h |
| 545 | hle/service/hid/controllers/controller_base.cpp | 546 | hle/service/hid/controllers/controller_base.cpp |
| 546 | hle/service/hid/controllers/controller_base.h | 547 | hle/service/hid/controllers/controller_base.h |
| 547 | hle/service/hid/controllers/debug_pad.cpp | 548 | hle/service/hid/controllers/debug_pad.cpp |
| @@ -556,6 +557,10 @@ add_library(core STATIC | |||
| 556 | hle/service/hid/controllers/npad.h | 557 | hle/service/hid/controllers/npad.h |
| 557 | hle/service/hid/controllers/palma.cpp | 558 | hle/service/hid/controllers/palma.cpp |
| 558 | 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 | ||
| 559 | hle/service/hid/controllers/stubbed.cpp | 564 | hle/service/hid/controllers/stubbed.cpp |
| 560 | hle/service/hid/controllers/stubbed.h | 565 | hle/service/hid/controllers/stubbed.h |
| 561 | hle/service/hid/controllers/touchscreen.cpp | 566 | hle/service/hid/controllers/touchscreen.cpp |
| @@ -921,6 +926,22 @@ if (ENABLE_WEB_SERVICE) | |||
| 921 | target_link_libraries(core PRIVATE web_service) | 926 | target_link_libraries(core PRIVATE web_service) |
| 922 | endif() | 927 | endif() |
| 923 | 928 | ||
| 929 | if (HAS_NCE) | ||
| 930 | enable_language(C ASM) | ||
| 931 | set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp") | ||
| 932 | |||
| 933 | target_sources(core PRIVATE | ||
| 934 | arm/nce/arm_nce.cpp | ||
| 935 | arm/nce/arm_nce.h | ||
| 936 | arm/nce/arm_nce.s | ||
| 937 | arm/nce/guest_context.h | ||
| 938 | arm/nce/patcher.cpp | ||
| 939 | arm/nce/patcher.h | ||
| 940 | arm/nce/instructions.h | ||
| 941 | ) | ||
| 942 | target_link_libraries(core PRIVATE merry::oaknut) | ||
| 943 | endif() | ||
| 944 | |||
| 924 | if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) | 945 | if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) |
| 925 | target_sources(core PRIVATE | 946 | target_sources(core PRIVATE |
| 926 | arm/dynarmic/arm_dynarmic.h | 947 | arm/dynarmic/arm_dynarmic.h |
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 558fba5bd..d231bf89c 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp | |||
| @@ -201,6 +201,8 @@ void ARM_Interface::Run() { | |||
| 201 | if (True(hr & HaltReason::DataAbort)) { | 201 | if (True(hr & HaltReason::DataAbort)) { |
| 202 | if (system.DebuggerEnabled()) { | 202 | if (system.DebuggerEnabled()) { |
| 203 | system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); | 203 | system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); |
| 204 | } else { | ||
| 205 | LogBacktrace(); | ||
| 204 | } | 206 | } |
| 205 | current_thread->RequestSuspend(SuspendType::Debug); | 207 | current_thread->RequestSuspend(SuspendType::Debug); |
| 206 | break; | 208 | break; |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 3d866ff6f..a9d9ac09d 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -81,6 +81,9 @@ public: | |||
| 81 | // thread context to be 800 bytes in size. | 81 | // thread context to be 800 bytes in size. |
| 82 | static_assert(sizeof(ThreadContext64) == 0x320); | 82 | static_assert(sizeof(ThreadContext64) == 0x320); |
| 83 | 83 | ||
| 84 | /// Perform any backend-specific initialization. | ||
| 85 | virtual void Initialize() {} | ||
| 86 | |||
| 84 | /// Runs the CPU until an event happens | 87 | /// Runs the CPU until an event happens |
| 85 | void Run(); | 88 | void Run(); |
| 86 | 89 | ||
diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp new file mode 100644 index 000000000..f7bdafd39 --- /dev/null +++ b/src/core/arm/nce/arm_nce.cpp | |||
| @@ -0,0 +1,400 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <cinttypes> | ||
| 5 | #include <memory> | ||
| 6 | |||
| 7 | #include "common/signal_chain.h" | ||
| 8 | #include "core/arm/nce/arm_nce.h" | ||
| 9 | #include "core/arm/nce/patcher.h" | ||
| 10 | #include "core/core.h" | ||
| 11 | #include "core/memory.h" | ||
| 12 | |||
| 13 | #include "core/hle/kernel/k_process.h" | ||
| 14 | |||
| 15 | #include <signal.h> | ||
| 16 | #include <sys/syscall.h> | ||
| 17 | #include <unistd.h> | ||
| 18 | |||
| 19 | namespace Core { | ||
| 20 | |||
| 21 | namespace { | ||
| 22 | |||
| 23 | struct sigaction g_orig_action; | ||
| 24 | |||
| 25 | // Verify assembly offsets. | ||
| 26 | using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters; | ||
| 27 | static_assert(offsetof(NativeExecutionParameters, native_context) == TpidrEl0NativeContext); | ||
| 28 | static_assert(offsetof(NativeExecutionParameters, lock) == TpidrEl0Lock); | ||
| 29 | static_assert(offsetof(NativeExecutionParameters, magic) == TpidrEl0TlsMagic); | ||
| 30 | |||
| 31 | fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) { | ||
| 32 | _aarch64_ctx* header = reinterpret_cast<_aarch64_ctx*>(&host_ctx.__reserved); | ||
| 33 | while (header->magic != FPSIMD_MAGIC) { | ||
| 34 | header = reinterpret_cast<_aarch64_ctx*>(reinterpret_cast<char*>(header) + header->size); | ||
| 35 | } | ||
| 36 | return reinterpret_cast<fpsimd_context*>(header); | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace | ||
| 40 | |||
| 41 | void* ARM_NCE::RestoreGuestContext(void* raw_context) { | ||
| 42 | // Retrieve the host context. | ||
| 43 | auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext; | ||
| 44 | |||
| 45 | // Thread-local parameters will be located in x9. | ||
| 46 | auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs[9]); | ||
| 47 | auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context); | ||
| 48 | |||
| 49 | // Retrieve the host floating point state. | ||
| 50 | auto* fpctx = GetFloatingPointState(host_ctx); | ||
| 51 | |||
| 52 | // Save host callee-saved registers. | ||
| 53 | std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &fpctx->vregs[8], | ||
| 54 | sizeof(guest_ctx->host_ctx.host_saved_vregs)); | ||
| 55 | std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &host_ctx.regs[19], | ||
| 56 | sizeof(guest_ctx->host_ctx.host_saved_regs)); | ||
| 57 | |||
| 58 | // Save stack pointer. | ||
| 59 | guest_ctx->host_ctx.host_sp = host_ctx.sp; | ||
| 60 | |||
| 61 | // Restore all guest state except tpidr_el0. | ||
| 62 | host_ctx.sp = guest_ctx->sp; | ||
| 63 | host_ctx.pc = guest_ctx->pc; | ||
| 64 | host_ctx.pstate = guest_ctx->pstate; | ||
| 65 | fpctx->fpcr = guest_ctx->fpcr; | ||
| 66 | fpctx->fpsr = guest_ctx->fpsr; | ||
| 67 | std::memcpy(host_ctx.regs, guest_ctx->cpu_registers.data(), sizeof(host_ctx.regs)); | ||
| 68 | std::memcpy(fpctx->vregs, guest_ctx->vector_registers.data(), sizeof(fpctx->vregs)); | ||
| 69 | |||
| 70 | // Return the new thread-local storage pointer. | ||
| 71 | return tpidr; | ||
| 72 | } | ||
| 73 | |||
| 74 | void ARM_NCE::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) { | ||
| 75 | // Retrieve the host context. | ||
| 76 | auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext; | ||
| 77 | |||
| 78 | // Retrieve the host floating point state. | ||
| 79 | auto* fpctx = GetFloatingPointState(host_ctx); | ||
| 80 | |||
| 81 | // Save all guest registers except tpidr_el0. | ||
| 82 | std::memcpy(guest_ctx->cpu_registers.data(), host_ctx.regs, sizeof(host_ctx.regs)); | ||
| 83 | std::memcpy(guest_ctx->vector_registers.data(), fpctx->vregs, sizeof(fpctx->vregs)); | ||
| 84 | guest_ctx->fpsr = fpctx->fpsr; | ||
| 85 | guest_ctx->fpcr = fpctx->fpcr; | ||
| 86 | guest_ctx->pstate = static_cast<u32>(host_ctx.pstate); | ||
| 87 | guest_ctx->pc = host_ctx.pc; | ||
| 88 | guest_ctx->sp = host_ctx.sp; | ||
| 89 | |||
| 90 | // Restore stack pointer. | ||
| 91 | host_ctx.sp = guest_ctx->host_ctx.host_sp; | ||
| 92 | |||
| 93 | // Restore host callee-saved registers. | ||
| 94 | std::memcpy(&host_ctx.regs[19], guest_ctx->host_ctx.host_saved_regs.data(), | ||
| 95 | sizeof(guest_ctx->host_ctx.host_saved_regs)); | ||
| 96 | std::memcpy(&fpctx->vregs[8], guest_ctx->host_ctx.host_saved_vregs.data(), | ||
| 97 | sizeof(guest_ctx->host_ctx.host_saved_vregs)); | ||
| 98 | |||
| 99 | // Return from the call on exit by setting pc to x30. | ||
| 100 | host_ctx.pc = guest_ctx->host_ctx.host_saved_regs[11]; | ||
| 101 | |||
| 102 | // Clear esr_el1 and return it. | ||
| 103 | host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0); | ||
| 104 | } | ||
| 105 | |||
| 106 | bool ARM_NCE::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) { | ||
| 107 | auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext; | ||
| 108 | auto* info = static_cast<siginfo_t*>(raw_info); | ||
| 109 | |||
| 110 | // Try to handle an invalid access. | ||
| 111 | // TODO: handle accesses which split a page? | ||
| 112 | const Common::ProcessAddress addr = | ||
| 113 | (reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK); | ||
| 114 | if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) { | ||
| 115 | // We handled the access successfully and are returning to guest code. | ||
| 116 | return true; | ||
| 117 | } | ||
| 118 | |||
| 119 | // We can't handle the access, so determine why we crashed. | ||
| 120 | const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr); | ||
| 121 | |||
| 122 | // For data aborts, skip the instruction and return to guest code. | ||
| 123 | // This will allow games to continue in many scenarios where they would otherwise crash. | ||
| 124 | if (!is_prefetch_abort) { | ||
| 125 | host_ctx.pc += 4; | ||
| 126 | return true; | ||
| 127 | } | ||
| 128 | |||
| 129 | // This is a prefetch abort. | ||
| 130 | guest_ctx->esr_el1.fetch_or(static_cast<u64>(HaltReason::PrefetchAbort)); | ||
| 131 | |||
| 132 | // Forcibly mark the context as locked. We are still running. | ||
| 133 | // We may race with SignalInterrupt here: | ||
| 134 | // - If we lose the race, then SignalInterrupt will send us a signal we are masking, | ||
| 135 | // and it will do nothing when it is unmasked, as we have already left guest code. | ||
| 136 | // - If we win the race, then SignalInterrupt will wait for us to unlock first. | ||
| 137 | auto& thread_params = guest_ctx->parent->running_thread->GetNativeExecutionParameters(); | ||
| 138 | thread_params.lock.store(SpinLockLocked); | ||
| 139 | |||
| 140 | // Return to host. | ||
| 141 | SaveGuestContext(guest_ctx, raw_context); | ||
| 142 | return false; | ||
| 143 | } | ||
| 144 | |||
| 145 | void ARM_NCE::HandleHostFault(int sig, void* raw_info, void* raw_context) { | ||
| 146 | return g_orig_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context); | ||
| 147 | } | ||
| 148 | |||
| 149 | HaltReason ARM_NCE::RunJit() { | ||
| 150 | // Get the thread parameters. | ||
| 151 | // TODO: pass the current thread down from ::Run | ||
| 152 | auto* thread = Kernel::GetCurrentThreadPointer(system.Kernel()); | ||
| 153 | auto* thread_params = &thread->GetNativeExecutionParameters(); | ||
| 154 | |||
| 155 | { | ||
| 156 | // Lock our core context. | ||
| 157 | std::scoped_lock lk{lock}; | ||
| 158 | |||
| 159 | // We should not be running. | ||
| 160 | ASSERT(running_thread == nullptr); | ||
| 161 | |||
| 162 | // Check if we need to run. If we have already been halted, we are done. | ||
| 163 | u64 halt = guest_ctx.esr_el1.exchange(0); | ||
| 164 | if (halt != 0) { | ||
| 165 | return static_cast<HaltReason>(halt); | ||
| 166 | } | ||
| 167 | |||
| 168 | // Mark that we are running. | ||
| 169 | running_thread = thread; | ||
| 170 | |||
| 171 | // Acquire the lock on the thread parameters. | ||
| 172 | // This allows us to force synchronization with SignalInterrupt. | ||
| 173 | LockThreadParameters(thread_params); | ||
| 174 | } | ||
| 175 | |||
| 176 | // Assign current members. | ||
| 177 | guest_ctx.parent = this; | ||
| 178 | thread_params->native_context = &guest_ctx; | ||
| 179 | thread_params->tpidr_el0 = guest_ctx.tpidr_el0; | ||
| 180 | thread_params->tpidrro_el0 = guest_ctx.tpidrro_el0; | ||
| 181 | thread_params->is_running = true; | ||
| 182 | |||
| 183 | HaltReason halt{}; | ||
| 184 | |||
| 185 | // TODO: finding and creating the post handler needs to be locked | ||
| 186 | // to deal with dynamic loading of NROs. | ||
| 187 | const auto& post_handlers = system.ApplicationProcess()->GetPostHandlers(); | ||
| 188 | if (auto it = post_handlers.find(guest_ctx.pc); it != post_handlers.end()) { | ||
| 189 | halt = ReturnToRunCodeByTrampoline(thread_params, &guest_ctx, it->second); | ||
| 190 | } else { | ||
| 191 | halt = ReturnToRunCodeByExceptionLevelChange(thread_id, thread_params); | ||
| 192 | } | ||
| 193 | |||
| 194 | // Unload members. | ||
| 195 | // The thread does not change, so we can persist the old reference. | ||
| 196 | guest_ctx.tpidr_el0 = thread_params->tpidr_el0; | ||
| 197 | thread_params->native_context = nullptr; | ||
| 198 | thread_params->is_running = false; | ||
| 199 | |||
| 200 | // Unlock the thread parameters. | ||
| 201 | UnlockThreadParameters(thread_params); | ||
| 202 | |||
| 203 | { | ||
| 204 | // Lock the core context. | ||
| 205 | std::scoped_lock lk{lock}; | ||
| 206 | |||
| 207 | // On exit, we no longer have an active thread. | ||
| 208 | running_thread = nullptr; | ||
| 209 | } | ||
| 210 | |||
| 211 | // Return the halt reason. | ||
| 212 | return halt; | ||
| 213 | } | ||
| 214 | |||
| 215 | HaltReason ARM_NCE::StepJit() { | ||
| 216 | return HaltReason::StepThread; | ||
| 217 | } | ||
| 218 | |||
| 219 | u32 ARM_NCE::GetSvcNumber() const { | ||
| 220 | return guest_ctx.svc_swi; | ||
| 221 | } | ||
| 222 | |||
| 223 | ARM_NCE::ARM_NCE(System& system_, bool uses_wall_clock_, std::size_t core_index_) | ||
| 224 | : ARM_Interface{system_, uses_wall_clock_}, core_index{core_index_} { | ||
| 225 | guest_ctx.system = &system_; | ||
| 226 | } | ||
| 227 | |||
| 228 | ARM_NCE::~ARM_NCE() = default; | ||
| 229 | |||
| 230 | void ARM_NCE::Initialize() { | ||
| 231 | thread_id = gettid(); | ||
| 232 | |||
| 233 | // Setup our signals | ||
| 234 | static std::once_flag flag; | ||
| 235 | std::call_once(flag, [] { | ||
| 236 | using HandlerType = decltype(sigaction::sa_sigaction); | ||
| 237 | |||
| 238 | sigset_t signal_mask; | ||
| 239 | sigemptyset(&signal_mask); | ||
| 240 | sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal); | ||
| 241 | sigaddset(&signal_mask, BreakFromRunCodeSignal); | ||
| 242 | sigaddset(&signal_mask, GuestFaultSignal); | ||
| 243 | |||
| 244 | struct sigaction return_to_run_code_action {}; | ||
| 245 | return_to_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK; | ||
| 246 | return_to_run_code_action.sa_sigaction = reinterpret_cast<HandlerType>( | ||
| 247 | &ARM_NCE::ReturnToRunCodeByExceptionLevelChangeSignalHandler); | ||
| 248 | return_to_run_code_action.sa_mask = signal_mask; | ||
| 249 | Common::SigAction(ReturnToRunCodeByExceptionLevelChangeSignal, &return_to_run_code_action, | ||
| 250 | nullptr); | ||
| 251 | |||
| 252 | struct sigaction break_from_run_code_action {}; | ||
| 253 | break_from_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK; | ||
| 254 | break_from_run_code_action.sa_sigaction = | ||
| 255 | reinterpret_cast<HandlerType>(&ARM_NCE::BreakFromRunCodeSignalHandler); | ||
| 256 | break_from_run_code_action.sa_mask = signal_mask; | ||
| 257 | Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr); | ||
| 258 | |||
| 259 | struct sigaction fault_action {}; | ||
| 260 | fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART; | ||
| 261 | fault_action.sa_sigaction = | ||
| 262 | reinterpret_cast<HandlerType>(&ARM_NCE::GuestFaultSignalHandler); | ||
| 263 | fault_action.sa_mask = signal_mask; | ||
| 264 | Common::SigAction(GuestFaultSignal, &fault_action, &g_orig_action); | ||
| 265 | |||
| 266 | // Simplify call for g_orig_action. | ||
| 267 | // These fields occupy the same space in memory, so this should be a no-op in practice. | ||
| 268 | if (!(g_orig_action.sa_flags & SA_SIGINFO)) { | ||
| 269 | g_orig_action.sa_sigaction = | ||
| 270 | reinterpret_cast<decltype(g_orig_action.sa_sigaction)>(g_orig_action.sa_handler); | ||
| 271 | } | ||
| 272 | }); | ||
| 273 | } | ||
| 274 | |||
| 275 | void ARM_NCE::SetPC(u64 pc) { | ||
| 276 | guest_ctx.pc = pc; | ||
| 277 | } | ||
| 278 | |||
| 279 | u64 ARM_NCE::GetPC() const { | ||
| 280 | return guest_ctx.pc; | ||
| 281 | } | ||
| 282 | |||
| 283 | u64 ARM_NCE::GetSP() const { | ||
| 284 | return guest_ctx.sp; | ||
| 285 | } | ||
| 286 | |||
| 287 | u64 ARM_NCE::GetReg(int index) const { | ||
| 288 | return guest_ctx.cpu_registers[index]; | ||
| 289 | } | ||
| 290 | |||
| 291 | void ARM_NCE::SetReg(int index, u64 value) { | ||
| 292 | guest_ctx.cpu_registers[index] = value; | ||
| 293 | } | ||
| 294 | |||
| 295 | u128 ARM_NCE::GetVectorReg(int index) const { | ||
| 296 | return guest_ctx.vector_registers[index]; | ||
| 297 | } | ||
| 298 | |||
| 299 | void ARM_NCE::SetVectorReg(int index, u128 value) { | ||
| 300 | guest_ctx.vector_registers[index] = value; | ||
| 301 | } | ||
| 302 | |||
| 303 | u32 ARM_NCE::GetPSTATE() const { | ||
| 304 | return guest_ctx.pstate; | ||
| 305 | } | ||
| 306 | |||
| 307 | void ARM_NCE::SetPSTATE(u32 pstate) { | ||
| 308 | guest_ctx.pstate = pstate; | ||
| 309 | } | ||
| 310 | |||
| 311 | u64 ARM_NCE::GetTlsAddress() const { | ||
| 312 | return guest_ctx.tpidrro_el0; | ||
| 313 | } | ||
| 314 | |||
| 315 | void ARM_NCE::SetTlsAddress(u64 address) { | ||
| 316 | guest_ctx.tpidrro_el0 = address; | ||
| 317 | } | ||
| 318 | |||
| 319 | u64 ARM_NCE::GetTPIDR_EL0() const { | ||
| 320 | return guest_ctx.tpidr_el0; | ||
| 321 | } | ||
| 322 | |||
| 323 | void ARM_NCE::SetTPIDR_EL0(u64 value) { | ||
| 324 | guest_ctx.tpidr_el0 = value; | ||
| 325 | } | ||
| 326 | |||
| 327 | void ARM_NCE::SaveContext(ThreadContext64& ctx) const { | ||
| 328 | ctx.cpu_registers = guest_ctx.cpu_registers; | ||
| 329 | ctx.sp = guest_ctx.sp; | ||
| 330 | ctx.pc = guest_ctx.pc; | ||
| 331 | ctx.pstate = guest_ctx.pstate; | ||
| 332 | ctx.vector_registers = guest_ctx.vector_registers; | ||
| 333 | ctx.fpcr = guest_ctx.fpcr; | ||
| 334 | ctx.fpsr = guest_ctx.fpsr; | ||
| 335 | ctx.tpidr = guest_ctx.tpidr_el0; | ||
| 336 | } | ||
| 337 | |||
| 338 | void ARM_NCE::LoadContext(const ThreadContext64& ctx) { | ||
| 339 | guest_ctx.cpu_registers = ctx.cpu_registers; | ||
| 340 | guest_ctx.sp = ctx.sp; | ||
| 341 | guest_ctx.pc = ctx.pc; | ||
| 342 | guest_ctx.pstate = ctx.pstate; | ||
| 343 | guest_ctx.vector_registers = ctx.vector_registers; | ||
| 344 | guest_ctx.fpcr = ctx.fpcr; | ||
| 345 | guest_ctx.fpsr = ctx.fpsr; | ||
| 346 | guest_ctx.tpidr_el0 = ctx.tpidr; | ||
| 347 | } | ||
| 348 | |||
| 349 | void ARM_NCE::SignalInterrupt() { | ||
| 350 | // Lock core context. | ||
| 351 | std::scoped_lock lk{lock}; | ||
| 352 | |||
| 353 | // Add break loop condition. | ||
| 354 | guest_ctx.esr_el1.fetch_or(static_cast<u64>(HaltReason::BreakLoop)); | ||
| 355 | |||
| 356 | // If there is no thread running, we are done. | ||
| 357 | if (running_thread == nullptr) { | ||
| 358 | return; | ||
| 359 | } | ||
| 360 | |||
| 361 | // Lock the thread context. | ||
| 362 | auto* params = &running_thread->GetNativeExecutionParameters(); | ||
| 363 | LockThreadParameters(params); | ||
| 364 | |||
| 365 | if (params->is_running) { | ||
| 366 | // We should signal to the running thread. | ||
| 367 | // The running thread will unlock the thread context. | ||
| 368 | syscall(SYS_tkill, thread_id, BreakFromRunCodeSignal); | ||
| 369 | } else { | ||
| 370 | // If the thread is no longer running, we have nothing to do. | ||
| 371 | UnlockThreadParameters(params); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | void ARM_NCE::ClearInterrupt() { | ||
| 376 | guest_ctx.esr_el1 = {}; | ||
| 377 | } | ||
| 378 | |||
| 379 | void ARM_NCE::ClearInstructionCache() { | ||
| 380 | // TODO: This is not possible to implement correctly on Linux because | ||
| 381 | // we do not have any access to ic iallu. | ||
| 382 | |||
| 383 | // Require accesses to complete. | ||
| 384 | std::atomic_thread_fence(std::memory_order_seq_cst); | ||
| 385 | } | ||
| 386 | |||
| 387 | void ARM_NCE::InvalidateCacheRange(u64 addr, std::size_t size) { | ||
| 388 | this->ClearInstructionCache(); | ||
| 389 | } | ||
| 390 | |||
| 391 | void ARM_NCE::ClearExclusiveState() { | ||
| 392 | // No-op. | ||
| 393 | } | ||
| 394 | |||
| 395 | void ARM_NCE::PageTableChanged(Common::PageTable& page_table, | ||
| 396 | std::size_t new_address_space_size_in_bits) { | ||
| 397 | // No-op. Page table is never used. | ||
| 398 | } | ||
| 399 | |||
| 400 | } // namespace Core | ||
diff --git a/src/core/arm/nce/arm_nce.h b/src/core/arm/nce/arm_nce.h new file mode 100644 index 000000000..5fbd6dbf3 --- /dev/null +++ b/src/core/arm/nce/arm_nce.h | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <atomic> | ||
| 7 | #include <memory> | ||
| 8 | #include <span> | ||
| 9 | #include <unordered_map> | ||
| 10 | #include <vector> | ||
| 11 | |||
| 12 | #include "core/arm/arm_interface.h" | ||
| 13 | #include "core/arm/nce/guest_context.h" | ||
| 14 | |||
| 15 | namespace Core::Memory { | ||
| 16 | class Memory; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace Core { | ||
| 20 | |||
| 21 | class System; | ||
| 22 | |||
| 23 | class ARM_NCE final : public ARM_Interface { | ||
| 24 | public: | ||
| 25 | ARM_NCE(System& system_, bool uses_wall_clock_, std::size_t core_index_); | ||
| 26 | |||
| 27 | ~ARM_NCE() override; | ||
| 28 | |||
| 29 | void Initialize() override; | ||
| 30 | void SetPC(u64 pc) override; | ||
| 31 | u64 GetPC() const override; | ||
| 32 | u64 GetSP() const override; | ||
| 33 | u64 GetReg(int index) const override; | ||
| 34 | void SetReg(int index, u64 value) override; | ||
| 35 | u128 GetVectorReg(int index) const override; | ||
| 36 | void SetVectorReg(int index, u128 value) override; | ||
| 37 | |||
| 38 | u32 GetPSTATE() const override; | ||
| 39 | void SetPSTATE(u32 pstate) override; | ||
| 40 | u64 GetTlsAddress() const override; | ||
| 41 | void SetTlsAddress(u64 address) override; | ||
| 42 | void SetTPIDR_EL0(u64 value) override; | ||
| 43 | u64 GetTPIDR_EL0() const override; | ||
| 44 | |||
| 45 | Architecture GetArchitecture() const override { | ||
| 46 | return Architecture::Aarch64; | ||
| 47 | } | ||
| 48 | |||
| 49 | void SaveContext(ThreadContext32& ctx) const override {} | ||
| 50 | void SaveContext(ThreadContext64& ctx) const override; | ||
| 51 | void LoadContext(const ThreadContext32& ctx) override {} | ||
| 52 | void LoadContext(const ThreadContext64& ctx) override; | ||
| 53 | |||
| 54 | void SignalInterrupt() override; | ||
| 55 | void ClearInterrupt() override; | ||
| 56 | void ClearExclusiveState() override; | ||
| 57 | void ClearInstructionCache() override; | ||
| 58 | void InvalidateCacheRange(u64 addr, std::size_t size) override; | ||
| 59 | void PageTableChanged(Common::PageTable& new_page_table, | ||
| 60 | std::size_t new_address_space_size_in_bits) override; | ||
| 61 | |||
| 62 | protected: | ||
| 63 | HaltReason RunJit() override; | ||
| 64 | HaltReason StepJit() override; | ||
| 65 | |||
| 66 | u32 GetSvcNumber() const override; | ||
| 67 | |||
| 68 | const Kernel::DebugWatchpoint* HaltedWatchpoint() const override { | ||
| 69 | return nullptr; | ||
| 70 | } | ||
| 71 | |||
| 72 | void RewindBreakpointInstruction() override {} | ||
| 73 | |||
| 74 | private: | ||
| 75 | // Assembly definitions. | ||
| 76 | static HaltReason ReturnToRunCodeByTrampoline(void* tpidr, GuestContext* ctx, | ||
| 77 | u64 trampoline_addr); | ||
| 78 | static HaltReason ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr); | ||
| 79 | |||
| 80 | static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info, | ||
| 81 | void* raw_context); | ||
| 82 | static void BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context); | ||
| 83 | static void GuestFaultSignalHandler(int sig, void* info, void* raw_context); | ||
| 84 | |||
| 85 | static void LockThreadParameters(void* tpidr); | ||
| 86 | static void UnlockThreadParameters(void* tpidr); | ||
| 87 | |||
| 88 | private: | ||
| 89 | // C++ implementation functions for assembly definitions. | ||
| 90 | static void* RestoreGuestContext(void* raw_context); | ||
| 91 | static void SaveGuestContext(GuestContext* ctx, void* raw_context); | ||
| 92 | static bool HandleGuestFault(GuestContext* ctx, void* info, void* raw_context); | ||
| 93 | static void HandleHostFault(int sig, void* info, void* raw_context); | ||
| 94 | |||
| 95 | public: | ||
| 96 | // Members set on initialization. | ||
| 97 | std::size_t core_index{}; | ||
| 98 | pid_t thread_id{-1}; | ||
| 99 | |||
| 100 | // Core context. | ||
| 101 | GuestContext guest_ctx; | ||
| 102 | |||
| 103 | // Thread and invalidation info. | ||
| 104 | std::mutex lock; | ||
| 105 | Kernel::KThread* running_thread{}; | ||
| 106 | }; | ||
| 107 | |||
| 108 | } // namespace Core | ||
diff --git a/src/core/arm/nce/arm_nce.s b/src/core/arm/nce/arm_nce.s new file mode 100644 index 000000000..b98e09f31 --- /dev/null +++ b/src/core/arm/nce/arm_nce.s | |||
| @@ -0,0 +1,222 @@ | |||
| 1 | /* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */ | ||
| 2 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||
| 3 | |||
| 4 | #include "core/arm/nce/arm_nce_asm_definitions.h" | ||
| 5 | |||
| 6 | #define LOAD_IMMEDIATE_32(reg, val) \ | ||
| 7 | mov reg, #(((val) >> 0x00) & 0xFFFF); \ | ||
| 8 | movk reg, #(((val) >> 0x10) & 0xFFFF), lsl #16 | ||
| 9 | |||
| 10 | |||
| 11 | /* static HaltReason Core::ARM_NCE::ReturnToRunCodeByTrampoline(void* tpidr, Core::GuestContext* ctx, u64 trampoline_addr) */ | ||
| 12 | .section .text._ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, "ax", %progbits | ||
| 13 | .global _ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm | ||
| 14 | .type _ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, %function | ||
| 15 | _ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm: | ||
| 16 | /* Back up host sp to x3. */ | ||
| 17 | /* Back up host tpidr_el0 to x4. */ | ||
| 18 | mov x3, sp | ||
| 19 | mrs x4, tpidr_el0 | ||
| 20 | |||
| 21 | /* Load guest sp. x5 is used as a scratch register. */ | ||
| 22 | ldr x5, [x1, #(GuestContextSp)] | ||
| 23 | mov sp, x5 | ||
| 24 | |||
| 25 | /* Offset GuestContext pointer to the host member. */ | ||
| 26 | add x5, x1, #(GuestContextHostContext) | ||
| 27 | |||
| 28 | /* Save original host sp and tpidr_el0 (x3, x4) to host context. */ | ||
| 29 | stp x3, x4, [x5, #(HostContextSpTpidrEl0)] | ||
| 30 | |||
| 31 | /* Save all callee-saved host GPRs. */ | ||
| 32 | stp x19, x20, [x5, #(HostContextRegs+0x0)] | ||
| 33 | stp x21, x22, [x5, #(HostContextRegs+0x10)] | ||
| 34 | stp x23, x24, [x5, #(HostContextRegs+0x20)] | ||
| 35 | stp x25, x26, [x5, #(HostContextRegs+0x30)] | ||
| 36 | stp x27, x28, [x5, #(HostContextRegs+0x40)] | ||
| 37 | stp x29, x30, [x5, #(HostContextRegs+0x50)] | ||
| 38 | |||
| 39 | /* Save all callee-saved host FPRs. */ | ||
| 40 | stp q8, q9, [x5, #(HostContextVregs+0x0)] | ||
| 41 | stp q10, q11, [x5, #(HostContextVregs+0x20)] | ||
| 42 | stp q12, q13, [x5, #(HostContextVregs+0x40)] | ||
| 43 | stp q14, q15, [x5, #(HostContextVregs+0x60)] | ||
| 44 | |||
| 45 | /* Load guest tpidr_el0 from argument. */ | ||
| 46 | msr tpidr_el0, x0 | ||
| 47 | |||
| 48 | /* Tail call the trampoline to restore guest state. */ | ||
| 49 | br x2 | ||
| 50 | |||
| 51 | |||
| 52 | /* static HaltReason Core::ARM_NCE::ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr) */ | ||
| 53 | .section .text._ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv, "ax", %progbits | ||
| 54 | .global _ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv | ||
| 55 | .type _ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv, %function | ||
| 56 | _ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv: | ||
| 57 | /* This jumps to the signal handler, which will restore the entire context. */ | ||
| 58 | /* On entry, x0 = thread id, which is already in the right place. */ | ||
| 59 | |||
| 60 | /* Move tpidr to x9 so it is not trampled. */ | ||
| 61 | mov x9, x1 | ||
| 62 | |||
| 63 | /* Set up arguments. */ | ||
| 64 | mov x8, #(__NR_tkill) | ||
| 65 | mov x1, #(ReturnToRunCodeByExceptionLevelChangeSignal) | ||
| 66 | |||
| 67 | /* Tail call the signal handler. */ | ||
| 68 | svc #0 | ||
| 69 | |||
| 70 | /* Block execution from flowing here. */ | ||
| 71 | brk #1000 | ||
| 72 | |||
| 73 | |||
| 74 | /* static void Core::ARM_NCE::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info, void* raw_context) */ | ||
| 75 | .section .text._ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, "ax", %progbits | ||
| 76 | .global _ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_ | ||
| 77 | .type _ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, %function | ||
| 78 | _ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_: | ||
| 79 | stp x29, x30, [sp, #-0x10]! | ||
| 80 | mov x29, sp | ||
| 81 | |||
| 82 | /* Call the context restorer with the raw context. */ | ||
| 83 | mov x0, x2 | ||
| 84 | bl _ZN4Core7ARM_NCE19RestoreGuestContextEPv | ||
| 85 | |||
| 86 | /* Save the old value of tpidr_el0. */ | ||
| 87 | mrs x8, tpidr_el0 | ||
| 88 | ldr x9, [x0, #(TpidrEl0NativeContext)] | ||
| 89 | str x8, [x9, #(GuestContextHostContext + HostContextTpidrEl0)] | ||
| 90 | |||
| 91 | /* Set our new tpidr_el0. */ | ||
| 92 | msr tpidr_el0, x0 | ||
| 93 | |||
| 94 | /* Unlock the context. */ | ||
| 95 | bl _ZN4Core7ARM_NCE22UnlockThreadParametersEPv | ||
| 96 | |||
| 97 | /* Returning from here will enter the guest. */ | ||
| 98 | ldp x29, x30, [sp], #0x10 | ||
| 99 | ret | ||
| 100 | |||
| 101 | |||
| 102 | /* static void Core::ARM_NCE::BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context) */ | ||
| 103 | .section .text._ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_, "ax", %progbits | ||
| 104 | .global _ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_ | ||
| 105 | .type _ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_, %function | ||
| 106 | _ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_: | ||
| 107 | /* Check to see if we have the correct TLS magic. */ | ||
| 108 | mrs x8, tpidr_el0 | ||
| 109 | ldr w9, [x8, #(TpidrEl0TlsMagic)] | ||
| 110 | |||
| 111 | LOAD_IMMEDIATE_32(w10, TlsMagic) | ||
| 112 | |||
| 113 | cmp w9, w10 | ||
| 114 | b.ne 1f | ||
| 115 | |||
| 116 | /* Correct TLS magic, so this is a guest interrupt. */ | ||
| 117 | /* Restore host tpidr_el0. */ | ||
| 118 | ldr x0, [x8, #(TpidrEl0NativeContext)] | ||
| 119 | ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)] | ||
| 120 | msr tpidr_el0, x3 | ||
| 121 | |||
| 122 | /* Tail call the restorer. */ | ||
| 123 | mov x1, x2 | ||
| 124 | b _ZN4Core7ARM_NCE16SaveGuestContextEPNS_12GuestContextEPv | ||
| 125 | |||
| 126 | /* Returning from here will enter host code. */ | ||
| 127 | |||
| 128 | 1: | ||
| 129 | /* Incorrect TLS magic, so this is a spurious signal. */ | ||
| 130 | ret | ||
| 131 | |||
| 132 | |||
| 133 | /* static void Core::ARM_NCE::GuestFaultSignalHandler(int sig, void* info, void* raw_context) */ | ||
| 134 | .section .text._ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_, "ax", %progbits | ||
| 135 | .global _ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_ | ||
| 136 | .type _ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_, %function | ||
| 137 | _ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_: | ||
| 138 | /* Check to see if we have the correct TLS magic. */ | ||
| 139 | mrs x8, tpidr_el0 | ||
| 140 | ldr w9, [x8, #(TpidrEl0TlsMagic)] | ||
| 141 | |||
| 142 | LOAD_IMMEDIATE_32(w10, TlsMagic) | ||
| 143 | |||
| 144 | cmp w9, w10 | ||
| 145 | b.eq 1f | ||
| 146 | |||
| 147 | /* Incorrect TLS magic, so this is a host fault. */ | ||
| 148 | /* Tail call the handler. */ | ||
| 149 | b _ZN4Core7ARM_NCE15HandleHostFaultEiPvS1_ | ||
| 150 | |||
| 151 | 1: | ||
| 152 | /* Correct TLS magic, so this is a guest fault. */ | ||
| 153 | stp x29, x30, [sp, #-0x20]! | ||
| 154 | str x19, [sp, #0x10] | ||
| 155 | mov x29, sp | ||
| 156 | |||
| 157 | /* Save the old tpidr_el0. */ | ||
| 158 | mov x19, x8 | ||
| 159 | |||
| 160 | /* Restore host tpidr_el0. */ | ||
| 161 | ldr x0, [x8, #(TpidrEl0NativeContext)] | ||
| 162 | ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)] | ||
| 163 | msr tpidr_el0, x3 | ||
| 164 | |||
| 165 | /* Call the handler. */ | ||
| 166 | bl _ZN4Core7ARM_NCE16HandleGuestFaultEPNS_12GuestContextEPvS3_ | ||
| 167 | |||
| 168 | /* If the handler returned false, we want to preserve the host tpidr_el0. */ | ||
| 169 | cbz x0, 2f | ||
| 170 | |||
| 171 | /* Otherwise, restore guest tpidr_el0. */ | ||
| 172 | msr tpidr_el0, x19 | ||
| 173 | |||
| 174 | 2: | ||
| 175 | ldr x19, [sp, #0x10] | ||
| 176 | ldp x29, x30, [sp], #0x20 | ||
| 177 | ret | ||
| 178 | |||
| 179 | |||
| 180 | /* static void Core::ARM_NCE::LockThreadParameters(void* tpidr) */ | ||
| 181 | .section .text._ZN4Core7ARM_NCE20LockThreadParametersEPv, "ax", %progbits | ||
| 182 | .global _ZN4Core7ARM_NCE20LockThreadParametersEPv | ||
| 183 | .type _ZN4Core7ARM_NCE20LockThreadParametersEPv, %function | ||
| 184 | _ZN4Core7ARM_NCE20LockThreadParametersEPv: | ||
| 185 | /* Offset to lock member. */ | ||
| 186 | add x0, x0, #(TpidrEl0Lock) | ||
| 187 | |||
| 188 | 1: | ||
| 189 | /* Clear the monitor. */ | ||
| 190 | clrex | ||
| 191 | |||
| 192 | 2: | ||
| 193 | /* Load-linked with acquire ordering. */ | ||
| 194 | ldaxr w1, [x0] | ||
| 195 | |||
| 196 | /* If the value was SpinLockLocked, clear monitor and retry. */ | ||
| 197 | cbz w1, 1b | ||
| 198 | |||
| 199 | /* Store-conditional SpinLockLocked with relaxed ordering. */ | ||
| 200 | stxr w1, wzr, [x0] | ||
| 201 | |||
| 202 | /* If we failed to store, retry. */ | ||
| 203 | cbnz w1, 2b | ||
| 204 | |||
| 205 | ret | ||
| 206 | |||
| 207 | |||
| 208 | /* static void Core::ARM_NCE::UnlockThreadParameters(void* tpidr) */ | ||
| 209 | .section .text._ZN4Core7ARM_NCE22UnlockThreadParametersEPv, "ax", %progbits | ||
| 210 | .global _ZN4Core7ARM_NCE22UnlockThreadParametersEPv | ||
| 211 | .type _ZN4Core7ARM_NCE22UnlockThreadParametersEPv, %function | ||
| 212 | _ZN4Core7ARM_NCE22UnlockThreadParametersEPv: | ||
| 213 | /* Offset to lock member. */ | ||
| 214 | add x0, x0, #(TpidrEl0Lock) | ||
| 215 | |||
| 216 | /* Load SpinLockUnlocked. */ | ||
| 217 | mov w1, #(SpinLockUnlocked) | ||
| 218 | |||
| 219 | /* Store value with release ordering. */ | ||
| 220 | stlr w1, [x0] | ||
| 221 | |||
| 222 | ret | ||
diff --git a/src/core/arm/nce/arm_nce_asm_definitions.h b/src/core/arm/nce/arm_nce_asm_definitions.h new file mode 100644 index 000000000..8a9b285b5 --- /dev/null +++ b/src/core/arm/nce/arm_nce_asm_definitions.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | /* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */ | ||
| 2 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #define __ASSEMBLY__ | ||
| 7 | |||
| 8 | #include <asm-generic/signal.h> | ||
| 9 | #include <asm-generic/unistd.h> | ||
| 10 | |||
| 11 | #define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2 | ||
| 12 | #define BreakFromRunCodeSignal SIGURG | ||
| 13 | #define GuestFaultSignal SIGSEGV | ||
| 14 | |||
| 15 | #define GuestContextSp 0xF8 | ||
| 16 | #define GuestContextHostContext 0x320 | ||
| 17 | |||
| 18 | #define HostContextSpTpidrEl0 0xE0 | ||
| 19 | #define HostContextTpidrEl0 0xE8 | ||
| 20 | #define HostContextRegs 0x0 | ||
| 21 | #define HostContextVregs 0x60 | ||
| 22 | |||
| 23 | #define TpidrEl0NativeContext 0x10 | ||
| 24 | #define TpidrEl0Lock 0x18 | ||
| 25 | #define TpidrEl0TlsMagic 0x20 | ||
| 26 | #define TlsMagic 0x555a5559 | ||
| 27 | |||
| 28 | #define SpinLockLocked 0 | ||
| 29 | #define SpinLockUnlocked 1 | ||
diff --git a/src/core/arm/nce/guest_context.h b/src/core/arm/nce/guest_context.h new file mode 100644 index 000000000..0767a0337 --- /dev/null +++ b/src/core/arm/nce/guest_context.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "core/arm/arm_interface.h" | ||
| 9 | #include "core/arm/nce/arm_nce_asm_definitions.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | |||
| 13 | class ARM_NCE; | ||
| 14 | class System; | ||
| 15 | |||
| 16 | struct HostContext { | ||
| 17 | alignas(16) std::array<u64, 12> host_saved_regs{}; | ||
| 18 | alignas(16) std::array<u128, 8> host_saved_vregs{}; | ||
| 19 | u64 host_sp{}; | ||
| 20 | void* host_tpidr_el0{}; | ||
| 21 | }; | ||
| 22 | |||
| 23 | struct GuestContext { | ||
| 24 | std::array<u64, 31> cpu_registers{}; | ||
| 25 | u64 sp{}; | ||
| 26 | u64 pc{}; | ||
| 27 | u32 fpcr{}; | ||
| 28 | u32 fpsr{}; | ||
| 29 | std::array<u128, 32> vector_registers{}; | ||
| 30 | u32 pstate{}; | ||
| 31 | alignas(16) HostContext host_ctx{}; | ||
| 32 | u64 tpidrro_el0{}; | ||
| 33 | u64 tpidr_el0{}; | ||
| 34 | std::atomic<u64> esr_el1{}; | ||
| 35 | u32 nzcv{}; | ||
| 36 | u32 svc_swi{}; | ||
| 37 | System* system{}; | ||
| 38 | ARM_NCE* parent{}; | ||
| 39 | }; | ||
| 40 | |||
| 41 | // Verify assembly offsets. | ||
| 42 | static_assert(offsetof(GuestContext, sp) == GuestContextSp); | ||
| 43 | static_assert(offsetof(GuestContext, host_ctx) == GuestContextHostContext); | ||
| 44 | static_assert(offsetof(HostContext, host_sp) == HostContextSpTpidrEl0); | ||
| 45 | static_assert(offsetof(HostContext, host_tpidr_el0) - 8 == HostContextSpTpidrEl0); | ||
| 46 | static_assert(offsetof(HostContext, host_tpidr_el0) == HostContextTpidrEl0); | ||
| 47 | static_assert(offsetof(HostContext, host_saved_regs) == HostContextRegs); | ||
| 48 | static_assert(offsetof(HostContext, host_saved_vregs) == HostContextVregs); | ||
| 49 | |||
| 50 | } // namespace Core | ||
diff --git a/src/core/arm/nce/instructions.h b/src/core/arm/nce/instructions.h new file mode 100644 index 000000000..5b56ff857 --- /dev/null +++ b/src/core/arm/nce/instructions.h | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright © 2020 Skyline Team and Contributors | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | |||
| 4 | #include "common/bit_field.h" | ||
| 5 | #include "common/common_types.h" | ||
| 6 | |||
| 7 | namespace Core::NCE { | ||
| 8 | |||
| 9 | enum SystemRegister : u32 { | ||
| 10 | TpidrEl0 = 0x5E82, | ||
| 11 | TpidrroEl0 = 0x5E83, | ||
| 12 | CntfrqEl0 = 0x5F00, | ||
| 13 | CntpctEl0 = 0x5F01, | ||
| 14 | }; | ||
| 15 | |||
| 16 | // https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/SVC--Supervisor-Call- | ||
| 17 | union SVC { | ||
| 18 | constexpr explicit SVC(u32 raw_) : raw{raw_} {} | ||
| 19 | |||
| 20 | constexpr bool Verify() { | ||
| 21 | return (this->GetSig0() == 0x1 && this->GetSig1() == 0x6A0); | ||
| 22 | } | ||
| 23 | |||
| 24 | constexpr u32 GetSig0() { | ||
| 25 | return decltype(sig0)::ExtractValue(raw); | ||
| 26 | } | ||
| 27 | |||
| 28 | constexpr u32 GetValue() { | ||
| 29 | return decltype(value)::ExtractValue(raw); | ||
| 30 | } | ||
| 31 | |||
| 32 | constexpr u32 GetSig1() { | ||
| 33 | return decltype(sig1)::ExtractValue(raw); | ||
| 34 | } | ||
| 35 | |||
| 36 | u32 raw; | ||
| 37 | |||
| 38 | private: | ||
| 39 | BitField<0, 5, u32> sig0; // 0x1 | ||
| 40 | BitField<5, 16, u32> value; // 16-bit immediate | ||
| 41 | BitField<21, 11, u32> sig1; // 0x6A0 | ||
| 42 | }; | ||
| 43 | static_assert(sizeof(SVC) == sizeof(u32)); | ||
| 44 | static_assert(SVC(0xD40000C1).Verify()); | ||
| 45 | static_assert(SVC(0xD40000C1).GetValue() == 0x6); | ||
| 46 | |||
| 47 | // https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/MRS--Move-System-Register- | ||
| 48 | union MRS { | ||
| 49 | constexpr explicit MRS(u32 raw_) : raw{raw_} {} | ||
| 50 | |||
| 51 | constexpr bool Verify() { | ||
| 52 | return (this->GetSig() == 0xD53); | ||
| 53 | } | ||
| 54 | |||
| 55 | constexpr u32 GetRt() { | ||
| 56 | return decltype(rt)::ExtractValue(raw); | ||
| 57 | } | ||
| 58 | |||
| 59 | constexpr u32 GetSystemReg() { | ||
| 60 | return decltype(system_reg)::ExtractValue(raw); | ||
| 61 | } | ||
| 62 | |||
| 63 | constexpr u32 GetSig() { | ||
| 64 | return decltype(sig)::ExtractValue(raw); | ||
| 65 | } | ||
| 66 | |||
| 67 | u32 raw; | ||
| 68 | |||
| 69 | private: | ||
| 70 | BitField<0, 5, u32> rt; // destination register | ||
| 71 | BitField<5, 15, u32> system_reg; // source system register | ||
| 72 | BitField<20, 12, u32> sig; // 0xD53 | ||
| 73 | }; | ||
| 74 | static_assert(sizeof(MRS) == sizeof(u32)); | ||
| 75 | static_assert(MRS(0xD53BE020).Verify()); | ||
| 76 | static_assert(MRS(0xD53BE020).GetSystemReg() == CntpctEl0); | ||
| 77 | static_assert(MRS(0xD53BE020).GetRt() == 0x0); | ||
| 78 | |||
| 79 | // https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/MSR--register---Move-general-purpose-register-to-System-Register- | ||
| 80 | union MSR { | ||
| 81 | constexpr explicit MSR(u32 raw_) : raw{raw_} {} | ||
| 82 | |||
| 83 | constexpr bool Verify() { | ||
| 84 | return this->GetSig() == 0xD51; | ||
| 85 | } | ||
| 86 | |||
| 87 | constexpr u32 GetRt() { | ||
| 88 | return decltype(rt)::ExtractValue(raw); | ||
| 89 | } | ||
| 90 | |||
| 91 | constexpr u32 GetSystemReg() { | ||
| 92 | return decltype(system_reg)::ExtractValue(raw); | ||
| 93 | } | ||
| 94 | |||
| 95 | constexpr u32 GetSig() { | ||
| 96 | return decltype(sig)::ExtractValue(raw); | ||
| 97 | } | ||
| 98 | |||
| 99 | u32 raw; | ||
| 100 | |||
| 101 | private: | ||
| 102 | BitField<0, 5, u32> rt; // source register | ||
| 103 | BitField<5, 15, u32> system_reg; // destination system register | ||
| 104 | BitField<20, 12, u32> sig; // 0xD51 | ||
| 105 | }; | ||
| 106 | static_assert(sizeof(MSR) == sizeof(u32)); | ||
| 107 | static_assert(MSR(0xD51BD040).Verify()); | ||
| 108 | static_assert(MSR(0xD51BD040).GetSystemReg() == TpidrEl0); | ||
| 109 | static_assert(MSR(0xD51BD040).GetRt() == 0x0); | ||
| 110 | |||
| 111 | // https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDXR--Load-Exclusive-Register- | ||
| 112 | // https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDXP--Load-Exclusive-Pair-of-Registers- | ||
| 113 | // https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/STXR--Store-Exclusive-Register- | ||
| 114 | // https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/STXP--Store-Exclusive-Pair-of-registers- | ||
| 115 | union Exclusive { | ||
| 116 | constexpr explicit Exclusive(u32 raw_) : raw{raw_} {} | ||
| 117 | |||
| 118 | constexpr bool Verify() { | ||
| 119 | return this->GetSig() == 0x10; | ||
| 120 | } | ||
| 121 | |||
| 122 | constexpr u32 GetSig() { | ||
| 123 | return decltype(sig)::ExtractValue(raw); | ||
| 124 | } | ||
| 125 | |||
| 126 | constexpr u32 AsOrdered() { | ||
| 127 | return raw | decltype(o0)::FormatValue(1); | ||
| 128 | } | ||
| 129 | |||
| 130 | u32 raw; | ||
| 131 | |||
| 132 | private: | ||
| 133 | BitField<0, 5, u32> rt; // memory operand | ||
| 134 | BitField<5, 5, u32> rn; // register operand 1 | ||
| 135 | BitField<10, 5, u32> rt2; // register operand 2 | ||
| 136 | BitField<15, 1, u32> o0; // ordered | ||
| 137 | BitField<16, 5, u32> rs; // status register | ||
| 138 | BitField<21, 2, u32> l; // operation type | ||
| 139 | BitField<23, 7, u32> sig; // 0x10 | ||
| 140 | BitField<30, 2, u32> size; // size | ||
| 141 | }; | ||
| 142 | static_assert(Exclusive(0xC85FFC00).Verify()); | ||
| 143 | static_assert(Exclusive(0xC85FFC00).AsOrdered() == 0xC85FFC00); | ||
| 144 | static_assert(Exclusive(0xC85F7C00).AsOrdered() == 0xC85FFC00); | ||
| 145 | static_assert(Exclusive(0xC8200440).AsOrdered() == 0xC8208440); | ||
| 146 | |||
| 147 | } // namespace Core::NCE | ||
diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp new file mode 100644 index 000000000..ec8527224 --- /dev/null +++ b/src/core/arm/nce/patcher.cpp | |||
| @@ -0,0 +1,474 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/arm64/native_clock.h" | ||
| 5 | #include "common/bit_cast.h" | ||
| 6 | #include "common/literals.h" | ||
| 7 | #include "core/arm/nce/arm_nce.h" | ||
| 8 | #include "core/arm/nce/guest_context.h" | ||
| 9 | #include "core/arm/nce/instructions.h" | ||
| 10 | #include "core/arm/nce/patcher.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/core_timing.h" | ||
| 13 | #include "core/hle/kernel/svc.h" | ||
| 14 | |||
| 15 | namespace Core::NCE { | ||
| 16 | |||
| 17 | using namespace Common::Literals; | ||
| 18 | using namespace oaknut::util; | ||
| 19 | |||
| 20 | using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters; | ||
| 21 | |||
| 22 | constexpr size_t MaxRelativeBranch = 128_MiB; | ||
| 23 | constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); | ||
| 24 | |||
| 25 | Patcher::Patcher() : c(m_patch_instructions) {} | ||
| 26 | |||
| 27 | Patcher::~Patcher() = default; | ||
| 28 | |||
| 29 | void Patcher::PatchText(const Kernel::PhysicalMemory& program_image, | ||
| 30 | const Kernel::CodeSet::Segment& code) { | ||
| 31 | |||
| 32 | // Write save context helper function. | ||
| 33 | c.l(m_save_context); | ||
| 34 | WriteSaveContext(); | ||
| 35 | |||
| 36 | // Write load context helper function. | ||
| 37 | c.l(m_load_context); | ||
| 38 | WriteLoadContext(); | ||
| 39 | |||
| 40 | // Retrieve text segment data. | ||
| 41 | const auto text = std::span{program_image}.subspan(code.offset, code.size); | ||
| 42 | const auto text_words = | ||
| 43 | std::span<const u32>{reinterpret_cast<const u32*>(text.data()), text.size() / sizeof(u32)}; | ||
| 44 | |||
| 45 | // Loop through instructions, patching as needed. | ||
| 46 | for (u32 i = ModuleCodeIndex; i < static_cast<u32>(text_words.size()); i++) { | ||
| 47 | const u32 inst = text_words[i]; | ||
| 48 | |||
| 49 | const auto AddRelocations = [&] { | ||
| 50 | const uintptr_t this_offset = i * sizeof(u32); | ||
| 51 | const uintptr_t next_offset = this_offset + sizeof(u32); | ||
| 52 | |||
| 53 | // Relocate from here to patch. | ||
| 54 | this->BranchToPatch(this_offset); | ||
| 55 | |||
| 56 | // Relocate from patch to next instruction. | ||
| 57 | return next_offset; | ||
| 58 | }; | ||
| 59 | |||
| 60 | // SVC | ||
| 61 | if (auto svc = SVC{inst}; svc.Verify()) { | ||
| 62 | WriteSvcTrampoline(AddRelocations(), svc.GetValue()); | ||
| 63 | continue; | ||
| 64 | } | ||
| 65 | |||
| 66 | // MRS Xn, TPIDR_EL0 | ||
| 67 | // MRS Xn, TPIDRRO_EL0 | ||
| 68 | if (auto mrs = MRS{inst}; | ||
| 69 | mrs.Verify() && (mrs.GetSystemReg() == TpidrroEl0 || mrs.GetSystemReg() == TpidrEl0)) { | ||
| 70 | const auto src_reg = mrs.GetSystemReg() == TpidrroEl0 ? oaknut::SystemReg::TPIDRRO_EL0 | ||
| 71 | : oaknut::SystemReg::TPIDR_EL0; | ||
| 72 | const auto dest_reg = oaknut::XReg{static_cast<int>(mrs.GetRt())}; | ||
| 73 | WriteMrsHandler(AddRelocations(), dest_reg, src_reg); | ||
| 74 | continue; | ||
| 75 | } | ||
| 76 | |||
| 77 | // MRS Xn, CNTPCT_EL0 | ||
| 78 | if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntpctEl0) { | ||
| 79 | WriteCntpctHandler(AddRelocations(), oaknut::XReg{static_cast<int>(mrs.GetRt())}); | ||
| 80 | continue; | ||
| 81 | } | ||
| 82 | |||
| 83 | // MRS Xn, CNTFRQ_EL0 | ||
| 84 | if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntfrqEl0) { | ||
| 85 | UNREACHABLE(); | ||
| 86 | } | ||
| 87 | |||
| 88 | // MSR TPIDR_EL0, Xn | ||
| 89 | if (auto msr = MSR{inst}; msr.Verify() && msr.GetSystemReg() == TpidrEl0) { | ||
| 90 | WriteMsrHandler(AddRelocations(), oaknut::XReg{static_cast<int>(msr.GetRt())}); | ||
| 91 | continue; | ||
| 92 | } | ||
| 93 | |||
| 94 | if (auto exclusive = Exclusive{inst}; exclusive.Verify()) { | ||
| 95 | m_exclusives.push_back(i); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | // Determine patching mode for the final relocation step | ||
| 100 | const size_t image_size = program_image.size(); | ||
| 101 | this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData; | ||
| 102 | } | ||
| 103 | |||
| 104 | void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, | ||
| 105 | const Kernel::CodeSet::Segment& code, | ||
| 106 | Kernel::PhysicalMemory& program_image, | ||
| 107 | EntryTrampolines* out_trampolines) { | ||
| 108 | const size_t patch_size = GetSectionSize(); | ||
| 109 | const size_t image_size = program_image.size(); | ||
| 110 | |||
| 111 | // Retrieve text segment data. | ||
| 112 | const auto text = std::span{program_image}.subspan(code.offset, code.size); | ||
| 113 | const auto text_words = | ||
| 114 | std::span<u32>{reinterpret_cast<u32*>(text.data()), text.size() / sizeof(u32)}; | ||
| 115 | |||
| 116 | const auto ApplyBranchToPatchRelocation = [&](u32* target, const Relocation& rel) { | ||
| 117 | oaknut::CodeGenerator rc{target}; | ||
| 118 | if (mode == PatchMode::PreText) { | ||
| 119 | rc.B(rel.patch_offset - patch_size - rel.module_offset); | ||
| 120 | } else { | ||
| 121 | rc.B(image_size - rel.module_offset + rel.patch_offset); | ||
| 122 | } | ||
| 123 | }; | ||
| 124 | |||
| 125 | const auto ApplyBranchToModuleRelocation = [&](u32* target, const Relocation& rel) { | ||
| 126 | oaknut::CodeGenerator rc{target}; | ||
| 127 | if (mode == PatchMode::PreText) { | ||
| 128 | rc.B(patch_size - rel.patch_offset + rel.module_offset); | ||
| 129 | } else { | ||
| 130 | rc.B(rel.module_offset - image_size - rel.patch_offset); | ||
| 131 | } | ||
| 132 | }; | ||
| 133 | |||
| 134 | const auto RebasePatch = [&](ptrdiff_t patch_offset) { | ||
| 135 | if (mode == PatchMode::PreText) { | ||
| 136 | return GetInteger(load_base) + patch_offset; | ||
| 137 | } else { | ||
| 138 | return GetInteger(load_base) + image_size + patch_offset; | ||
| 139 | } | ||
| 140 | }; | ||
| 141 | |||
| 142 | const auto RebasePc = [&](uintptr_t module_offset) { | ||
| 143 | if (mode == PatchMode::PreText) { | ||
| 144 | return GetInteger(load_base) + patch_size + module_offset; | ||
| 145 | } else { | ||
| 146 | return GetInteger(load_base) + module_offset; | ||
| 147 | } | ||
| 148 | }; | ||
| 149 | |||
| 150 | // We are now ready to relocate! | ||
| 151 | for (const Relocation& rel : m_branch_to_patch_relocations) { | ||
| 152 | ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel); | ||
| 153 | } | ||
| 154 | for (const Relocation& rel : m_branch_to_module_relocations) { | ||
| 155 | ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32), | ||
| 156 | rel); | ||
| 157 | } | ||
| 158 | |||
| 159 | // Rewrite PC constants and record post trampolines | ||
| 160 | for (const Relocation& rel : m_write_module_pc_relocations) { | ||
| 161 | oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)}; | ||
| 162 | rc.dx(RebasePc(rel.module_offset)); | ||
| 163 | } | ||
| 164 | for (const Trampoline& rel : m_trampolines) { | ||
| 165 | out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)}); | ||
| 166 | } | ||
| 167 | |||
| 168 | // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not. | ||
| 169 | // Convert to ordered to preserve this assumption. | ||
| 170 | for (const ModuleTextAddress i : m_exclusives) { | ||
| 171 | auto exclusive = Exclusive{text_words[i]}; | ||
| 172 | text_words[i] = exclusive.AsOrdered(); | ||
| 173 | } | ||
| 174 | |||
| 175 | // Copy to program image | ||
| 176 | if (this->mode == PatchMode::PreText) { | ||
| 177 | std::memcpy(program_image.data(), m_patch_instructions.data(), | ||
| 178 | m_patch_instructions.size() * sizeof(u32)); | ||
| 179 | } else { | ||
| 180 | program_image.resize(image_size + patch_size); | ||
| 181 | std::memcpy(program_image.data() + image_size, m_patch_instructions.data(), | ||
| 182 | m_patch_instructions.size() * sizeof(u32)); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | size_t Patcher::GetSectionSize() const noexcept { | ||
| 187 | return Common::AlignUp(m_patch_instructions.size() * sizeof(u32), Core::Memory::YUZU_PAGESIZE); | ||
| 188 | } | ||
| 189 | |||
| 190 | void Patcher::WriteLoadContext() { | ||
| 191 | // This function was called, which modifies X30, so use that as a scratch register. | ||
| 192 | // SP contains the guest X30, so save our return X30 to SP + 8, since we have allocated 16 bytes | ||
| 193 | // of stack. | ||
| 194 | c.STR(X30, SP, 8); | ||
| 195 | c.MRS(X30, oaknut::SystemReg::TPIDR_EL0); | ||
| 196 | c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context)); | ||
| 197 | |||
| 198 | // Load system registers. | ||
| 199 | c.LDR(W0, X30, offsetof(GuestContext, fpsr)); | ||
| 200 | c.MSR(oaknut::SystemReg::FPSR, X0); | ||
| 201 | c.LDR(W0, X30, offsetof(GuestContext, fpcr)); | ||
| 202 | c.MSR(oaknut::SystemReg::FPCR, X0); | ||
| 203 | c.LDR(W0, X30, offsetof(GuestContext, nzcv)); | ||
| 204 | c.MSR(oaknut::SystemReg::NZCV, X0); | ||
| 205 | |||
| 206 | // Load all vector registers. | ||
| 207 | static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers); | ||
| 208 | for (int i = 0; i <= 30; i += 2) { | ||
| 209 | c.LDP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i); | ||
| 210 | } | ||
| 211 | |||
| 212 | // Load all general-purpose registers except X30. | ||
| 213 | for (int i = 0; i <= 28; i += 2) { | ||
| 214 | c.LDP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i); | ||
| 215 | } | ||
| 216 | |||
| 217 | // Reload our return X30 from the stack and return. | ||
| 218 | // The patch code will reload the guest X30 for us. | ||
| 219 | c.LDR(X30, SP, 8); | ||
| 220 | c.RET(); | ||
| 221 | } | ||
| 222 | |||
| 223 | void Patcher::WriteSaveContext() { | ||
| 224 | // This function was called, which modifies X30, so use that as a scratch register. | ||
| 225 | // SP contains the guest X30, so save our X30 to SP + 8, since we have allocated 16 bytes of | ||
| 226 | // stack. | ||
| 227 | c.STR(X30, SP, 8); | ||
| 228 | c.MRS(X30, oaknut::SystemReg::TPIDR_EL0); | ||
| 229 | c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context)); | ||
| 230 | |||
| 231 | // Store all general-purpose registers except X30. | ||
| 232 | for (int i = 0; i <= 28; i += 2) { | ||
| 233 | c.STP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i); | ||
| 234 | } | ||
| 235 | |||
| 236 | // Store all vector registers. | ||
| 237 | static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers); | ||
| 238 | for (int i = 0; i <= 30; i += 2) { | ||
| 239 | c.STP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i); | ||
| 240 | } | ||
| 241 | |||
| 242 | // Store guest system registers, X30 and SP, using X0 as a scratch register. | ||
| 243 | c.STR(X0, SP, PRE_INDEXED, -16); | ||
| 244 | c.LDR(X0, SP, 16); | ||
| 245 | c.STR(X0, X30, 8 * 30); | ||
| 246 | c.ADD(X0, SP, 32); | ||
| 247 | c.STR(X0, X30, offsetof(GuestContext, sp)); | ||
| 248 | c.MRS(X0, oaknut::SystemReg::FPSR); | ||
| 249 | c.STR(W0, X30, offsetof(GuestContext, fpsr)); | ||
| 250 | c.MRS(X0, oaknut::SystemReg::FPCR); | ||
| 251 | c.STR(W0, X30, offsetof(GuestContext, fpcr)); | ||
| 252 | c.MRS(X0, oaknut::SystemReg::NZCV); | ||
| 253 | c.STR(W0, X30, offsetof(GuestContext, nzcv)); | ||
| 254 | c.LDR(X0, SP, POST_INDEXED, 16); | ||
| 255 | |||
| 256 | // Reload our return X30 from the stack, and return. | ||
| 257 | c.LDR(X30, SP, 8); | ||
| 258 | c.RET(); | ||
| 259 | } | ||
| 260 | |||
| 261 | void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) { | ||
| 262 | // We are about to start saving state, so we need to lock the context. | ||
| 263 | this->LockContext(); | ||
| 264 | |||
| 265 | // Store guest X30 to the stack. Then, save the context and restore the stack. | ||
| 266 | // This will save all registers except PC, but we know PC at patch time. | ||
| 267 | c.STR(X30, SP, PRE_INDEXED, -16); | ||
| 268 | c.BL(m_save_context); | ||
| 269 | c.LDR(X30, SP, POST_INDEXED, 16); | ||
| 270 | |||
| 271 | // Now that we've saved all registers, we can use any registers as scratch. | ||
| 272 | // Store PC + 4 to arm interface, since we know the instruction offset from the entry point. | ||
| 273 | oaknut::Label pc_after_svc; | ||
| 274 | c.MRS(X1, oaknut::SystemReg::TPIDR_EL0); | ||
| 275 | c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context)); | ||
| 276 | c.LDR(X2, pc_after_svc); | ||
| 277 | c.STR(X2, X1, offsetof(GuestContext, pc)); | ||
| 278 | |||
| 279 | // Store SVC number to execute when we return | ||
| 280 | c.MOV(X2, svc_id); | ||
| 281 | c.STR(W2, X1, offsetof(GuestContext, svc_swi)); | ||
| 282 | |||
| 283 | // We are calling a SVC. Clear esr_el1 and return it. | ||
| 284 | static_assert(std::is_same_v<std::underlying_type_t<HaltReason>, u64>); | ||
| 285 | oaknut::Label retry; | ||
| 286 | c.ADD(X2, X1, offsetof(GuestContext, esr_el1)); | ||
| 287 | c.l(retry); | ||
| 288 | c.LDAXR(X0, X2); | ||
| 289 | c.STLXR(W3, XZR, X2); | ||
| 290 | c.CBNZ(W3, retry); | ||
| 291 | |||
| 292 | // Add "calling SVC" flag. Since this is X0, this is now our return value. | ||
| 293 | c.ORR(X0, X0, static_cast<u64>(HaltReason::SupervisorCall)); | ||
| 294 | |||
| 295 | // Offset the GuestContext pointer to the HostContext member. | ||
| 296 | // STP has limited range of [-512, 504] which we can't reach otherwise | ||
| 297 | // NB: Due to this all offsets below are from the start of HostContext. | ||
| 298 | c.ADD(X1, X1, offsetof(GuestContext, host_ctx)); | ||
| 299 | |||
| 300 | // Reload host TPIDR_EL0 and SP. | ||
| 301 | static_assert(offsetof(HostContext, host_sp) + 8 == offsetof(HostContext, host_tpidr_el0)); | ||
| 302 | c.LDP(X2, X3, X1, offsetof(HostContext, host_sp)); | ||
| 303 | c.MOV(SP, X2); | ||
| 304 | c.MSR(oaknut::SystemReg::TPIDR_EL0, X3); | ||
| 305 | |||
| 306 | // Load callee-saved host registers and return to host. | ||
| 307 | static constexpr size_t HOST_REGS_OFF = offsetof(HostContext, host_saved_regs); | ||
| 308 | static constexpr size_t HOST_VREGS_OFF = offsetof(HostContext, host_saved_vregs); | ||
| 309 | c.LDP(X19, X20, X1, HOST_REGS_OFF); | ||
| 310 | c.LDP(X21, X22, X1, HOST_REGS_OFF + 2 * sizeof(u64)); | ||
| 311 | c.LDP(X23, X24, X1, HOST_REGS_OFF + 4 * sizeof(u64)); | ||
| 312 | c.LDP(X25, X26, X1, HOST_REGS_OFF + 6 * sizeof(u64)); | ||
| 313 | c.LDP(X27, X28, X1, HOST_REGS_OFF + 8 * sizeof(u64)); | ||
| 314 | c.LDP(X29, X30, X1, HOST_REGS_OFF + 10 * sizeof(u64)); | ||
| 315 | c.LDP(Q8, Q9, X1, HOST_VREGS_OFF); | ||
| 316 | c.LDP(Q10, Q11, X1, HOST_VREGS_OFF + 2 * sizeof(u128)); | ||
| 317 | c.LDP(Q12, Q13, X1, HOST_VREGS_OFF + 4 * sizeof(u128)); | ||
| 318 | c.LDP(Q14, Q15, X1, HOST_VREGS_OFF + 6 * sizeof(u128)); | ||
| 319 | c.RET(); | ||
| 320 | |||
| 321 | // Write the post-SVC trampoline address, which will jump back to the guest after restoring its | ||
| 322 | // state. | ||
| 323 | m_trampolines.push_back({c.offset(), module_dest}); | ||
| 324 | |||
| 325 | // Host called this location. Save the return address so we can | ||
| 326 | // unwind the stack properly when jumping back. | ||
| 327 | c.MRS(X2, oaknut::SystemReg::TPIDR_EL0); | ||
| 328 | c.LDR(X2, X2, offsetof(NativeExecutionParameters, native_context)); | ||
| 329 | c.ADD(X0, X2, offsetof(GuestContext, host_ctx)); | ||
| 330 | c.STR(X30, X0, offsetof(HostContext, host_saved_regs) + 11 * sizeof(u64)); | ||
| 331 | |||
| 332 | // Reload all guest registers except X30 and PC. | ||
| 333 | // The function also expects 16 bytes of stack already allocated. | ||
| 334 | c.STR(X30, SP, PRE_INDEXED, -16); | ||
| 335 | c.BL(m_load_context); | ||
| 336 | c.LDR(X30, SP, POST_INDEXED, 16); | ||
| 337 | |||
| 338 | // Use X1 as a scratch register to restore X30. | ||
| 339 | c.STR(X1, SP, PRE_INDEXED, -16); | ||
| 340 | c.MRS(X1, oaknut::SystemReg::TPIDR_EL0); | ||
| 341 | c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context)); | ||
| 342 | c.LDR(X30, X1, offsetof(GuestContext, cpu_registers) + sizeof(u64) * 30); | ||
| 343 | c.LDR(X1, SP, POST_INDEXED, 16); | ||
| 344 | |||
| 345 | // Unlock the context. | ||
| 346 | this->UnlockContext(); | ||
| 347 | |||
| 348 | // Jump back to the instruction after the emulated SVC. | ||
| 349 | this->BranchToModule(module_dest); | ||
| 350 | |||
| 351 | // Store PC after call. | ||
| 352 | c.l(pc_after_svc); | ||
| 353 | this->WriteModulePc(module_dest); | ||
| 354 | } | ||
| 355 | |||
| 356 | void Patcher::WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, | ||
| 357 | oaknut::SystemReg src_reg) { | ||
| 358 | // Retrieve emulated TLS register from GuestContext. | ||
| 359 | c.MRS(dest_reg, oaknut::SystemReg::TPIDR_EL0); | ||
| 360 | if (src_reg == oaknut::SystemReg::TPIDRRO_EL0) { | ||
| 361 | c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidrro_el0)); | ||
| 362 | } else { | ||
| 363 | c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidr_el0)); | ||
| 364 | } | ||
| 365 | |||
| 366 | // Jump back to the instruction after the emulated MRS. | ||
| 367 | this->BranchToModule(module_dest); | ||
| 368 | } | ||
| 369 | |||
| 370 | void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg) { | ||
| 371 | const auto scratch_reg = src_reg.index() == 0 ? X1 : X0; | ||
| 372 | c.STR(scratch_reg, SP, PRE_INDEXED, -16); | ||
| 373 | |||
| 374 | // Save guest value to NativeExecutionParameters::tpidr_el0. | ||
| 375 | c.MRS(scratch_reg, oaknut::SystemReg::TPIDR_EL0); | ||
| 376 | c.STR(src_reg, scratch_reg, offsetof(NativeExecutionParameters, tpidr_el0)); | ||
| 377 | |||
| 378 | // Restore scratch register. | ||
| 379 | c.LDR(scratch_reg, SP, POST_INDEXED, 16); | ||
| 380 | |||
| 381 | // Jump back to the instruction after the emulated MSR. | ||
| 382 | this->BranchToModule(module_dest); | ||
| 383 | } | ||
| 384 | |||
| 385 | void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) { | ||
| 386 | static Common::Arm64::NativeClock clock{}; | ||
| 387 | const auto factor = clock.GetGuestCNTFRQFactor(); | ||
| 388 | const auto raw_factor = Common::BitCast<std::array<u64, 2>>(factor); | ||
| 389 | |||
| 390 | const auto use_x2_x3 = dest_reg.index() == 0 || dest_reg.index() == 1; | ||
| 391 | oaknut::XReg scratch0 = use_x2_x3 ? X2 : X0; | ||
| 392 | oaknut::XReg scratch1 = use_x2_x3 ? X3 : X1; | ||
| 393 | |||
| 394 | oaknut::Label factorlo; | ||
| 395 | oaknut::Label factorhi; | ||
| 396 | |||
| 397 | // Save scratches. | ||
| 398 | c.STP(scratch0, scratch1, SP, PRE_INDEXED, -16); | ||
| 399 | |||
| 400 | // Load counter value. | ||
| 401 | c.MRS(dest_reg, oaknut::SystemReg::CNTVCT_EL0); | ||
| 402 | |||
| 403 | // Load scaling factor. | ||
| 404 | c.LDR(scratch0, factorlo); | ||
| 405 | c.LDR(scratch1, factorhi); | ||
| 406 | |||
| 407 | // Multiply low bits and get result. | ||
| 408 | c.UMULH(scratch0, dest_reg, scratch0); | ||
| 409 | |||
| 410 | // Multiply high bits and add low bit result. | ||
| 411 | c.MADD(dest_reg, dest_reg, scratch1, scratch0); | ||
| 412 | |||
| 413 | // Reload scratches. | ||
| 414 | c.LDP(scratch0, scratch1, SP, POST_INDEXED, 16); | ||
| 415 | |||
| 416 | // Jump back to the instruction after the emulated MRS. | ||
| 417 | this->BranchToModule(module_dest); | ||
| 418 | |||
| 419 | // Scaling factor constant values. | ||
| 420 | c.l(factorlo); | ||
| 421 | c.dx(raw_factor[0]); | ||
| 422 | c.l(factorhi); | ||
| 423 | c.dx(raw_factor[1]); | ||
| 424 | } | ||
| 425 | |||
| 426 | void Patcher::LockContext() { | ||
| 427 | oaknut::Label retry; | ||
| 428 | |||
| 429 | // Save scratches. | ||
| 430 | c.STP(X0, X1, SP, PRE_INDEXED, -16); | ||
| 431 | |||
| 432 | // Reload lock pointer. | ||
| 433 | c.l(retry); | ||
| 434 | c.CLREX(); | ||
| 435 | c.MRS(X0, oaknut::SystemReg::TPIDR_EL0); | ||
| 436 | c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock)); | ||
| 437 | |||
| 438 | static_assert(SpinLockLocked == 0); | ||
| 439 | |||
| 440 | // Load-linked with acquire ordering. | ||
| 441 | c.LDAXR(W1, X0); | ||
| 442 | |||
| 443 | // If the value was SpinLockLocked, clear monitor and retry. | ||
| 444 | c.CBZ(W1, retry); | ||
| 445 | |||
| 446 | // Store-conditional SpinLockLocked with relaxed ordering. | ||
| 447 | c.STXR(W1, WZR, X0); | ||
| 448 | |||
| 449 | // If we failed to store, retry. | ||
| 450 | c.CBNZ(W1, retry); | ||
| 451 | |||
| 452 | // We succeeded! Reload scratches. | ||
| 453 | c.LDP(X0, X1, SP, POST_INDEXED, 16); | ||
| 454 | } | ||
| 455 | |||
| 456 | void Patcher::UnlockContext() { | ||
| 457 | // Save scratches. | ||
| 458 | c.STP(X0, X1, SP, PRE_INDEXED, -16); | ||
| 459 | |||
| 460 | // Load lock pointer. | ||
| 461 | c.MRS(X0, oaknut::SystemReg::TPIDR_EL0); | ||
| 462 | c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock)); | ||
| 463 | |||
| 464 | // Load SpinLockUnlocked. | ||
| 465 | c.MOV(W1, SpinLockUnlocked); | ||
| 466 | |||
| 467 | // Store value with release ordering. | ||
| 468 | c.STLR(W1, X0); | ||
| 469 | |||
| 470 | // Load scratches. | ||
| 471 | c.LDP(X0, X1, SP, POST_INDEXED, 16); | ||
| 472 | } | ||
| 473 | |||
| 474 | } // namespace Core::NCE | ||
diff --git a/src/core/arm/nce/patcher.h b/src/core/arm/nce/patcher.h new file mode 100644 index 000000000..c6d1608c1 --- /dev/null +++ b/src/core/arm/nce/patcher.h | |||
| @@ -0,0 +1,98 @@ | |||
| 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 <unordered_map> | ||
| 8 | #include <vector> | ||
| 9 | #include <oaknut/code_block.hpp> | ||
| 10 | #include <oaknut/oaknut.hpp> | ||
| 11 | |||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "core/hle/kernel/code_set.h" | ||
| 14 | #include "core/hle/kernel/k_typed_address.h" | ||
| 15 | #include "core/hle/kernel/physical_memory.h" | ||
| 16 | |||
| 17 | namespace Core::NCE { | ||
| 18 | |||
| 19 | enum class PatchMode : u32 { | ||
| 20 | None, | ||
| 21 | PreText, ///< Patch section is inserted before .text | ||
| 22 | PostData, ///< Patch section is inserted after .data | ||
| 23 | }; | ||
| 24 | |||
| 25 | using ModuleTextAddress = u64; | ||
| 26 | using PatchTextAddress = u64; | ||
| 27 | using EntryTrampolines = std::unordered_map<ModuleTextAddress, PatchTextAddress>; | ||
| 28 | |||
| 29 | class Patcher { | ||
| 30 | public: | ||
| 31 | explicit Patcher(); | ||
| 32 | ~Patcher(); | ||
| 33 | |||
| 34 | void PatchText(const Kernel::PhysicalMemory& program_image, | ||
| 35 | const Kernel::CodeSet::Segment& code); | ||
| 36 | void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, | ||
| 37 | Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines); | ||
| 38 | size_t GetSectionSize() const noexcept; | ||
| 39 | |||
| 40 | [[nodiscard]] PatchMode GetPatchMode() const noexcept { | ||
| 41 | return mode; | ||
| 42 | } | ||
| 43 | |||
| 44 | private: | ||
| 45 | using ModuleDestLabel = uintptr_t; | ||
| 46 | |||
| 47 | struct Trampoline { | ||
| 48 | ptrdiff_t patch_offset; | ||
| 49 | uintptr_t module_offset; | ||
| 50 | }; | ||
| 51 | |||
| 52 | void WriteLoadContext(); | ||
| 53 | void WriteSaveContext(); | ||
| 54 | void LockContext(); | ||
| 55 | void UnlockContext(); | ||
| 56 | void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id); | ||
| 57 | void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, | ||
| 58 | oaknut::SystemReg src_reg); | ||
| 59 | void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg); | ||
| 60 | void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg); | ||
| 61 | |||
| 62 | private: | ||
| 63 | void BranchToPatch(uintptr_t module_dest) { | ||
| 64 | m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); | ||
| 65 | } | ||
| 66 | |||
| 67 | void BranchToModule(uintptr_t module_dest) { | ||
| 68 | m_branch_to_module_relocations.push_back({c.offset(), module_dest}); | ||
| 69 | c.dw(0); | ||
| 70 | } | ||
| 71 | |||
| 72 | void WriteModulePc(uintptr_t module_dest) { | ||
| 73 | m_write_module_pc_relocations.push_back({c.offset(), module_dest}); | ||
| 74 | c.dx(0); | ||
| 75 | } | ||
| 76 | |||
| 77 | private: | ||
| 78 | // List of patch instructions we have generated. | ||
| 79 | std::vector<u32> m_patch_instructions{}; | ||
| 80 | |||
| 81 | // Relocation type for relative branch from module to patch. | ||
| 82 | struct Relocation { | ||
| 83 | ptrdiff_t patch_offset; ///< Offset in bytes from the start of the patch section. | ||
| 84 | uintptr_t module_offset; ///< Offset in bytes from the start of the text section. | ||
| 85 | }; | ||
| 86 | |||
| 87 | oaknut::VectorCodeGenerator c; | ||
| 88 | std::vector<Trampoline> m_trampolines; | ||
| 89 | std::vector<Relocation> m_branch_to_patch_relocations{}; | ||
| 90 | std::vector<Relocation> m_branch_to_module_relocations{}; | ||
| 91 | std::vector<Relocation> m_write_module_pc_relocations{}; | ||
| 92 | std::vector<ModuleTextAddress> m_exclusives{}; | ||
| 93 | oaknut::Label m_save_context{}; | ||
| 94 | oaknut::Label m_load_context{}; | ||
| 95 | PatchMode mode{PatchMode::None}; | ||
| 96 | }; | ||
| 97 | |||
| 98 | } // namespace Core::NCE | ||
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 980bb97f9..151eb3870 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -211,6 +211,8 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) { | |||
| 211 | system.GPU().ObtainContext(); | 211 | system.GPU().ObtainContext(); |
| 212 | } | 212 | } |
| 213 | 213 | ||
| 214 | system.ArmInterface(core).Initialize(); | ||
| 215 | |||
| 214 | auto& kernel = system.Kernel(); | 216 | auto& kernel = system.Kernel(); |
| 215 | auto& scheduler = *kernel.CurrentScheduler(); | 217 | auto& scheduler = *kernel.CurrentScheduler(); |
| 216 | auto* thread = scheduler.GetSchedulerCurrentThread(); | 218 | auto* thread = scheduler.GetSchedulerCurrentThread(); |
diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp index de3f8ef8f..1aea56a99 100644 --- a/src/core/device_memory.cpp +++ b/src/core/device_memory.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | namespace Core { | 7 | namespace Core { |
| 8 | 8 | ||
| 9 | #ifdef ANDROID | 9 | #ifdef HAS_NCE |
| 10 | constexpr size_t VirtualReserveSize = 1ULL << 38; | 10 | constexpr size_t VirtualReserveSize = 1ULL << 38; |
| 11 | #else | 11 | #else |
| 12 | constexpr size_t VirtualReserveSize = 1ULL << 39; | 12 | constexpr size_t VirtualReserveSize = 1ULL << 39; |
| @@ -15,6 +15,7 @@ constexpr size_t VirtualReserveSize = 1ULL << 39; | |||
| 15 | DeviceMemory::DeviceMemory() | 15 | DeviceMemory::DeviceMemory() |
| 16 | : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(), | 16 | : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(), |
| 17 | VirtualReserveSize} {} | 17 | VirtualReserveSize} {} |
| 18 | |||
| 18 | DeviceMemory::~DeviceMemory() = default; | 19 | DeviceMemory::~DeviceMemory() = default; |
| 19 | 20 | ||
| 20 | } // namespace Core | 21 | } // namespace Core |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 0bca05587..cc7af2ea3 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -429,10 +429,6 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs | |||
| 429 | LOG_DEBUG(Loader, "{}", log_string); | 429 | LOG_DEBUG(Loader, "{}", log_string); |
| 430 | } | 430 | } |
| 431 | 431 | ||
| 432 | if (base_romfs == nullptr) { | ||
| 433 | return base_romfs; | ||
| 434 | } | ||
| 435 | |||
| 436 | auto romfs = base_romfs; | 432 | auto romfs = base_romfs; |
| 437 | 433 | ||
| 438 | // Game Updates | 434 | // Game Updates |
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 1eb1f439a..6de2103a0 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <memory> | 4 | #include <memory> |
| 5 | 5 | ||
| 6 | #include "common/assert.h" | ||
| 6 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 7 | #include "common/string_util.h" | 8 | #include "common/string_util.h" |
| 8 | #include "common/swap.h" | 9 | #include "common/swap.h" |
| @@ -101,24 +102,30 @@ void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size | |||
| 101 | } // Anonymous namespace | 102 | } // Anonymous namespace |
| 102 | 103 | ||
| 103 | VirtualDir ExtractRomFS(VirtualFile file) { | 104 | VirtualDir ExtractRomFS(VirtualFile file) { |
| 105 | auto root_container = std::make_shared<VectorVfsDirectory>(); | ||
| 106 | if (!file) { | ||
| 107 | return root_container; | ||
| 108 | } | ||
| 109 | |||
| 104 | RomFSHeader header{}; | 110 | RomFSHeader header{}; |
| 105 | if (file->ReadObject(&header) != sizeof(RomFSHeader)) | 111 | if (file->ReadObject(&header) != sizeof(RomFSHeader)) { |
| 106 | return nullptr; | 112 | return root_container; |
| 113 | } | ||
| 107 | 114 | ||
| 108 | if (header.header_size != sizeof(RomFSHeader)) | 115 | if (header.header_size != sizeof(RomFSHeader)) { |
| 109 | return nullptr; | 116 | return root_container; |
| 117 | } | ||
| 110 | 118 | ||
| 111 | const u64 file_offset = header.file_meta.offset; | 119 | const u64 file_offset = header.file_meta.offset; |
| 112 | const u64 dir_offset = header.directory_meta.offset; | 120 | const u64 dir_offset = header.directory_meta.offset; |
| 113 | 121 | ||
| 114 | auto root_container = std::make_shared<VectorVfsDirectory>(); | ||
| 115 | |||
| 116 | ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container); | 122 | ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container); |
| 117 | 123 | ||
| 118 | if (auto root = root_container->GetSubdirectory(""); root) { | 124 | if (auto root = root_container->GetSubdirectory(""); root) { |
| 119 | return std::make_shared<CachedVfsDirectory>(std::move(root)); | 125 | return std::make_shared<CachedVfsDirectory>(std::move(root)); |
| 120 | } | 126 | } |
| 121 | 127 | ||
| 128 | ASSERT(false); | ||
| 122 | return nullptr; | 129 | return nullptr; |
| 123 | } | 130 | } |
| 124 | 131 | ||
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 1bc07dae5..35e149905 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -22,7 +22,7 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provi | |||
| 22 | : content_provider{provider}, filesystem_controller{controller} { | 22 | : content_provider{provider}, filesystem_controller{controller} { |
| 23 | // Load the RomFS from the app | 23 | // Load the RomFS from the app |
| 24 | if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { | 24 | if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { |
| 25 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); | 25 | LOG_WARNING(Service_FS, "Unable to read base RomFS"); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | updatable = app_loader.IsRomFSUpdatable(); | 28 | updatable = app_loader.IsRomFSUpdatable(); |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index a72df034e..c7b48a58d 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -167,6 +167,11 @@ protected: | |||
| 167 | */ | 167 | */ |
| 168 | std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const; | 168 | std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const; |
| 169 | 169 | ||
| 170 | /** | ||
| 171 | * Clip the provided coordinates to be inside the touchscreen area. | ||
| 172 | */ | ||
| 173 | std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const; | ||
| 174 | |||
| 170 | WindowSystemInfo window_info; | 175 | WindowSystemInfo window_info; |
| 171 | 176 | ||
| 172 | bool strict_context_required = false; | 177 | bool strict_context_required = false; |
| @@ -181,11 +186,6 @@ private: | |||
| 181 | // By default, ignore this request and do nothing. | 186 | // By default, ignore this request and do nothing. |
| 182 | } | 187 | } |
| 183 | 188 | ||
| 184 | /** | ||
| 185 | * Clip the provided coordinates to be inside the touchscreen area. | ||
| 186 | */ | ||
| 187 | std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const; | ||
| 188 | |||
| 189 | Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout | 189 | Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout |
| 190 | 190 | ||
| 191 | u32 client_area_width; ///< Current client width, should be set by window impl. | 191 | u32 client_area_width; ///< Current client width, should be set by window impl. |
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 34927cddd..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) { |
| @@ -118,7 +119,7 @@ void EmulatedController::ReloadFromSettings() { | |||
| 118 | } | 119 | } |
| 119 | 120 | ||
| 120 | void EmulatedController::ReloadColorsFromSettings() { | 121 | void EmulatedController::ReloadColorsFromSettings() { |
| 121 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 122 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 122 | const auto& player = Settings::values.players.GetValue()[player_index]; | 123 | const auto& player = Settings::values.players.GetValue()[player_index]; |
| 123 | 124 | ||
| 124 | // Avoid updating colors if overridden by physical controller | 125 | // Avoid updating colors if overridden by physical controller |
| @@ -215,7 +216,7 @@ void EmulatedController::LoadDevices() { | |||
| 215 | } | 216 | } |
| 216 | 217 | ||
| 217 | void EmulatedController::LoadTASParams() { | 218 | void EmulatedController::LoadTASParams() { |
| 218 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 219 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 219 | Common::ParamPackage common_params{}; | 220 | Common::ParamPackage common_params{}; |
| 220 | common_params.Set("engine", "tas"); | 221 | common_params.Set("engine", "tas"); |
| 221 | common_params.Set("port", static_cast<int>(player_index)); | 222 | common_params.Set("port", static_cast<int>(player_index)); |
| @@ -264,7 +265,7 @@ void EmulatedController::LoadTASParams() { | |||
| 264 | } | 265 | } |
| 265 | 266 | ||
| 266 | void EmulatedController::LoadVirtualGamepadParams() { | 267 | void EmulatedController::LoadVirtualGamepadParams() { |
| 267 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 268 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 268 | Common::ParamPackage common_params{}; | 269 | Common::ParamPackage common_params{}; |
| 269 | common_params.Set("engine", "virtual_gamepad"); | 270 | common_params.Set("engine", "virtual_gamepad"); |
| 270 | common_params.Set("port", static_cast<int>(player_index)); | 271 | common_params.Set("port", static_cast<int>(player_index)); |
| @@ -508,9 +509,11 @@ void EmulatedController::ReloadInput() { | |||
| 508 | }); | 509 | }); |
| 509 | } | 510 | } |
| 510 | turbo_button_state = 0; | 511 | turbo_button_state = 0; |
| 512 | is_initalized = true; | ||
| 511 | } | 513 | } |
| 512 | 514 | ||
| 513 | void EmulatedController::UnloadInput() { | 515 | void EmulatedController::UnloadInput() { |
| 516 | is_initalized = false; | ||
| 514 | for (auto& button : button_devices) { | 517 | for (auto& button : button_devices) { |
| 515 | button.reset(); | 518 | button.reset(); |
| 516 | } | 519 | } |
| @@ -615,7 +618,7 @@ bool EmulatedController::IsConfiguring() const { | |||
| 615 | } | 618 | } |
| 616 | 619 | ||
| 617 | void EmulatedController::SaveCurrentConfig() { | 620 | void EmulatedController::SaveCurrentConfig() { |
| 618 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 621 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 619 | auto& player = Settings::values.players.GetValue()[player_index]; | 622 | auto& player = Settings::values.players.GetValue()[player_index]; |
| 620 | player.connected = is_connected; | 623 | player.connected = is_connected; |
| 621 | player.controller_type = MapNPadToSettingsType(npad_type); | 624 | player.controller_type = MapNPadToSettingsType(npad_type); |
| @@ -1206,13 +1209,16 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { | |||
| 1206 | } | 1209 | } |
| 1207 | 1210 | ||
| 1208 | 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 | } | ||
| 1209 | if (device_index >= output_devices.size()) { | 1215 | if (device_index >= output_devices.size()) { |
| 1210 | return false; | 1216 | return false; |
| 1211 | } | 1217 | } |
| 1212 | if (!output_devices[device_index]) { | 1218 | if (!output_devices[device_index]) { |
| 1213 | return false; | 1219 | return false; |
| 1214 | } | 1220 | } |
| 1215 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 1221 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 1216 | const auto& player = Settings::values.players.GetValue()[player_index]; | 1222 | const auto& player = Settings::values.players.GetValue()[player_index]; |
| 1217 | const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; | 1223 | const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; |
| 1218 | 1224 | ||
| @@ -1238,9 +1244,13 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v | |||
| 1238 | } | 1244 | } |
| 1239 | 1245 | ||
| 1240 | bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { | 1246 | bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { |
| 1241 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | 1247 | const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); |
| 1242 | const auto& player = Settings::values.players.GetValue()[player_index]; | 1248 | const auto& player = Settings::values.players.GetValue()[player_index]; |
| 1243 | 1249 | ||
| 1250 | if (!is_initalized) { | ||
| 1251 | return false; | ||
| 1252 | } | ||
| 1253 | |||
| 1244 | if (!player.vibration_enabled) { | 1254 | if (!player.vibration_enabled) { |
| 1245 | return false; | 1255 | return false; |
| 1246 | } | 1256 | } |
| @@ -1260,6 +1270,10 @@ Common::Input::DriverResult EmulatedController::SetPollingMode( | |||
| 1260 | EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { | 1270 | EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { |
| 1261 | 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); |
| 1262 | 1272 | ||
| 1273 | if (!is_initalized) { | ||
| 1274 | return Common::Input::DriverResult::InvalidHandle; | ||
| 1275 | } | ||
| 1276 | |||
| 1263 | 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)]; |
| 1264 | 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)]; |
| 1265 | auto& nfc_output_device = output_devices[3]; | 1279 | auto& nfc_output_device = output_devices[3]; |
| @@ -1305,6 +1319,10 @@ bool EmulatedController::SetCameraFormat( | |||
| 1305 | Core::IrSensor::ImageTransferProcessorFormat camera_format) { | 1319 | Core::IrSensor::ImageTransferProcessorFormat camera_format) { |
| 1306 | LOG_INFO(Service_HID, "Set camera format {}", camera_format); | 1320 | LOG_INFO(Service_HID, "Set camera format {}", camera_format); |
| 1307 | 1321 | ||
| 1322 | if (!is_initalized) { | ||
| 1323 | return false; | ||
| 1324 | } | ||
| 1325 | |||
| 1308 | 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)]; |
| 1309 | auto& camera_output_device = output_devices[2]; | 1327 | auto& camera_output_device = output_devices[2]; |
| 1310 | 1328 | ||
| @@ -1328,6 +1346,11 @@ void EmulatedController::SetRingParam(Common::ParamPackage param) { | |||
| 1328 | } | 1346 | } |
| 1329 | 1347 | ||
| 1330 | bool EmulatedController::HasNfc() const { | 1348 | bool EmulatedController::HasNfc() const { |
| 1349 | |||
| 1350 | if (!is_initalized) { | ||
| 1351 | return false; | ||
| 1352 | } | ||
| 1353 | |||
| 1331 | const auto& nfc_output_device = output_devices[3]; | 1354 | const auto& nfc_output_device = output_devices[3]; |
| 1332 | 1355 | ||
| 1333 | switch (npad_type) { | 1356 | switch (npad_type) { |
| @@ -1365,6 +1388,10 @@ bool EmulatedController::RemoveNfcHandle() { | |||
| 1365 | } | 1388 | } |
| 1366 | 1389 | ||
| 1367 | bool EmulatedController::StartNfcPolling() { | 1390 | bool EmulatedController::StartNfcPolling() { |
| 1391 | if (!is_initalized) { | ||
| 1392 | return false; | ||
| 1393 | } | ||
| 1394 | |||
| 1368 | 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)]; |
| 1369 | auto& nfc_virtual_output_device = output_devices[3]; | 1396 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1370 | 1397 | ||
| @@ -1376,6 +1403,10 @@ bool EmulatedController::StartNfcPolling() { | |||
| 1376 | } | 1403 | } |
| 1377 | 1404 | ||
| 1378 | bool EmulatedController::StopNfcPolling() { | 1405 | bool EmulatedController::StopNfcPolling() { |
| 1406 | if (!is_initalized) { | ||
| 1407 | return false; | ||
| 1408 | } | ||
| 1409 | |||
| 1379 | 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)]; |
| 1380 | auto& nfc_virtual_output_device = output_devices[3]; | 1411 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1381 | 1412 | ||
| @@ -1387,6 +1418,10 @@ bool EmulatedController::StopNfcPolling() { | |||
| 1387 | } | 1418 | } |
| 1388 | 1419 | ||
| 1389 | bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { | 1420 | bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { |
| 1421 | if (!is_initalized) { | ||
| 1422 | return false; | ||
| 1423 | } | ||
| 1424 | |||
| 1390 | 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)]; |
| 1391 | auto& nfc_virtual_output_device = output_devices[3]; | 1426 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1392 | 1427 | ||
| @@ -1399,6 +1434,10 @@ bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { | |||
| 1399 | 1434 | ||
| 1400 | bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, | 1435 | bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, |
| 1401 | Common::Input::MifareRequest& out_data) { | 1436 | Common::Input::MifareRequest& out_data) { |
| 1437 | if (!is_initalized) { | ||
| 1438 | return false; | ||
| 1439 | } | ||
| 1440 | |||
| 1402 | 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)]; |
| 1403 | auto& nfc_virtual_output_device = output_devices[3]; | 1442 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1404 | 1443 | ||
| @@ -1411,6 +1450,10 @@ bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& requ | |||
| 1411 | } | 1450 | } |
| 1412 | 1451 | ||
| 1413 | 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 | |||
| 1414 | 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)]; |
| 1415 | auto& nfc_virtual_output_device = output_devices[3]; | 1458 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1416 | 1459 | ||
| @@ -1422,6 +1465,10 @@ bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& req | |||
| 1422 | } | 1465 | } |
| 1423 | 1466 | ||
| 1424 | 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 | |||
| 1425 | 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)]; |
| 1426 | auto& nfc_virtual_output_device = output_devices[3]; | 1473 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1427 | 1474 | ||
| @@ -1433,6 +1480,10 @@ bool EmulatedController::WriteNfc(const std::vector<u8>& data) { | |||
| 1433 | } | 1480 | } |
| 1434 | 1481 | ||
| 1435 | void EmulatedController::SetLedPattern() { | 1482 | void EmulatedController::SetLedPattern() { |
| 1483 | if (!is_initalized) { | ||
| 1484 | return; | ||
| 1485 | } | ||
| 1486 | |||
| 1436 | for (auto& device : output_devices) { | 1487 | for (auto& device : output_devices) { |
| 1437 | if (!device) { | 1488 | if (!device) { |
| 1438 | continue; | 1489 | continue; |
| @@ -1648,7 +1699,7 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { | |||
| 1648 | } | 1699 | } |
| 1649 | if (is_connected) { | 1700 | if (is_connected) { |
| 1650 | LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", | 1701 | LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", |
| 1651 | NpadIdTypeToIndex(npad_id_type)); | 1702 | Service::HID::NpadIdTypeToIndex(npad_id_type)); |
| 1652 | } | 1703 | } |
| 1653 | npad_type = npad_type_; | 1704 | npad_type = npad_type_; |
| 1654 | } | 1705 | } |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index ea18c2343..d6e20ab66 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -559,6 +559,7 @@ private: | |||
| 559 | NpadStyleTag supported_style_tag{NpadStyleSet::All}; | 559 | NpadStyleTag supported_style_tag{NpadStyleSet::All}; |
| 560 | bool is_connected{false}; | 560 | bool is_connected{false}; |
| 561 | bool is_configuring{false}; | 561 | bool is_configuring{false}; |
| 562 | bool is_initalized{false}; | ||
| 562 | bool system_buttons_enabled{true}; | 563 | bool system_buttons_enabled{true}; |
| 563 | f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; | 564 | f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; |
| 564 | 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 9d48cd90e..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, |
| @@ -591,6 +599,29 @@ struct SixAxisSensorIcInformation { | |||
| 591 | static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, | 599 | static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, |
| 592 | "SixAxisSensorIcInformation is an invalid size"); | 600 | "SixAxisSensorIcInformation is an invalid size"); |
| 593 | 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 | |||
| 594 | // This is nn::hid::VibrationDeviceHandle | 625 | // This is nn::hid::VibrationDeviceHandle |
| 595 | struct VibrationDeviceHandle { | 626 | struct VibrationDeviceHandle { |
| 596 | NpadStyleIndex npad_type{NpadStyleIndex::None}; | 627 | NpadStyleIndex npad_type{NpadStyleIndex::None}; |
| @@ -701,60 +732,4 @@ struct UniquePadId { | |||
| 701 | }; | 732 | }; |
| 702 | static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); | 733 | static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); |
| 703 | 734 | ||
| 704 | /// Converts a NpadIdType to an array index. | ||
| 705 | constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) { | ||
| 706 | switch (npad_id_type) { | ||
| 707 | case NpadIdType::Player1: | ||
| 708 | return 0; | ||
| 709 | case NpadIdType::Player2: | ||
| 710 | return 1; | ||
| 711 | case NpadIdType::Player3: | ||
| 712 | return 2; | ||
| 713 | case NpadIdType::Player4: | ||
| 714 | return 3; | ||
| 715 | case NpadIdType::Player5: | ||
| 716 | return 4; | ||
| 717 | case NpadIdType::Player6: | ||
| 718 | return 5; | ||
| 719 | case NpadIdType::Player7: | ||
| 720 | return 6; | ||
| 721 | case NpadIdType::Player8: | ||
| 722 | return 7; | ||
| 723 | case NpadIdType::Handheld: | ||
| 724 | return 8; | ||
| 725 | case NpadIdType::Other: | ||
| 726 | return 9; | ||
| 727 | default: | ||
| 728 | return 0; | ||
| 729 | } | ||
| 730 | } | ||
| 731 | |||
| 732 | /// Converts an array index to a NpadIdType | ||
| 733 | constexpr NpadIdType IndexToNpadIdType(size_t index) { | ||
| 734 | switch (index) { | ||
| 735 | case 0: | ||
| 736 | return NpadIdType::Player1; | ||
| 737 | case 1: | ||
| 738 | return NpadIdType::Player2; | ||
| 739 | case 2: | ||
| 740 | return NpadIdType::Player3; | ||
| 741 | case 3: | ||
| 742 | return NpadIdType::Player4; | ||
| 743 | case 4: | ||
| 744 | return NpadIdType::Player5; | ||
| 745 | case 5: | ||
| 746 | return NpadIdType::Player6; | ||
| 747 | case 6: | ||
| 748 | return NpadIdType::Player7; | ||
| 749 | case 7: | ||
| 750 | return NpadIdType::Player8; | ||
| 751 | case 8: | ||
| 752 | return NpadIdType::Handheld; | ||
| 753 | case 9: | ||
| 754 | return NpadIdType::Other; | ||
| 755 | default: | ||
| 756 | return NpadIdType::Invalid; | ||
| 757 | } | ||
| 758 | } | ||
| 759 | |||
| 760 | } // namespace Core::HID | 735 | } // namespace Core::HID |
diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp index 11359f318..a6bdd28f2 100644 --- a/src/core/hid/input_interpreter.cpp +++ b/src/core/hid/input_interpreter.cpp | |||
| @@ -13,14 +13,14 @@ InputInterpreter::InputInterpreter(Core::System& system) | |||
| 13 | : npad{system.ServiceManager() | 13 | : npad{system.ServiceManager() |
| 14 | .GetService<Service::HID::IHidServer>("hid") | 14 | .GetService<Service::HID::IHidServer>("hid") |
| 15 | ->GetResourceManager() | 15 | ->GetResourceManager() |
| 16 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} { | 16 | ->GetNpad()} { |
| 17 | ResetButtonStates(); | 17 | ResetButtonStates(); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | InputInterpreter::~InputInterpreter() = default; | 20 | InputInterpreter::~InputInterpreter() = default; |
| 21 | 21 | ||
| 22 | void InputInterpreter::PollInput() { | 22 | void InputInterpreter::PollInput() { |
| 23 | const auto button_state = npad.GetAndResetPressState(); | 23 | const auto button_state = npad->GetAndResetPressState(); |
| 24 | 24 | ||
| 25 | previous_index = current_index; | 25 | previous_index = current_index; |
| 26 | 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/code_set.h b/src/core/hle/kernel/code_set.h index af1af2b78..4d2d0098e 100644 --- a/src/core/hle/kernel/code_set.h +++ b/src/core/hle/kernel/code_set.h | |||
| @@ -75,12 +75,26 @@ struct CodeSet final { | |||
| 75 | return segments[2]; | 75 | return segments[2]; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | #ifdef HAS_NCE | ||
| 79 | Segment& PatchSegment() { | ||
| 80 | return patch_segment; | ||
| 81 | } | ||
| 82 | |||
| 83 | const Segment& PatchSegment() const { | ||
| 84 | return patch_segment; | ||
| 85 | } | ||
| 86 | #endif | ||
| 87 | |||
| 78 | /// The overall data that backs this code set. | 88 | /// The overall data that backs this code set. |
| 79 | Kernel::PhysicalMemory memory; | 89 | Kernel::PhysicalMemory memory; |
| 80 | 90 | ||
| 81 | /// The segments that comprise this code set. | 91 | /// The segments that comprise this code set. |
| 82 | std::array<Segment, 3> segments; | 92 | std::array<Segment, 3> segments; |
| 83 | 93 | ||
| 94 | #ifdef HAS_NCE | ||
| 95 | Segment patch_segment; | ||
| 96 | #endif | ||
| 97 | |||
| 84 | /// The entry point address for this code set. | 98 | /// The entry point address for this code set. |
| 85 | KProcessAddress entrypoint = 0; | 99 | KProcessAddress entrypoint = 0; |
| 86 | }; | 100 | }; |
diff --git a/src/core/hle/kernel/k_address_space_info.cpp b/src/core/hle/kernel/k_address_space_info.cpp index 32173e52b..23258071e 100644 --- a/src/core/hle/kernel/k_address_space_info.cpp +++ b/src/core/hle/kernel/k_address_space_info.cpp | |||
| @@ -25,8 +25,8 @@ constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{ | |||
| 25 | { .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, }, | 25 | { .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, }, |
| 26 | { .bit_width = 36, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, }, | 26 | { .bit_width = 36, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, }, |
| 27 | { .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, }, | 27 | { .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, }, |
| 28 | #ifdef ANDROID | 28 | #ifdef HAS_NCE |
| 29 | // With Android, we use a 38-bit address space due to memory limitations. This should (safely) truncate ASLR region. | 29 | // With NCE, we use a 38-bit address space due to memory limitations. This should (safely) truncate ASLR region. |
| 30 | { .bit_width = 39, .address = 128_MiB , .size = 256_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, }, | 30 | { .bit_width = 39, .address = 128_MiB , .size = 256_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, }, |
| 31 | #else | 31 | #else |
| 32 | { .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, }, | 32 | { .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, }, |
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index 47dc8fd35..6691586ed 100644 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp | |||
| @@ -88,6 +88,22 @@ Result FlushDataCache(AddressType addr, u64 size) { | |||
| 88 | R_SUCCEED(); | 88 | R_SUCCEED(); |
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | constexpr Common::MemoryPermission ConvertToMemoryPermission(KMemoryPermission perm) { | ||
| 92 | Common::MemoryPermission perms{}; | ||
| 93 | if (True(perm & KMemoryPermission::UserRead)) { | ||
| 94 | perms |= Common::MemoryPermission::Read; | ||
| 95 | } | ||
| 96 | if (True(perm & KMemoryPermission::UserWrite)) { | ||
| 97 | perms |= Common::MemoryPermission::Write; | ||
| 98 | } | ||
| 99 | #ifdef HAS_NCE | ||
| 100 | if (True(perm & KMemoryPermission::UserExecute)) { | ||
| 101 | perms |= Common::MemoryPermission::Execute; | ||
| 102 | } | ||
| 103 | #endif | ||
| 104 | return perms; | ||
| 105 | } | ||
| 106 | |||
| 91 | } // namespace | 107 | } // namespace |
| 92 | 108 | ||
| 93 | void KPageTableBase::MemoryRange::Open() { | 109 | void KPageTableBase::MemoryRange::Open() { |
| @@ -170,7 +186,8 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool | |||
| 170 | KMemoryManager::Pool pool, KProcessAddress code_address, | 186 | KMemoryManager::Pool pool, KProcessAddress code_address, |
| 171 | size_t code_size, KSystemResource* system_resource, | 187 | size_t code_size, KSystemResource* system_resource, |
| 172 | KResourceLimit* resource_limit, | 188 | KResourceLimit* resource_limit, |
| 173 | Core::Memory::Memory& memory) { | 189 | Core::Memory::Memory& memory, |
| 190 | KProcessAddress aslr_space_start) { | ||
| 174 | // Calculate region extents. | 191 | // Calculate region extents. |
| 175 | const size_t as_width = GetAddressSpaceWidth(as_type); | 192 | const size_t as_width = GetAddressSpaceWidth(as_type); |
| 176 | const KProcessAddress start = 0; | 193 | const KProcessAddress start = 0; |
| @@ -211,7 +228,8 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool | |||
| 211 | heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap); | 228 | heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap); |
| 212 | stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack); | 229 | stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack); |
| 213 | kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); | 230 | kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); |
| 214 | m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit); | 231 | m_code_region_start = m_address_space_start + aslr_space_start + |
| 232 | GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit); | ||
| 215 | m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit); | 233 | m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit); |
| 216 | m_alias_code_region_start = m_code_region_start; | 234 | m_alias_code_region_start = m_code_region_start; |
| 217 | m_alias_code_region_end = m_code_region_end; | 235 | m_alias_code_region_end = m_code_region_end; |
| @@ -5643,7 +5661,8 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a | |||
| 5643 | case OperationType::Map: { | 5661 | case OperationType::Map: { |
| 5644 | ASSERT(virt_addr != 0); | 5662 | ASSERT(virt_addr != 0); |
| 5645 | ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); | 5663 | ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); |
| 5646 | m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr); | 5664 | m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr, |
| 5665 | ConvertToMemoryPermission(properties.perm)); | ||
| 5647 | 5666 | ||
| 5648 | // Open references to pages, if we should. | 5667 | // Open references to pages, if we should. |
| 5649 | if (this->IsHeapPhysicalAddress(phys_addr)) { | 5668 | if (this->IsHeapPhysicalAddress(phys_addr)) { |
| @@ -5658,8 +5677,11 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a | |||
| 5658 | } | 5677 | } |
| 5659 | case OperationType::ChangePermissions: | 5678 | case OperationType::ChangePermissions: |
| 5660 | case OperationType::ChangePermissionsAndRefresh: | 5679 | case OperationType::ChangePermissionsAndRefresh: |
| 5661 | case OperationType::ChangePermissionsAndRefreshAndFlush: | 5680 | case OperationType::ChangePermissionsAndRefreshAndFlush: { |
| 5681 | m_memory->ProtectRegion(*m_impl, virt_addr, num_pages * PageSize, | ||
| 5682 | ConvertToMemoryPermission(properties.perm)); | ||
| 5662 | R_SUCCEED(); | 5683 | R_SUCCEED(); |
| 5684 | } | ||
| 5663 | default: | 5685 | default: |
| 5664 | UNREACHABLE(); | 5686 | UNREACHABLE(); |
| 5665 | } | 5687 | } |
| @@ -5687,7 +5709,8 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a | |||
| 5687 | const size_t size{node.GetNumPages() * PageSize}; | 5709 | const size_t size{node.GetNumPages() * PageSize}; |
| 5688 | 5710 | ||
| 5689 | // Map the pages. | 5711 | // Map the pages. |
| 5690 | m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress()); | 5712 | m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(), |
| 5713 | ConvertToMemoryPermission(properties.perm)); | ||
| 5691 | 5714 | ||
| 5692 | virt_addr += size; | 5715 | virt_addr += size; |
| 5693 | } | 5716 | } |
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h index ee2c41e67..556d230b3 100644 --- a/src/core/hle/kernel/k_page_table_base.h +++ b/src/core/hle/kernel/k_page_table_base.h | |||
| @@ -235,7 +235,8 @@ public: | |||
| 235 | bool enable_device_address_space_merge, bool from_back, | 235 | bool enable_device_address_space_merge, bool from_back, |
| 236 | KMemoryManager::Pool pool, KProcessAddress code_address, | 236 | KMemoryManager::Pool pool, KProcessAddress code_address, |
| 237 | size_t code_size, KSystemResource* system_resource, | 237 | size_t code_size, KSystemResource* system_resource, |
| 238 | KResourceLimit* resource_limit, Core::Memory::Memory& memory); | 238 | KResourceLimit* resource_limit, Core::Memory::Memory& memory, |
| 239 | KProcessAddress aslr_space_start); | ||
| 239 | 240 | ||
| 240 | void Finalize(); | 241 | void Finalize(); |
| 241 | 242 | ||
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 3cfb414e5..6c29eb72c 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -300,7 +300,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa | |||
| 300 | False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); | 300 | False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); |
| 301 | R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, | 301 | R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, |
| 302 | params.code_address, params.code_num_pages * PageSize, | 302 | params.code_address, params.code_num_pages * PageSize, |
| 303 | m_system_resource, res_limit, this->GetMemory())); | 303 | m_system_resource, res_limit, this->GetMemory(), 0)); |
| 304 | } | 304 | } |
| 305 | ON_RESULT_FAILURE_2 { | 305 | ON_RESULT_FAILURE_2 { |
| 306 | m_page_table.Finalize(); | 306 | m_page_table.Finalize(); |
| @@ -332,7 +332,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa | |||
| 332 | 332 | ||
| 333 | Result KProcess::Initialize(const Svc::CreateProcessParameter& params, | 333 | Result KProcess::Initialize(const Svc::CreateProcessParameter& params, |
| 334 | std::span<const u32> user_caps, KResourceLimit* res_limit, | 334 | std::span<const u32> user_caps, KResourceLimit* res_limit, |
| 335 | KMemoryManager::Pool pool) { | 335 | KMemoryManager::Pool pool, KProcessAddress aslr_space_start) { |
| 336 | ASSERT(res_limit != nullptr); | 336 | ASSERT(res_limit != nullptr); |
| 337 | 337 | ||
| 338 | // Set members. | 338 | // Set members. |
| @@ -393,7 +393,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, | |||
| 393 | False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); | 393 | False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); |
| 394 | R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, | 394 | R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, |
| 395 | params.code_address, code_size, m_system_resource, res_limit, | 395 | params.code_address, code_size, m_system_resource, res_limit, |
| 396 | this->GetMemory())); | 396 | this->GetMemory(), aslr_space_start)); |
| 397 | } | 397 | } |
| 398 | ON_RESULT_FAILURE_2 { | 398 | ON_RESULT_FAILURE_2 { |
| 399 | m_page_table.Finalize(); | 399 | m_page_table.Finalize(); |
| @@ -1128,7 +1128,7 @@ KProcess::KProcess(KernelCore& 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, |
| 1131 | bool is_hbl) { | 1131 | KProcessAddress aslr_space_start, bool is_hbl) { |
| 1132 | // Create a resource limit for the process. | 1132 | // Create a resource limit for the process. |
| 1133 | const auto physical_memory_size = | 1133 | const auto physical_memory_size = |
| 1134 | m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); | 1134 | m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); |
| @@ -1179,7 +1179,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: | |||
| 1179 | .name = {}, | 1179 | .name = {}, |
| 1180 | .version = {}, | 1180 | .version = {}, |
| 1181 | .program_id = metadata.GetTitleID(), | 1181 | .program_id = metadata.GetTitleID(), |
| 1182 | .code_address = code_address, | 1182 | .code_address = code_address + GetInteger(aslr_space_start), |
| 1183 | .code_num_pages = static_cast<s32>(code_size / PageSize), | 1183 | .code_num_pages = static_cast<s32>(code_size / PageSize), |
| 1184 | .flags = flag, | 1184 | .flags = flag, |
| 1185 | .reslimit = Svc::InvalidHandle, | 1185 | .reslimit = Svc::InvalidHandle, |
| @@ -1193,7 +1193,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: | |||
| 1193 | 1193 | ||
| 1194 | // Initialize for application process. | 1194 | // Initialize for application process. |
| 1195 | R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, | 1195 | R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, |
| 1196 | KMemoryManager::Pool::Application)); | 1196 | KMemoryManager::Pool::Application, aslr_space_start)); |
| 1197 | 1197 | ||
| 1198 | // Assign remaining properties. | 1198 | // Assign remaining properties. |
| 1199 | m_is_hbl = is_hbl; | 1199 | m_is_hbl = is_hbl; |
| @@ -1214,6 +1214,17 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { | |||
| 1214 | ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute); | 1214 | ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute); |
| 1215 | ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read); | 1215 | ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read); |
| 1216 | ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); | 1216 | ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); |
| 1217 | |||
| 1218 | #ifdef HAS_NCE | ||
| 1219 | if (Settings::IsNceEnabled()) { | ||
| 1220 | auto& buffer = m_kernel.System().DeviceMemory().buffer; | ||
| 1221 | const auto& code = code_set.CodeSegment(); | ||
| 1222 | const auto& patch = code_set.PatchSegment(); | ||
| 1223 | buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true); | ||
| 1224 | buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true); | ||
| 1225 | ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None); | ||
| 1226 | } | ||
| 1227 | #endif | ||
| 1217 | } | 1228 | } |
| 1218 | 1229 | ||
| 1219 | bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) { | 1230 | bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) { |
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 8339465fd..d8cd0fdde 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h | |||
| @@ -120,6 +120,9 @@ private: | |||
| 120 | std::atomic<s64> m_num_ipc_messages{}; | 120 | std::atomic<s64> m_num_ipc_messages{}; |
| 121 | std::atomic<s64> m_num_ipc_replies{}; | 121 | std::atomic<s64> m_num_ipc_replies{}; |
| 122 | std::atomic<s64> m_num_ipc_receives{}; | 122 | std::atomic<s64> m_num_ipc_receives{}; |
| 123 | #ifdef HAS_NCE | ||
| 124 | std::unordered_map<u64, u64> m_post_handlers{}; | ||
| 125 | #endif | ||
| 123 | 126 | ||
| 124 | private: | 127 | private: |
| 125 | Result StartTermination(); | 128 | Result StartTermination(); |
| @@ -150,7 +153,8 @@ public: | |||
| 150 | std::span<const u32> caps, KResourceLimit* res_limit, | 153 | std::span<const u32> caps, KResourceLimit* res_limit, |
| 151 | KMemoryManager::Pool pool, bool immortal); | 154 | KMemoryManager::Pool pool, bool immortal); |
| 152 | Result Initialize(const Svc::CreateProcessParameter& params, std::span<const u32> user_caps, | 155 | Result Initialize(const Svc::CreateProcessParameter& params, std::span<const u32> user_caps, |
| 153 | KResourceLimit* res_limit, KMemoryManager::Pool pool); | 156 | KResourceLimit* res_limit, KMemoryManager::Pool pool, |
| 157 | KProcessAddress aslr_space_start); | ||
| 154 | void Exit(); | 158 | void Exit(); |
| 155 | 159 | ||
| 156 | const char* GetName() const { | 160 | const char* GetName() const { |
| @@ -466,6 +470,12 @@ public: | |||
| 466 | 470 | ||
| 467 | static void Switch(KProcess* cur_process, KProcess* next_process); | 471 | static void Switch(KProcess* cur_process, KProcess* next_process); |
| 468 | 472 | ||
| 473 | #ifdef HAS_NCE | ||
| 474 | std::unordered_map<u64, u64>& GetPostHandlers() noexcept { | ||
| 475 | return m_post_handlers; | ||
| 476 | } | ||
| 477 | #endif | ||
| 478 | |||
| 469 | public: | 479 | public: |
| 470 | // Attempts to insert a watchpoint into a free slot. Returns false if none are available. | 480 | // Attempts to insert a watchpoint into a free slot. Returns false if none are available. |
| 471 | bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); | 481 | bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); |
| @@ -479,7 +489,7 @@ public: | |||
| 479 | 489 | ||
| 480 | public: | 490 | public: |
| 481 | Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, | 491 | Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, |
| 482 | bool is_hbl); | 492 | KProcessAddress aslr_space_start, bool is_hbl); |
| 483 | 493 | ||
| 484 | void LoadModule(CodeSet code_set, KProcessAddress base_addr); | 494 | void LoadModule(CodeSet code_set, KProcessAddress base_addr); |
| 485 | 495 | ||
diff --git a/src/core/hle/kernel/k_process_page_table.h b/src/core/hle/kernel/k_process_page_table.h index b7ae5abd0..9e40f68bc 100644 --- a/src/core/hle/kernel/k_process_page_table.h +++ b/src/core/hle/kernel/k_process_page_table.h | |||
| @@ -23,10 +23,11 @@ public: | |||
| 23 | Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, | 23 | Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, |
| 24 | bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, | 24 | bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, |
| 25 | size_t code_size, KSystemResource* system_resource, | 25 | size_t code_size, KSystemResource* system_resource, |
| 26 | KResourceLimit* resource_limit, Core::Memory::Memory& memory) { | 26 | KResourceLimit* resource_limit, Core::Memory::Memory& memory, |
| 27 | R_RETURN(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge, | 27 | KProcessAddress aslr_space_start) { |
| 28 | from_back, pool, code_address, code_size, | 28 | R_RETURN(m_page_table.InitializeForProcess( |
| 29 | system_resource, resource_limit, memory)); | 29 | as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size, |
| 30 | system_resource, resource_limit, memory, aslr_space_start)); | ||
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | void Finalize() { | 33 | void Finalize() { |
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index e1f80b04f..e9ca5dfca 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -655,6 +655,21 @@ public: | |||
| 655 | return m_stack_top; | 655 | return m_stack_top; |
| 656 | } | 656 | } |
| 657 | 657 | ||
| 658 | public: | ||
| 659 | // TODO: This shouldn't be defined in kernel namespace | ||
| 660 | struct NativeExecutionParameters { | ||
| 661 | u64 tpidr_el0{}; | ||
| 662 | u64 tpidrro_el0{}; | ||
| 663 | void* native_context{}; | ||
| 664 | std::atomic<u32> lock{1}; | ||
| 665 | bool is_running{}; | ||
| 666 | u32 magic{Common::MakeMagic('Y', 'U', 'Z', 'U')}; | ||
| 667 | }; | ||
| 668 | |||
| 669 | NativeExecutionParameters& GetNativeExecutionParameters() { | ||
| 670 | return m_native_execution_parameters; | ||
| 671 | } | ||
| 672 | |||
| 658 | private: | 673 | private: |
| 659 | KThread* RemoveWaiterByKey(bool* out_has_waiters, KProcessAddress key, | 674 | KThread* RemoveWaiterByKey(bool* out_has_waiters, KProcessAddress key, |
| 660 | bool is_kernel_address_key); | 675 | bool is_kernel_address_key); |
| @@ -914,6 +929,7 @@ private: | |||
| 914 | ThreadWaitReasonForDebugging m_wait_reason_for_debugging{}; | 929 | ThreadWaitReasonForDebugging m_wait_reason_for_debugging{}; |
| 915 | uintptr_t m_argument{}; | 930 | uintptr_t m_argument{}; |
| 916 | KProcessAddress m_stack_top{}; | 931 | KProcessAddress m_stack_top{}; |
| 932 | NativeExecutionParameters m_native_execution_parameters{}; | ||
| 917 | 933 | ||
| 918 | public: | 934 | public: |
| 919 | using ConditionVariableThreadTreeType = ConditionVariableThreadTree; | 935 | using ConditionVariableThreadTreeType = ConditionVariableThreadTree; |
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index 5ee869fa2..073039825 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp | |||
| @@ -1,8 +1,12 @@ | |||
| 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 "common/settings.h" | ||
| 4 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | 5 | #include "core/arm/dynarmic/arm_dynarmic_32.h" |
| 5 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 6 | #include "core/arm/dynarmic/arm_dynarmic_64.h" |
| 7 | #ifdef HAS_NCE | ||
| 8 | #include "core/arm/nce/arm_nce.h" | ||
| 9 | #endif | ||
| 6 | #include "core/core.h" | 10 | #include "core/core.h" |
| 7 | #include "core/hle/kernel/k_scheduler.h" | 11 | #include "core/hle/kernel/k_scheduler.h" |
| 8 | #include "core/hle/kernel/kernel.h" | 12 | #include "core/hle/kernel/kernel.h" |
| @@ -14,7 +18,8 @@ PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KSchedu | |||
| 14 | : m_core_index{core_index}, m_system{system}, m_scheduler{scheduler} { | 18 | : m_core_index{core_index}, m_system{system}, m_scheduler{scheduler} { |
| 15 | #if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) | 19 | #if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) |
| 16 | // TODO(bunnei): Initialization relies on a core being available. We may later replace this with | 20 | // TODO(bunnei): Initialization relies on a core being available. We may later replace this with |
| 17 | // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. | 21 | // an NCE interface or a 32-bit instance of Dynarmic. This should be abstracted out to a CPU |
| 22 | // manager. | ||
| 18 | auto& kernel = system.Kernel(); | 23 | auto& kernel = system.Kernel(); |
| 19 | m_arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( | 24 | m_arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( |
| 20 | system, kernel.IsMulticore(), | 25 | system, kernel.IsMulticore(), |
| @@ -28,6 +33,13 @@ PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KSchedu | |||
| 28 | PhysicalCore::~PhysicalCore() = default; | 33 | PhysicalCore::~PhysicalCore() = default; |
| 29 | 34 | ||
| 30 | void PhysicalCore::Initialize(bool is_64_bit) { | 35 | void PhysicalCore::Initialize(bool is_64_bit) { |
| 36 | #if defined(HAS_NCE) | ||
| 37 | if (Settings::IsNceEnabled()) { | ||
| 38 | m_arm_interface = std::make_unique<Core::ARM_NCE>(m_system, m_system.Kernel().IsMulticore(), | ||
| 39 | m_core_index); | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | #endif | ||
| 31 | #if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) | 43 | #if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) |
| 32 | auto& kernel = m_system.Kernel(); | 44 | auto& kernel = m_system.Kernel(); |
| 33 | if (!is_64_bit) { | 45 | if (!is_64_bit) { |
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..3906c0fa4 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 | } |
| @@ -130,7 +131,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) | |||
| 130 | nfp_device->DeleteApplicationArea(); | 131 | nfp_device->DeleteApplicationArea(); |
| 131 | break; | 132 | break; |
| 132 | case Service::NFP::CabinetMode::StartRestorer: | 133 | case Service::NFP::CabinetMode::StartRestorer: |
| 133 | nfp_device->RestoreAmiibo(); | 134 | nfp_device->Restore(); |
| 134 | break; | 135 | break; |
| 135 | case Service::NFP::CabinetMode::StartFormatter: | 136 | case Service::NFP::CabinetMode::StartFormatter: |
| 136 | nfp_device->Format(); | 137 | nfp_device->Format(); |
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/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/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 d46bf917e..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(); |
| @@ -484,7 +433,7 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { | |||
| 484 | } | 433 | } |
| 485 | } | 434 | } |
| 486 | 435 | ||
| 487 | void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 436 | void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 488 | if (!IsControllerActivated()) { | 437 | if (!IsControllerActivated()) { |
| 489 | return; | 438 | return; |
| 490 | } | 439 | } |
| @@ -614,134 +563,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | |||
| 614 | } | 563 | } |
| 615 | } | 564 | } |
| 616 | 565 | ||
| 617 | void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) { | 566 | void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { |
| 618 | if (!IsControllerActivated()) { | ||
| 619 | return; | ||
| 620 | } | ||
| 621 | |||
| 622 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | ||
| 623 | auto& controller = controller_data[i]; | ||
| 624 | |||
| 625 | const auto& controller_type = controller.device->GetNpadStyleIndex(); | ||
| 626 | |||
| 627 | if (controller_type == Core::HID::NpadStyleIndex::None || | ||
| 628 | !controller.device->IsConnected()) { | ||
| 629 | continue; | ||
| 630 | } | ||
| 631 | |||
| 632 | auto* npad = controller.shared_memory; | ||
| 633 | const auto& motion_state = controller.device->GetMotions(); | ||
| 634 | auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; | ||
| 635 | auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; | ||
| 636 | auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; | ||
| 637 | auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; | ||
| 638 | auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; | ||
| 639 | auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; | ||
| 640 | |||
| 641 | // Clear previous state | ||
| 642 | sixaxis_fullkey_state = {}; | ||
| 643 | sixaxis_handheld_state = {}; | ||
| 644 | sixaxis_dual_left_state = {}; | ||
| 645 | sixaxis_dual_right_state = {}; | ||
| 646 | sixaxis_left_lifo_state = {}; | ||
| 647 | sixaxis_right_lifo_state = {}; | ||
| 648 | |||
| 649 | if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { | ||
| 650 | controller.sixaxis_at_rest = true; | ||
| 651 | for (std::size_t e = 0; e < motion_state.size(); ++e) { | ||
| 652 | controller.sixaxis_at_rest = | ||
| 653 | controller.sixaxis_at_rest && motion_state[e].is_at_rest; | ||
| 654 | } | ||
| 655 | } | ||
| 656 | |||
| 657 | const auto set_motion_state = [&](SixAxisSensorState& state, | ||
| 658 | const Core::HID::ControllerMotion& hid_state) { | ||
| 659 | using namespace std::literals::chrono_literals; | ||
| 660 | static constexpr SixAxisSensorState default_motion_state = { | ||
| 661 | .delta_time = std::chrono::nanoseconds(5ms).count(), | ||
| 662 | .accel = {0, 0, -1.0f}, | ||
| 663 | .orientation = | ||
| 664 | { | ||
| 665 | Common::Vec3f{1.0f, 0, 0}, | ||
| 666 | Common::Vec3f{0, 1.0f, 0}, | ||
| 667 | Common::Vec3f{0, 0, 1.0f}, | ||
| 668 | }, | ||
| 669 | .attribute = {1}, | ||
| 670 | }; | ||
| 671 | if (!controller.sixaxis_sensor_enabled) { | ||
| 672 | state = default_motion_state; | ||
| 673 | return; | ||
| 674 | } | ||
| 675 | if (!Settings::values.motion_enabled.GetValue()) { | ||
| 676 | state = default_motion_state; | ||
| 677 | return; | ||
| 678 | } | ||
| 679 | state.attribute.is_connected.Assign(1); | ||
| 680 | state.delta_time = std::chrono::nanoseconds(5ms).count(); | ||
| 681 | state.accel = hid_state.accel; | ||
| 682 | state.gyro = hid_state.gyro; | ||
| 683 | state.rotation = hid_state.rotation; | ||
| 684 | state.orientation = hid_state.orientation; | ||
| 685 | }; | ||
| 686 | |||
| 687 | switch (controller_type) { | ||
| 688 | case Core::HID::NpadStyleIndex::None: | ||
| 689 | ASSERT(false); | ||
| 690 | break; | ||
| 691 | case Core::HID::NpadStyleIndex::ProController: | ||
| 692 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 693 | break; | ||
| 694 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 695 | set_motion_state(sixaxis_handheld_state, motion_state[0]); | ||
| 696 | break; | ||
| 697 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 698 | set_motion_state(sixaxis_dual_left_state, motion_state[0]); | ||
| 699 | set_motion_state(sixaxis_dual_right_state, motion_state[1]); | ||
| 700 | break; | ||
| 701 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 702 | set_motion_state(sixaxis_left_lifo_state, motion_state[0]); | ||
| 703 | break; | ||
| 704 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 705 | set_motion_state(sixaxis_right_lifo_state, motion_state[1]); | ||
| 706 | break; | ||
| 707 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 708 | using namespace std::literals::chrono_literals; | ||
| 709 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 710 | sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); | ||
| 711 | break; | ||
| 712 | default: | ||
| 713 | break; | ||
| 714 | } | ||
| 715 | |||
| 716 | sixaxis_fullkey_state.sampling_number = | ||
| 717 | npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 718 | sixaxis_handheld_state.sampling_number = | ||
| 719 | npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 720 | sixaxis_dual_left_state.sampling_number = | ||
| 721 | npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 722 | sixaxis_dual_right_state.sampling_number = | ||
| 723 | npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 724 | sixaxis_left_lifo_state.sampling_number = | ||
| 725 | npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 726 | sixaxis_right_lifo_state.sampling_number = | ||
| 727 | npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 728 | |||
| 729 | if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { | ||
| 730 | // This buffer only is updated on handheld on HW | ||
| 731 | npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); | ||
| 732 | } else { | ||
| 733 | // Handheld doesn't update this buffer on HW | ||
| 734 | npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); | ||
| 735 | } | ||
| 736 | |||
| 737 | npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); | ||
| 738 | npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); | ||
| 739 | npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); | ||
| 740 | npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); | ||
| 741 | } | ||
| 742 | } | ||
| 743 | |||
| 744 | void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { | ||
| 745 | hid_core.SetSupportedStyleTag(style_set); | 567 | hid_core.SetSupportedStyleTag(style_set); |
| 746 | 568 | ||
| 747 | if (is_controller_initialized) { | 569 | if (is_controller_initialized) { |
| @@ -752,14 +574,14 @@ void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { | |||
| 752 | is_controller_initialized = true; | 574 | is_controller_initialized = true; |
| 753 | } | 575 | } |
| 754 | 576 | ||
| 755 | Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { | 577 | Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const { |
| 756 | if (!is_controller_initialized) { | 578 | if (!is_controller_initialized) { |
| 757 | return {Core::HID::NpadStyleSet::None}; | 579 | return {Core::HID::NpadStyleSet::None}; |
| 758 | } | 580 | } |
| 759 | return hid_core.GetSupportedStyleTag(); | 581 | return hid_core.GetSupportedStyleTag(); |
| 760 | } | 582 | } |
| 761 | 583 | ||
| 762 | Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { | 584 | Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { |
| 763 | constexpr std::size_t max_number_npad_ids = 0xa; | 585 | constexpr std::size_t max_number_npad_ids = 0xa; |
| 764 | const auto length = data.size(); | 586 | const auto length = data.size(); |
| 765 | ASSERT(length > 0 && (length % sizeof(u32)) == 0); | 587 | ASSERT(length > 0 && (length % sizeof(u32)) == 0); |
| @@ -775,17 +597,17 @@ Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { | |||
| 775 | return ResultSuccess; | 597 | return ResultSuccess; |
| 776 | } | 598 | } |
| 777 | 599 | ||
| 778 | void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { | 600 | void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { |
| 779 | const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); | 601 | const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); |
| 780 | ASSERT(max_length <= copy_amount); | 602 | ASSERT(max_length <= copy_amount); |
| 781 | std::memcpy(data, supported_npad_id_types.data(), copy_amount); | 603 | std::memcpy(data, supported_npad_id_types.data(), copy_amount); |
| 782 | } | 604 | } |
| 783 | 605 | ||
| 784 | std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { | 606 | std::size_t NPad::GetSupportedNpadIdTypesSize() const { |
| 785 | return supported_npad_id_types.size(); | 607 | return supported_npad_id_types.size(); |
| 786 | } | 608 | } |
| 787 | 609 | ||
| 788 | void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { | 610 | void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { |
| 789 | if (joy_hold_type != NpadJoyHoldType::Horizontal && | 611 | if (joy_hold_type != NpadJoyHoldType::Horizontal && |
| 790 | joy_hold_type != NpadJoyHoldType::Vertical) { | 612 | joy_hold_type != NpadJoyHoldType::Vertical) { |
| 791 | 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={}", |
| @@ -795,11 +617,11 @@ void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { | |||
| 795 | hold_type = joy_hold_type; | 617 | hold_type = joy_hold_type; |
| 796 | } | 618 | } |
| 797 | 619 | ||
| 798 | Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { | 620 | NPad::NpadJoyHoldType NPad::GetHoldType() const { |
| 799 | return hold_type; | 621 | return hold_type; |
| 800 | } | 622 | } |
| 801 | 623 | ||
| 802 | void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { | 624 | void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { |
| 803 | if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { | 625 | if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { |
| 804 | 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"); |
| 805 | return; | 627 | return; |
| @@ -808,21 +630,20 @@ void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode a | |||
| 808 | handheld_activation_mode = activation_mode; | 630 | handheld_activation_mode = activation_mode; |
| 809 | } | 631 | } |
| 810 | 632 | ||
| 811 | Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const { | 633 | NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const { |
| 812 | return handheld_activation_mode; | 634 | return handheld_activation_mode; |
| 813 | } | 635 | } |
| 814 | 636 | ||
| 815 | void Controller_NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { | 637 | void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { |
| 816 | communication_mode = communication_mode_; | 638 | communication_mode = communication_mode_; |
| 817 | } | 639 | } |
| 818 | 640 | ||
| 819 | Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode() const { | 641 | NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const { |
| 820 | return communication_mode; | 642 | return communication_mode; |
| 821 | } | 643 | } |
| 822 | 644 | ||
| 823 | 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, |
| 824 | NpadJoyDeviceType npad_device_type, | 646 | NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) { |
| 825 | NpadJoyAssignmentMode assignment_mode) { | ||
| 826 | if (!IsNpadIdValid(npad_id)) { | 647 | if (!IsNpadIdValid(npad_id)) { |
| 827 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 648 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 828 | return false; | 649 | return false; |
| @@ -891,9 +712,8 @@ bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID: | |||
| 891 | return true; | 712 | return true; |
| 892 | } | 713 | } |
| 893 | 714 | ||
| 894 | bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, | 715 | bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, |
| 895 | std::size_t device_index, | 716 | const Core::HID::VibrationValue& vibration_value) { |
| 896 | const Core::HID::VibrationValue& vibration_value) { | ||
| 897 | auto& controller = GetControllerFromNpadIdType(npad_id); | 717 | auto& controller = GetControllerFromNpadIdType(npad_id); |
| 898 | if (!controller.device->IsConnected()) { | 718 | if (!controller.device->IsConnected()) { |
| 899 | return false; | 719 | return false; |
| @@ -937,10 +757,9 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, | |||
| 937 | return controller.device->SetVibration(device_index, vibration); | 757 | return controller.device->SetVibration(device_index, vibration); |
| 938 | } | 758 | } |
| 939 | 759 | ||
| 940 | void Controller_NPad::VibrateController( | 760 | void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle, |
| 941 | const Core::HID::VibrationDeviceHandle& vibration_device_handle, | 761 | const Core::HID::VibrationValue& vibration_value) { |
| 942 | const Core::HID::VibrationValue& vibration_value) { | 762 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { |
| 943 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { | ||
| 944 | return; | 763 | return; |
| 945 | } | 764 | } |
| 946 | 765 | ||
| @@ -984,7 +803,7 @@ void Controller_NPad::VibrateController( | |||
| 984 | } | 803 | } |
| 985 | } | 804 | } |
| 986 | 805 | ||
| 987 | void Controller_NPad::VibrateControllers( | 806 | void NPad::VibrateControllers( |
| 988 | std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, | 807 | std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, |
| 989 | std::span<const Core::HID::VibrationValue> vibration_values) { | 808 | std::span<const Core::HID::VibrationValue> vibration_values) { |
| 990 | if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { | 809 | if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { |
| @@ -1001,9 +820,9 @@ void Controller_NPad::VibrateControllers( | |||
| 1001 | } | 820 | } |
| 1002 | } | 821 | } |
| 1003 | 822 | ||
| 1004 | Core::HID::VibrationValue Controller_NPad::GetLastVibration( | 823 | Core::HID::VibrationValue NPad::GetLastVibration( |
| 1005 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { | 824 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { |
| 1006 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { | 825 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { |
| 1007 | return {}; | 826 | return {}; |
| 1008 | } | 827 | } |
| 1009 | 828 | ||
| @@ -1012,9 +831,9 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration( | |||
| 1012 | return controller.vibration[device_index].latest_vibration_value; | 831 | return controller.vibration[device_index].latest_vibration_value; |
| 1013 | } | 832 | } |
| 1014 | 833 | ||
| 1015 | void Controller_NPad::InitializeVibrationDevice( | 834 | void NPad::InitializeVibrationDevice( |
| 1016 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) { | 835 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) { |
| 1017 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { | 836 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { |
| 1018 | return; | 837 | return; |
| 1019 | } | 838 | } |
| 1020 | 839 | ||
| @@ -1023,8 +842,8 @@ void Controller_NPad::InitializeVibrationDevice( | |||
| 1023 | InitializeVibrationDeviceAtIndex(npad_index, device_index); | 842 | InitializeVibrationDeviceAtIndex(npad_index, device_index); |
| 1024 | } | 843 | } |
| 1025 | 844 | ||
| 1026 | void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, | 845 | void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, |
| 1027 | std::size_t device_index) { | 846 | std::size_t device_index) { |
| 1028 | auto& controller = GetControllerFromNpadIdType(npad_id); | 847 | auto& controller = GetControllerFromNpadIdType(npad_id); |
| 1029 | if (!Settings::values.vibration_enabled.GetValue()) { | 848 | if (!Settings::values.vibration_enabled.GetValue()) { |
| 1030 | controller.vibration[device_index].device_mounted = false; | 849 | controller.vibration[device_index].device_mounted = false; |
| @@ -1035,13 +854,13 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa | |||
| 1035 | controller.device->IsVibrationEnabled(device_index); | 854 | controller.device->IsVibrationEnabled(device_index); |
| 1036 | } | 855 | } |
| 1037 | 856 | ||
| 1038 | void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { | 857 | void NPad::SetPermitVibrationSession(bool permit_vibration_session) { |
| 1039 | permit_vibration_session_enabled = permit_vibration_session; | 858 | permit_vibration_session_enabled = permit_vibration_session; |
| 1040 | } | 859 | } |
| 1041 | 860 | ||
| 1042 | bool Controller_NPad::IsVibrationDeviceMounted( | 861 | bool NPad::IsVibrationDeviceMounted( |
| 1043 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { | 862 | const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { |
| 1044 | if (IsDeviceHandleValid(vibration_device_handle).IsError()) { | 863 | if (IsVibrationHandleValid(vibration_device_handle).IsError()) { |
| 1045 | return false; | 864 | return false; |
| 1046 | } | 865 | } |
| 1047 | 866 | ||
| @@ -1050,7 +869,7 @@ bool Controller_NPad::IsVibrationDeviceMounted( | |||
| 1050 | return controller.vibration[device_index].device_mounted; | 869 | return controller.vibration[device_index].device_mounted; |
| 1051 | } | 870 | } |
| 1052 | 871 | ||
| 1053 | Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { | 872 | Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { |
| 1054 | if (!IsNpadIdValid(npad_id)) { | 873 | if (!IsNpadIdValid(npad_id)) { |
| 1055 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 874 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1056 | // Fallback to player 1 | 875 | // Fallback to player 1 |
| @@ -1062,18 +881,17 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad | |||
| 1062 | return controller.styleset_changed_event->GetReadableEvent(); | 881 | return controller.styleset_changed_event->GetReadableEvent(); |
| 1063 | } | 882 | } |
| 1064 | 883 | ||
| 1065 | void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { | 884 | void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { |
| 1066 | const auto& controller = GetControllerFromNpadIdType(npad_id); | 885 | const auto& controller = GetControllerFromNpadIdType(npad_id); |
| 1067 | controller.styleset_changed_event->Signal(); | 886 | controller.styleset_changed_event->Signal(); |
| 1068 | } | 887 | } |
| 1069 | 888 | ||
| 1070 | void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, | 889 | void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) { |
| 1071 | Core::HID::NpadIdType npad_id) { | ||
| 1072 | UpdateControllerAt(controller, npad_id, true); | 890 | UpdateControllerAt(controller, npad_id, true); |
| 1073 | } | 891 | } |
| 1074 | 892 | ||
| 1075 | void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, | 893 | void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id, |
| 1076 | Core::HID::NpadIdType npad_id, bool connected) { | 894 | bool connected) { |
| 1077 | auto& controller = GetControllerFromNpadIdType(npad_id); | 895 | auto& controller = GetControllerFromNpadIdType(npad_id); |
| 1078 | if (!connected) { | 896 | if (!connected) { |
| 1079 | DisconnectNpad(npad_id); | 897 | DisconnectNpad(npad_id); |
| @@ -1084,7 +902,7 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, | |||
| 1084 | InitNewlyAddedController(npad_id); | 902 | InitNewlyAddedController(npad_id); |
| 1085 | } | 903 | } |
| 1086 | 904 | ||
| 1087 | Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { | 905 | Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { |
| 1088 | if (!IsNpadIdValid(npad_id)) { | 906 | if (!IsNpadIdValid(npad_id)) { |
| 1089 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 907 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1090 | return InvalidNpadId; | 908 | return InvalidNpadId; |
| @@ -1133,54 +951,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { | |||
| 1133 | return ResultSuccess; | 951 | return ResultSuccess; |
| 1134 | } | 952 | } |
| 1135 | 953 | ||
| 1136 | Result Controller_NPad::SetGyroscopeZeroDriftMode( | 954 | Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor( |
| 1137 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 1138 | Core::HID::GyroscopeZeroDriftMode drift_mode) { | ||
| 1139 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1140 | if (is_valid.IsError()) { | ||
| 1141 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1142 | return is_valid; | ||
| 1143 | } | ||
| 1144 | |||
| 1145 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1146 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1147 | sixaxis.gyroscope_zero_drift_mode = drift_mode; | ||
| 1148 | controller.device->SetGyroscopeZeroDriftMode(drift_mode); | ||
| 1149 | |||
| 1150 | return ResultSuccess; | ||
| 1151 | } | ||
| 1152 | |||
| 1153 | Result Controller_NPad::GetGyroscopeZeroDriftMode( | ||
| 1154 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 1155 | Core::HID::GyroscopeZeroDriftMode& drift_mode) const { | ||
| 1156 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1157 | if (is_valid.IsError()) { | ||
| 1158 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1159 | return is_valid; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1163 | drift_mode = sixaxis.gyroscope_zero_drift_mode; | ||
| 1164 | |||
| 1165 | return ResultSuccess; | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 1169 | bool& is_at_rest) const { | ||
| 1170 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1171 | if (is_valid.IsError()) { | ||
| 1172 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1173 | return is_valid; | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1177 | is_at_rest = controller.sixaxis_at_rest; | ||
| 1178 | return ResultSuccess; | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor( | ||
| 1182 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { | 955 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { |
| 1183 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | 956 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); |
| 1184 | if (is_valid.IsError()) { | 957 | if (is_valid.IsError()) { |
| 1185 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | 958 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); |
| 1186 | return is_valid; | 959 | return is_valid; |
| @@ -1191,65 +964,9 @@ Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor( | |||
| 1191 | return ResultSuccess; | 964 | return ResultSuccess; |
| 1192 | } | 965 | } |
| 1193 | 966 | ||
| 1194 | Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough( | 967 | Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( |
| 1195 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) { | ||
| 1196 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1197 | if (is_valid.IsError()) { | ||
| 1198 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1199 | return is_valid; | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1203 | sixaxis.unaltered_passtrough = is_enabled; | ||
| 1204 | return ResultSuccess; | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled( | ||
| 1208 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const { | ||
| 1209 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1210 | if (is_valid.IsError()) { | ||
| 1211 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1212 | return is_valid; | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1216 | is_enabled = sixaxis.unaltered_passtrough; | ||
| 1217 | return ResultSuccess; | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | Result Controller_NPad::LoadSixAxisSensorCalibrationParameter( | ||
| 1221 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 1222 | Core::HID::SixAxisSensorCalibrationParameter& calibration) const { | ||
| 1223 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1224 | if (is_valid.IsError()) { | ||
| 1225 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1226 | return is_valid; | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | // TODO: Request this data to the controller. On error return 0xd8ca | ||
| 1230 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1231 | calibration = sixaxis.calibration; | ||
| 1232 | return ResultSuccess; | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | Result Controller_NPad::GetSixAxisSensorIcInformation( | ||
| 1236 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 1237 | Core::HID::SixAxisSensorIcInformation& ic_information) const { | ||
| 1238 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1239 | if (is_valid.IsError()) { | ||
| 1240 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1241 | return is_valid; | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | // TODO: Request this data to the controller. On error return 0xd8ca | ||
| 1245 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1246 | ic_information = sixaxis.ic_information; | ||
| 1247 | return ResultSuccess; | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( | ||
| 1251 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { | 968 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { |
| 1252 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | 969 | const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); |
| 1253 | if (is_valid.IsError()) { | 970 | if (is_valid.IsError()) { |
| 1254 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | 971 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); |
| 1255 | return is_valid; | 972 | return is_valid; |
| @@ -1261,83 +978,32 @@ Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( | |||
| 1261 | return ResultSuccess; | 978 | return ResultSuccess; |
| 1262 | } | 979 | } |
| 1263 | 980 | ||
| 1264 | Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 981 | NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) { |
| 1265 | bool sixaxis_status) { | 982 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo; |
| 1266 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1267 | if (is_valid.IsError()) { | ||
| 1268 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1269 | return is_valid; | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1273 | controller.sixaxis_sensor_enabled = sixaxis_status; | ||
| 1274 | return ResultSuccess; | ||
| 1275 | } | 983 | } |
| 1276 | 984 | ||
| 1277 | Result Controller_NPad::IsSixAxisSensorFusionEnabled( | 985 | NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) { |
| 1278 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const { | 986 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo; |
| 1279 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1280 | if (is_valid.IsError()) { | ||
| 1281 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1282 | return is_valid; | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1286 | is_fusion_enabled = sixaxis.is_fusion_enabled; | ||
| 1287 | |||
| 1288 | return ResultSuccess; | ||
| 1289 | } | 987 | } |
| 1290 | Result Controller_NPad::SetSixAxisFusionEnabled( | ||
| 1291 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) { | ||
| 1292 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1293 | if (is_valid.IsError()) { | ||
| 1294 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1295 | return is_valid; | ||
| 1296 | } | ||
| 1297 | 988 | ||
| 1298 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | 989 | NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) { |
| 1299 | sixaxis.is_fusion_enabled = is_fusion_enabled; | 990 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo; |
| 1300 | |||
| 1301 | return ResultSuccess; | ||
| 1302 | } | 991 | } |
| 1303 | 992 | ||
| 1304 | Result Controller_NPad::SetSixAxisFusionParameters( | 993 | NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) { |
| 1305 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 994 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo; |
| 1306 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { | ||
| 1307 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1308 | if (is_valid.IsError()) { | ||
| 1309 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1310 | return is_valid; | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | const auto param1 = sixaxis_fusion_parameters.parameter1; | ||
| 1314 | if (param1 < 0.0f || param1 > 1.0f) { | ||
| 1315 | return InvalidSixAxisFusionRange; | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1319 | sixaxis.fusion = sixaxis_fusion_parameters; | ||
| 1320 | |||
| 1321 | return ResultSuccess; | ||
| 1322 | } | 995 | } |
| 1323 | 996 | ||
| 1324 | Result Controller_NPad::GetSixAxisFusionParameters( | 997 | NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) { |
| 1325 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 998 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo; |
| 1326 | Core::HID::SixAxisSensorFusionParameters& parameters) const { | 999 | } |
| 1327 | const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); | ||
| 1328 | if (is_valid.IsError()) { | ||
| 1329 | LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); | ||
| 1330 | return is_valid; | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | const auto& sixaxis = GetSixaxisState(sixaxis_handle); | ||
| 1334 | parameters = sixaxis.fusion; | ||
| 1335 | 1000 | ||
| 1336 | return ResultSuccess; | 1001 | NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) { |
| 1002 | return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo; | ||
| 1337 | } | 1003 | } |
| 1338 | 1004 | ||
| 1339 | Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, | 1005 | Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, |
| 1340 | Core::HID::NpadIdType npad_id_2) { | 1006 | Core::HID::NpadIdType npad_id_2) { |
| 1341 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { | 1007 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { |
| 1342 | 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, |
| 1343 | npad_id_2); | 1009 | npad_id_2); |
| @@ -1399,18 +1065,17 @@ Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, | |||
| 1399 | return ResultSuccess; | 1065 | return ResultSuccess; |
| 1400 | } | 1066 | } |
| 1401 | 1067 | ||
| 1402 | void Controller_NPad::StartLRAssignmentMode() { | 1068 | void NPad::StartLRAssignmentMode() { |
| 1403 | // 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 |
| 1404 | // 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 |
| 1405 | is_in_lr_assignment_mode = true; | 1071 | is_in_lr_assignment_mode = true; |
| 1406 | } | 1072 | } |
| 1407 | 1073 | ||
| 1408 | void Controller_NPad::StopLRAssignmentMode() { | 1074 | void NPad::StopLRAssignmentMode() { |
| 1409 | is_in_lr_assignment_mode = false; | 1075 | is_in_lr_assignment_mode = false; |
| 1410 | } | 1076 | } |
| 1411 | 1077 | ||
| 1412 | 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) { |
| 1413 | Core::HID::NpadIdType npad_id_2) { | ||
| 1414 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { | 1079 | if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { |
| 1415 | 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, |
| 1416 | npad_id_2); | 1081 | npad_id_2); |
| @@ -1441,8 +1106,7 @@ Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, | |||
| 1441 | return ResultSuccess; | 1106 | return ResultSuccess; |
| 1442 | } | 1107 | } |
| 1443 | 1108 | ||
| 1444 | Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, | 1109 | Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const { |
| 1445 | Core::HID::LedPattern& pattern) const { | ||
| 1446 | if (!IsNpadIdValid(npad_id)) { | 1110 | if (!IsNpadIdValid(npad_id)) { |
| 1447 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 1111 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1448 | return InvalidNpadId; | 1112 | return InvalidNpadId; |
| @@ -1452,8 +1116,8 @@ Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, | |||
| 1452 | return ResultSuccess; | 1116 | return ResultSuccess; |
| 1453 | } | 1117 | } |
| 1454 | 1118 | ||
| 1455 | Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, | 1119 | Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, |
| 1456 | bool& is_valid) const { | 1120 | bool& is_valid) const { |
| 1457 | if (!IsNpadIdValid(npad_id)) { | 1121 | if (!IsNpadIdValid(npad_id)) { |
| 1458 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 1122 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1459 | return InvalidNpadId; | 1123 | return InvalidNpadId; |
| @@ -1463,8 +1127,8 @@ Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID:: | |||
| 1463 | return ResultSuccess; | 1127 | return ResultSuccess; |
| 1464 | } | 1128 | } |
| 1465 | 1129 | ||
| 1466 | Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( | 1130 | Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, |
| 1467 | bool is_protection_enabled, Core::HID::NpadIdType npad_id) { | 1131 | Core::HID::NpadIdType npad_id) { |
| 1468 | if (!IsNpadIdValid(npad_id)) { | 1132 | if (!IsNpadIdValid(npad_id)) { |
| 1469 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 1133 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1470 | return InvalidNpadId; | 1134 | return InvalidNpadId; |
| @@ -1474,11 +1138,11 @@ Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( | |||
| 1474 | return ResultSuccess; | 1138 | return ResultSuccess; |
| 1475 | } | 1139 | } |
| 1476 | 1140 | ||
| 1477 | void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { | 1141 | void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { |
| 1478 | analog_stick_use_center_clamp = use_center_clamp; | 1142 | analog_stick_use_center_clamp = use_center_clamp; |
| 1479 | } | 1143 | } |
| 1480 | 1144 | ||
| 1481 | void Controller_NPad::ClearAllConnectedControllers() { | 1145 | void NPad::ClearAllConnectedControllers() { |
| 1482 | for (auto& controller : controller_data) { | 1146 | for (auto& controller : controller_data) { |
| 1483 | if (controller.device->IsConnected() && | 1147 | if (controller.device->IsConnected() && |
| 1484 | controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { | 1148 | controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { |
| @@ -1488,13 +1152,13 @@ void Controller_NPad::ClearAllConnectedControllers() { | |||
| 1488 | } | 1152 | } |
| 1489 | } | 1153 | } |
| 1490 | 1154 | ||
| 1491 | void Controller_NPad::DisconnectAllConnectedControllers() { | 1155 | void NPad::DisconnectAllConnectedControllers() { |
| 1492 | for (auto& controller : controller_data) { | 1156 | for (auto& controller : controller_data) { |
| 1493 | controller.device->Disconnect(); | 1157 | controller.device->Disconnect(); |
| 1494 | } | 1158 | } |
| 1495 | } | 1159 | } |
| 1496 | 1160 | ||
| 1497 | void Controller_NPad::ConnectAllDisconnectedControllers() { | 1161 | void NPad::ConnectAllDisconnectedControllers() { |
| 1498 | for (auto& controller : controller_data) { | 1162 | for (auto& controller : controller_data) { |
| 1499 | if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && | 1163 | if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && |
| 1500 | !controller.device->IsConnected()) { | 1164 | !controller.device->IsConnected()) { |
| @@ -1503,18 +1167,18 @@ void Controller_NPad::ConnectAllDisconnectedControllers() { | |||
| 1503 | } | 1167 | } |
| 1504 | } | 1168 | } |
| 1505 | 1169 | ||
| 1506 | void Controller_NPad::ClearAllControllers() { | 1170 | void NPad::ClearAllControllers() { |
| 1507 | for (auto& controller : controller_data) { | 1171 | for (auto& controller : controller_data) { |
| 1508 | controller.device->Disconnect(); | 1172 | controller.device->Disconnect(); |
| 1509 | controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); | 1173 | controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); |
| 1510 | } | 1174 | } |
| 1511 | } | 1175 | } |
| 1512 | 1176 | ||
| 1513 | Core::HID::NpadButton Controller_NPad::GetAndResetPressState() { | 1177 | Core::HID::NpadButton NPad::GetAndResetPressState() { |
| 1514 | return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); | 1178 | return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); |
| 1515 | } | 1179 | } |
| 1516 | 1180 | ||
| 1517 | void Controller_NPad::ApplyNpadSystemCommonPolicy() { | 1181 | void NPad::ApplyNpadSystemCommonPolicy() { |
| 1518 | Core::HID::NpadStyleTag styletag{}; | 1182 | Core::HID::NpadStyleTag styletag{}; |
| 1519 | styletag.fullkey.Assign(1); | 1183 | styletag.fullkey.Assign(1); |
| 1520 | styletag.handheld.Assign(1); | 1184 | styletag.handheld.Assign(1); |
| @@ -1539,7 +1203,7 @@ void Controller_NPad::ApplyNpadSystemCommonPolicy() { | |||
| 1539 | supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; | 1203 | supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; |
| 1540 | } | 1204 | } |
| 1541 | 1205 | ||
| 1542 | bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { | 1206 | bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { |
| 1543 | if (controller == Core::HID::NpadStyleIndex::Handheld) { | 1207 | if (controller == Core::HID::NpadStyleIndex::Handheld) { |
| 1544 | const bool support_handheld = | 1208 | const bool support_handheld = |
| 1545 | 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(), |
| @@ -1590,51 +1254,50 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller | |||
| 1590 | return false; | 1254 | return false; |
| 1591 | } | 1255 | } |
| 1592 | 1256 | ||
| 1593 | Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | 1257 | NPad::NpadControllerData& NPad::GetControllerFromHandle( |
| 1594 | const Core::HID::SixAxisSensorHandle& device_handle) { | 1258 | const Core::HID::VibrationDeviceHandle& device_handle) { |
| 1595 | 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); |
| 1596 | return GetControllerFromNpadIdType(npad_id); | 1260 | return GetControllerFromNpadIdType(npad_id); |
| 1597 | } | 1261 | } |
| 1598 | 1262 | ||
| 1599 | const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | 1263 | const NPad::NpadControllerData& NPad::GetControllerFromHandle( |
| 1600 | const Core::HID::SixAxisSensorHandle& device_handle) const { | 1264 | const Core::HID::VibrationDeviceHandle& device_handle) const { |
| 1601 | 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); |
| 1602 | return GetControllerFromNpadIdType(npad_id); | 1266 | return GetControllerFromNpadIdType(npad_id); |
| 1603 | } | 1267 | } |
| 1604 | 1268 | ||
| 1605 | Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | 1269 | NPad::NpadControllerData& NPad::GetControllerFromHandle( |
| 1606 | const Core::HID::VibrationDeviceHandle& device_handle) { | 1270 | const Core::HID::SixAxisSensorHandle& device_handle) { |
| 1607 | 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); |
| 1608 | return GetControllerFromNpadIdType(npad_id); | 1272 | return GetControllerFromNpadIdType(npad_id); |
| 1609 | } | 1273 | } |
| 1610 | 1274 | ||
| 1611 | const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( | 1275 | const NPad::NpadControllerData& NPad::GetControllerFromHandle( |
| 1612 | const Core::HID::VibrationDeviceHandle& device_handle) const { | 1276 | const Core::HID::SixAxisSensorHandle& device_handle) const { |
| 1613 | 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); |
| 1614 | return GetControllerFromNpadIdType(npad_id); | 1278 | return GetControllerFromNpadIdType(npad_id); |
| 1615 | } | 1279 | } |
| 1616 | 1280 | ||
| 1617 | Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( | 1281 | NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) { |
| 1618 | Core::HID::NpadIdType npad_id) { | ||
| 1619 | if (!IsNpadIdValid(npad_id)) { | 1282 | if (!IsNpadIdValid(npad_id)) { |
| 1620 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 1283 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1621 | npad_id = Core::HID::NpadIdType::Player1; | 1284 | npad_id = Core::HID::NpadIdType::Player1; |
| 1622 | } | 1285 | } |
| 1623 | const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); | 1286 | const auto npad_index = NpadIdTypeToIndex(npad_id); |
| 1624 | return controller_data[npad_index]; | 1287 | return controller_data[npad_index]; |
| 1625 | } | 1288 | } |
| 1626 | 1289 | ||
| 1627 | const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( | 1290 | const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType( |
| 1628 | Core::HID::NpadIdType npad_id) const { | 1291 | Core::HID::NpadIdType npad_id) const { |
| 1629 | if (!IsNpadIdValid(npad_id)) { | 1292 | if (!IsNpadIdValid(npad_id)) { |
| 1630 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); | 1293 | LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); |
| 1631 | npad_id = Core::HID::NpadIdType::Player1; | 1294 | npad_id = Core::HID::NpadIdType::Player1; |
| 1632 | } | 1295 | } |
| 1633 | const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); | 1296 | const auto npad_index = NpadIdTypeToIndex(npad_id); |
| 1634 | return controller_data[npad_index]; | 1297 | return controller_data[npad_index]; |
| 1635 | } | 1298 | } |
| 1636 | 1299 | ||
| 1637 | Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( | 1300 | Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( |
| 1638 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { | 1301 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { |
| 1639 | auto& controller = GetControllerFromHandle(sixaxis_handle); | 1302 | auto& controller = GetControllerFromHandle(sixaxis_handle); |
| 1640 | switch (sixaxis_handle.npad_type) { | 1303 | switch (sixaxis_handle.npad_type) { |
| @@ -1657,7 +1320,7 @@ Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( | |||
| 1657 | } | 1320 | } |
| 1658 | } | 1321 | } |
| 1659 | 1322 | ||
| 1660 | const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( | 1323 | const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( |
| 1661 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { | 1324 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { |
| 1662 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | 1325 | const auto& controller = GetControllerFromHandle(sixaxis_handle); |
| 1663 | switch (sixaxis_handle.npad_type) { | 1326 | switch (sixaxis_handle.npad_type) { |
| @@ -1680,50 +1343,13 @@ const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( | |||
| 1680 | } | 1343 | } |
| 1681 | } | 1344 | } |
| 1682 | 1345 | ||
| 1683 | Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( | 1346 | NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) { |
| 1684 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) { | 1347 | const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory; |
| 1685 | auto& controller = GetControllerFromHandle(sixaxis_handle); | ||
| 1686 | switch (sixaxis_handle.npad_type) { | ||
| 1687 | case Core::HID::NpadStyleIndex::ProController: | ||
| 1688 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 1689 | return controller.sixaxis_fullkey; | ||
| 1690 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 1691 | return controller.sixaxis_handheld; | ||
| 1692 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 1693 | if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { | ||
| 1694 | return controller.sixaxis_dual_left; | ||
| 1695 | } | ||
| 1696 | return controller.sixaxis_dual_right; | ||
| 1697 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 1698 | return controller.sixaxis_left; | ||
| 1699 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 1700 | return controller.sixaxis_right; | ||
| 1701 | default: | ||
| 1702 | return controller.sixaxis_unknown; | ||
| 1703 | } | ||
| 1704 | } | ||
| 1705 | 1348 | ||
| 1706 | const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( | 1349 | return { |
| 1707 | const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { | 1350 | .ui_variant = 0, |
| 1708 | const auto& controller = GetControllerFromHandle(sixaxis_handle); | 1351 | .footer = shared_memory->applet_footer_type, |
| 1709 | switch (sixaxis_handle.npad_type) { | 1352 | }; |
| 1710 | case Core::HID::NpadStyleIndex::ProController: | ||
| 1711 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 1712 | return controller.sixaxis_fullkey; | ||
| 1713 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 1714 | return controller.sixaxis_handheld; | ||
| 1715 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 1716 | if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) { | ||
| 1717 | return controller.sixaxis_dual_left; | ||
| 1718 | } | ||
| 1719 | return controller.sixaxis_dual_right; | ||
| 1720 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 1721 | return controller.sixaxis_left; | ||
| 1722 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 1723 | return controller.sixaxis_right; | ||
| 1724 | default: | ||
| 1725 | return controller.sixaxis_unknown; | ||
| 1726 | } | ||
| 1727 | } | 1353 | } |
| 1728 | 1354 | ||
| 1729 | } // 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 e23b4986c..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, |
| @@ -93,6 +129,8 @@ public: | |||
| 93 | Revision3 = 3, | 129 | Revision3 = 3, |
| 94 | }; | 130 | }; |
| 95 | 131 | ||
| 132 | using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>; | ||
| 133 | |||
| 96 | void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); | 134 | void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); |
| 97 | Core::HID::NpadStyleTag GetSupportedStyleSet() const; | 135 | Core::HID::NpadStyleTag GetSupportedStyleSet() const; |
| 98 | 136 | ||
| @@ -145,37 +183,18 @@ public: | |||
| 145 | 183 | ||
| 146 | Result DisconnectNpad(Core::HID::NpadIdType npad_id); | 184 | Result DisconnectNpad(Core::HID::NpadIdType npad_id); |
| 147 | 185 | ||
| 148 | Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 149 | Core::HID::GyroscopeZeroDriftMode drift_mode); | ||
| 150 | Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 151 | Core::HID::GyroscopeZeroDriftMode& drift_mode) const; | ||
| 152 | Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 153 | bool& is_at_rest) const; | ||
| 154 | Result IsFirmwareUpdateAvailableForSixAxisSensor( | 186 | Result IsFirmwareUpdateAvailableForSixAxisSensor( |
| 155 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; | 187 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; |
| 156 | Result EnableSixAxisSensorUnalteredPassthrough( | ||
| 157 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled); | ||
| 158 | Result IsSixAxisSensorUnalteredPassthroughEnabled( | ||
| 159 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const; | ||
| 160 | Result LoadSixAxisSensorCalibrationParameter( | ||
| 161 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 162 | Core::HID::SixAxisSensorCalibrationParameter& calibration) const; | ||
| 163 | Result GetSixAxisSensorIcInformation( | ||
| 164 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 165 | Core::HID::SixAxisSensorIcInformation& ic_information) const; | ||
| 166 | Result ResetIsSixAxisSensorDeviceNewlyAssigned( | 188 | Result ResetIsSixAxisSensorDeviceNewlyAssigned( |
| 167 | const Core::HID::SixAxisSensorHandle& sixaxis_handle); | 189 | const Core::HID::SixAxisSensorHandle& sixaxis_handle); |
| 168 | Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 190 | |
| 169 | bool sixaxis_status); | 191 | SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id); |
| 170 | Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 192 | SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id); |
| 171 | bool& is_fusion_enabled) const; | 193 | SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id); |
| 172 | Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 194 | SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id); |
| 173 | bool is_fusion_enabled); | 195 | SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id); |
| 174 | Result SetSixAxisFusionParameters( | 196 | SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id); |
| 175 | const Core::HID::SixAxisSensorHandle& sixaxis_handle, | 197 | |
| 176 | Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); | ||
| 177 | Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle, | ||
| 178 | Core::HID::SixAxisSensorFusionParameters& parameters) const; | ||
| 179 | 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; |
| 180 | Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, | 199 | Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, |
| 181 | bool& is_enabled) const; | 200 | bool& is_enabled) const; |
| @@ -199,10 +218,7 @@ public: | |||
| 199 | 218 | ||
| 200 | void ApplyNpadSystemCommonPolicy(); | 219 | void ApplyNpadSystemCommonPolicy(); |
| 201 | 220 | ||
| 202 | static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); | 221 | AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); |
| 203 | static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); | ||
| 204 | static Result VerifyValidSixAxisSensorHandle( | ||
| 205 | const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 206 | 222 | ||
| 207 | private: | 223 | private: |
| 208 | static constexpr std::size_t NPAD_COUNT = 10; | 224 | static constexpr std::size_t NPAD_COUNT = 10; |
| @@ -261,29 +277,6 @@ private: | |||
| 261 | }; | 277 | }; |
| 262 | static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); | 278 | static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); |
| 263 | 279 | ||
| 264 | // This is nn::hid::SixAxisSensorAttribute | ||
| 265 | struct SixAxisSensorAttribute { | ||
| 266 | union { | ||
| 267 | u32 raw{}; | ||
| 268 | BitField<0, 1, u32> is_connected; | ||
| 269 | BitField<1, 1, u32> is_interpolated; | ||
| 270 | }; | ||
| 271 | }; | ||
| 272 | static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size"); | ||
| 273 | |||
| 274 | // This is nn::hid::SixAxisSensorState | ||
| 275 | struct SixAxisSensorState { | ||
| 276 | s64 delta_time{}; | ||
| 277 | s64 sampling_number{}; | ||
| 278 | Common::Vec3f accel{}; | ||
| 279 | Common::Vec3f gyro{}; | ||
| 280 | Common::Vec3f rotation{}; | ||
| 281 | std::array<Common::Vec3f, 3> orientation{}; | ||
| 282 | SixAxisSensorAttribute attribute{}; | ||
| 283 | INSERT_PADDING_BYTES(4); // Reserved | ||
| 284 | }; | ||
| 285 | static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); | ||
| 286 | |||
| 287 | // This is nn::hid::server::NpadGcTriggerState | 280 | // This is nn::hid::server::NpadGcTriggerState |
| 288 | struct NpadGcTriggerState { | 281 | struct NpadGcTriggerState { |
| 289 | s64 sampling_number{}; | 282 | s64 sampling_number{}; |
| @@ -360,37 +353,6 @@ private: | |||
| 360 | static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, | 353 | static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, |
| 361 | "NfcXcdDeviceHandleStateImpl is an invalid size"); | 354 | "NfcXcdDeviceHandleStateImpl is an invalid size"); |
| 362 | 355 | ||
| 363 | // This is nn::hid::system::AppletFooterUiAttributesSet | ||
| 364 | struct AppletFooterUiAttributes { | ||
| 365 | INSERT_PADDING_BYTES(0x4); | ||
| 366 | }; | ||
| 367 | |||
| 368 | // This is nn::hid::system::AppletFooterUiType | ||
| 369 | enum class AppletFooterUiType : u8 { | ||
| 370 | None = 0, | ||
| 371 | HandheldNone = 1, | ||
| 372 | HandheldJoyConLeftOnly = 2, | ||
| 373 | HandheldJoyConRightOnly = 3, | ||
| 374 | HandheldJoyConLeftJoyConRight = 4, | ||
| 375 | JoyDual = 5, | ||
| 376 | JoyDualLeftOnly = 6, | ||
| 377 | JoyDualRightOnly = 7, | ||
| 378 | JoyLeftHorizontal = 8, | ||
| 379 | JoyLeftVertical = 9, | ||
| 380 | JoyRightHorizontal = 10, | ||
| 381 | JoyRightVertical = 11, | ||
| 382 | SwitchProController = 12, | ||
| 383 | CompatibleProController = 13, | ||
| 384 | CompatibleJoyCon = 14, | ||
| 385 | LarkHvc1 = 15, | ||
| 386 | LarkHvc2 = 16, | ||
| 387 | LarkNesLeft = 17, | ||
| 388 | LarkNesRight = 18, | ||
| 389 | Lucia = 19, | ||
| 390 | Verification = 20, | ||
| 391 | Lagon = 21, | ||
| 392 | }; | ||
| 393 | |||
| 394 | // This is nn::hid::NpadLarkType | 356 | // This is nn::hid::NpadLarkType |
| 395 | enum class NpadLarkType : u32 { | 357 | enum class NpadLarkType : u32 { |
| 396 | Invalid, | 358 | Invalid, |
| @@ -434,12 +396,12 @@ private: | |||
| 434 | Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; | 396 | Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; |
| 435 | Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; | 397 | Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; |
| 436 | Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; | 398 | Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; |
| 437 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; | 399 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; |
| 438 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; | 400 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; |
| 439 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; | 401 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; |
| 440 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; | 402 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; |
| 441 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; | 403 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; |
| 442 | Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; | 404 | Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; |
| 443 | DeviceType device_type{}; | 405 | DeviceType device_type{}; |
| 444 | INSERT_PADDING_BYTES(0x4); // Reserved | 406 | INSERT_PADDING_BYTES(0x4); // Reserved |
| 445 | NPadSystemProperties system_properties{}; | 407 | NPadSystemProperties system_properties{}; |
| @@ -473,16 +435,6 @@ private: | |||
| 473 | std::chrono::steady_clock::time_point last_vibration_timepoint{}; | 435 | std::chrono::steady_clock::time_point last_vibration_timepoint{}; |
| 474 | }; | 436 | }; |
| 475 | 437 | ||
| 476 | struct SixaxisParameters { | ||
| 477 | bool is_fusion_enabled{true}; | ||
| 478 | bool unaltered_passtrough{false}; | ||
| 479 | Core::HID::SixAxisSensorFusionParameters fusion{}; | ||
| 480 | Core::HID::SixAxisSensorCalibrationParameter calibration{}; | ||
| 481 | Core::HID::SixAxisSensorIcInformation ic_information{}; | ||
| 482 | Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{ | ||
| 483 | Core::HID::GyroscopeZeroDriftMode::Standard}; | ||
| 484 | }; | ||
| 485 | |||
| 486 | struct NpadControllerData { | 438 | struct NpadControllerData { |
| 487 | Kernel::KEvent* styleset_changed_event{}; | 439 | Kernel::KEvent* styleset_changed_event{}; |
| 488 | NpadInternalState* shared_memory = nullptr; | 440 | NpadInternalState* shared_memory = nullptr; |
| @@ -496,27 +448,10 @@ private: | |||
| 496 | bool is_dual_left_connected{true}; | 448 | bool is_dual_left_connected{true}; |
| 497 | bool is_dual_right_connected{true}; | 449 | bool is_dual_right_connected{true}; |
| 498 | 450 | ||
| 499 | // Motion parameters | ||
| 500 | bool sixaxis_at_rest{true}; | ||
| 501 | bool sixaxis_sensor_enabled{true}; | ||
| 502 | SixaxisParameters sixaxis_fullkey{}; | ||
| 503 | SixaxisParameters sixaxis_handheld{}; | ||
| 504 | SixaxisParameters sixaxis_dual_left{}; | ||
| 505 | SixaxisParameters sixaxis_dual_right{}; | ||
| 506 | SixaxisParameters sixaxis_left{}; | ||
| 507 | SixaxisParameters sixaxis_right{}; | ||
| 508 | SixaxisParameters sixaxis_unknown{}; | ||
| 509 | |||
| 510 | // Current pad state | 451 | // Current pad state |
| 511 | NPadGenericState npad_pad_state{}; | 452 | NPadGenericState npad_pad_state{}; |
| 512 | NPadGenericState npad_libnx_state{}; | 453 | NPadGenericState npad_libnx_state{}; |
| 513 | NpadGcTriggerState npad_trigger_state{}; | 454 | NpadGcTriggerState npad_trigger_state{}; |
| 514 | SixAxisSensorState sixaxis_fullkey_state{}; | ||
| 515 | SixAxisSensorState sixaxis_handheld_state{}; | ||
| 516 | SixAxisSensorState sixaxis_dual_left_state{}; | ||
| 517 | SixAxisSensorState sixaxis_dual_right_state{}; | ||
| 518 | SixAxisSensorState sixaxis_left_lifo_state{}; | ||
| 519 | SixAxisSensorState sixaxis_right_lifo_state{}; | ||
| 520 | int callback_key{}; | 455 | int callback_key{}; |
| 521 | }; | 456 | }; |
| 522 | 457 | ||
| @@ -527,13 +462,13 @@ private: | |||
| 527 | void WriteEmptyEntry(NpadInternalState* npad); | 462 | void WriteEmptyEntry(NpadInternalState* npad); |
| 528 | 463 | ||
| 529 | NpadControllerData& GetControllerFromHandle( | 464 | NpadControllerData& GetControllerFromHandle( |
| 530 | const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 531 | const NpadControllerData& GetControllerFromHandle( | ||
| 532 | const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 533 | NpadControllerData& GetControllerFromHandle( | ||
| 534 | const Core::HID::VibrationDeviceHandle& device_handle); | 465 | const Core::HID::VibrationDeviceHandle& device_handle); |
| 535 | const NpadControllerData& GetControllerFromHandle( | 466 | const NpadControllerData& GetControllerFromHandle( |
| 536 | 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; | ||
| 537 | NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); | 472 | NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); |
| 538 | const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; | 473 | const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; |
| 539 | 474 | ||
| @@ -541,9 +476,6 @@ private: | |||
| 541 | const Core::HID::SixAxisSensorHandle& device_handle); | 476 | const Core::HID::SixAxisSensorHandle& device_handle); |
| 542 | const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( | 477 | const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( |
| 543 | const Core::HID::SixAxisSensorHandle& device_handle) const; | 478 | const Core::HID::SixAxisSensorHandle& device_handle) const; |
| 544 | SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle); | ||
| 545 | const SixaxisParameters& GetSixaxisState( | ||
| 546 | const Core::HID::SixAxisSensorHandle& device_handle) const; | ||
| 547 | 479 | ||
| 548 | std::atomic<u64> press_state{}; | 480 | std::atomic<u64> press_state{}; |
| 549 | 481 | ||
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp index 51a18335f..588ff9d62 100644 --- a/src/core/hle/service/hid/controllers/palma.cpp +++ b/src/core/hle/service/hid/controllers/palma.cpp | |||
| @@ -12,35 +12,35 @@ | |||
| 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 | } |
| @@ -48,7 +48,7 @@ Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) { | |||
| 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..fcd973414 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp | |||
| @@ -15,9 +15,9 @@ | |||
| 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_) | 19 | : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width), |
| 20 | : ControllerBase{hid_core_} { | 20 | touchscreen_height(Layout::ScreenUndocked::Height) { |
| 21 | static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, | 21 | static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, |
| 22 | "TouchSharedMemory is bigger than the shared memory"); | 22 | "TouchSharedMemory is bigger than the shared memory"); |
| 23 | shared_memory = std::construct_at( | 23 | shared_memory = std::construct_at( |
| @@ -25,13 +25,13 @@ Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_, | |||
| 25 | console = hid_core.GetEmulatedConsole(); | 25 | console = hid_core.GetEmulatedConsole(); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | Controller_Touchscreen::~Controller_Touchscreen() = default; | 28 | TouchScreen::~TouchScreen() = default; |
| 29 | 29 | ||
| 30 | void Controller_Touchscreen::OnInit() {} | 30 | void TouchScreen::OnInit() {} |
| 31 | 31 | ||
| 32 | void Controller_Touchscreen::OnRelease() {} | 32 | void TouchScreen::OnRelease() {} |
| 33 | 33 | ||
| 34 | void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 34 | void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 35 | shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); | 35 | shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); |
| 36 | 36 | ||
| 37 | if (!IsControllerActivated()) { | 37 | if (!IsControllerActivated()) { |
| @@ -96,8 +96,8 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin | |||
| 96 | if (id < active_fingers_count) { | 96 | if (id < active_fingers_count) { |
| 97 | const auto& [active_x, active_y] = active_fingers[id].position; | 97 | const auto& [active_x, active_y] = active_fingers[id].position; |
| 98 | touch_entry.position = { | 98 | touch_entry.position = { |
| 99 | .x = static_cast<u16>(active_x * Layout::ScreenUndocked::Width), | 99 | .x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)), |
| 100 | .y = static_cast<u16>(active_y * Layout::ScreenUndocked::Height), | 100 | .y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)), |
| 101 | }; | 101 | }; |
| 102 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; | 102 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; |
| 103 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; | 103 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; |
| @@ -121,4 +121,9 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin | |||
| 121 | shared_memory->touch_screen_lifo.WriteNextEntry(next_state); | 121 | shared_memory->touch_screen_lifo.WriteNextEntry(next_state); |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) { | ||
| 125 | touchscreen_width = width; | ||
| 126 | touchscreen_height = height; | ||
| 127 | } | ||
| 128 | |||
| 124 | } // namespace Service::HID | 129 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index dd00921fd..79f026a81 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; |
| @@ -28,6 +28,8 @@ public: | |||
| 28 | // When the controller is requesting an update for the shared memory | 28 | // When the controller is requesting an update for the shared memory |
| 29 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | 29 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; |
| 30 | 30 | ||
| 31 | void SetTouchscreenDimensions(u32 width, u32 height); | ||
| 32 | |||
| 31 | private: | 33 | private: |
| 32 | static constexpr std::size_t MAX_FINGERS = 16; | 34 | static constexpr std::size_t MAX_FINGERS = 16; |
| 33 | 35 | ||
| @@ -53,5 +55,7 @@ private: | |||
| 53 | Core::HID::EmulatedConsole* console = nullptr; | 55 | Core::HID::EmulatedConsole* console = nullptr; |
| 54 | 56 | ||
| 55 | std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; | 57 | std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; |
| 58 | u32 touchscreen_width; | ||
| 59 | u32 touchscreen_height; | ||
| 56 | }; | 60 | }; |
| 57 | } // namespace Service::HID | 61 | } // namespace Service::HID |
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_server.cpp b/src/core/hle/service/hid/hid_server.cpp index 0be6a7186..a7d1578d9 100644 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp | |||
| @@ -12,11 +12,12 @@ | |||
| 12 | #include "core/hle/service/hid/errors.h" | 12 | #include "core/hle/service/hid/errors.h" |
| 13 | #include "core/hle/service/hid/hid_firmware_settings.h" | 13 | #include "core/hle/service/hid/hid_firmware_settings.h" |
| 14 | #include "core/hle/service/hid/hid_server.h" | 14 | #include "core/hle/service/hid/hid_server.h" |
| 15 | #include "core/hle/service/hid/hid_util.h" | ||
| 15 | #include "core/hle/service/hid/resource_manager.h" | 16 | #include "core/hle/service/hid/resource_manager.h" |
| 16 | #include "core/hle/service/ipc_helpers.h" | 17 | #include "core/hle/service/ipc_helpers.h" |
| 17 | #include "core/memory.h" | 18 | #include "core/memory.h" |
| 18 | 19 | ||
| 19 | #include "core/hle/service/hid/controllers/console_sixaxis.h" | 20 | #include "core/hle/service/hid/controllers/console_six_axis.h" |
| 20 | #include "core/hle/service/hid/controllers/controller_base.h" | 21 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 21 | #include "core/hle/service/hid/controllers/debug_pad.h" | 22 | #include "core/hle/service/hid/controllers/debug_pad.h" |
| 22 | #include "core/hle/service/hid/controllers/gesture.h" | 23 | #include "core/hle/service/hid/controllers/gesture.h" |
| @@ -24,9 +25,9 @@ | |||
| 24 | #include "core/hle/service/hid/controllers/mouse.h" | 25 | #include "core/hle/service/hid/controllers/mouse.h" |
| 25 | #include "core/hle/service/hid/controllers/npad.h" | 26 | #include "core/hle/service/hid/controllers/npad.h" |
| 26 | #include "core/hle/service/hid/controllers/palma.h" | 27 | #include "core/hle/service/hid/controllers/palma.h" |
| 27 | #include "core/hle/service/hid/controllers/stubbed.h" | 28 | #include "core/hle/service/hid/controllers/seven_six_axis.h" |
| 29 | #include "core/hle/service/hid/controllers/six_axis.h" | ||
| 28 | #include "core/hle/service/hid/controllers/touchscreen.h" | 30 | #include "core/hle/service/hid/controllers/touchscreen.h" |
| 29 | #include "core/hle/service/hid/controllers/xpad.h" | ||
| 30 | 31 | ||
| 31 | namespace Service::HID { | 32 | namespace Service::HID { |
| 32 | 33 | ||
| @@ -50,8 +51,7 @@ private: | |||
| 50 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; | 51 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; |
| 51 | 52 | ||
| 52 | if (resource_manager != nullptr) { | 53 | if (resource_manager != nullptr) { |
| 53 | resource_manager->GetController<Controller_NPad>(HidController::NPad) | 54 | resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle); |
| 54 | .InitializeVibrationDevice(vibration_device_handle); | ||
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", | 57 | LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", |
| @@ -208,6 +208,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r | |||
| 208 | {1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, | 208 | {1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, |
| 209 | {1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, | 209 | {1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, |
| 210 | {1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"}, | 210 | {1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"}, |
| 211 | {1004, &IHidServer::SetTouchScreenResolution, "SetTouchScreenResolution"}, | ||
| 211 | {2000, nullptr, "ActivateDigitizer"}, | 212 | {2000, nullptr, "ActivateDigitizer"}, |
| 212 | }; | 213 | }; |
| 213 | // clang-format on | 214 | // clang-format on |
| @@ -235,15 +236,14 @@ void IHidServer::ActivateDebugPad(HLERequestContext& ctx) { | |||
| 235 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 236 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 236 | 237 | ||
| 237 | Result result = ResultSuccess; | 238 | Result result = ResultSuccess; |
| 238 | auto& debug_pad = | 239 | auto debug_pad = GetResourceManager()->GetDebugPad(); |
| 239 | GetResourceManager()->GetController<Controller_DebugPad>(HidController::DebugPad); | ||
| 240 | 240 | ||
| 241 | if (!firmware_settings->IsDeviceManaged()) { | 241 | if (!firmware_settings->IsDeviceManaged()) { |
| 242 | result = debug_pad.Activate(); | 242 | result = debug_pad->Activate(); |
| 243 | } | 243 | } |
| 244 | 244 | ||
| 245 | if (result.IsSuccess()) { | 245 | if (result.IsSuccess()) { |
| 246 | result = debug_pad.Activate(applet_resource_user_id); | 246 | result = debug_pad->Activate(applet_resource_user_id); |
| 247 | } | 247 | } |
| 248 | 248 | ||
| 249 | IPC::ResponseBuilder rb{ctx, 2}; | 249 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -257,15 +257,14 @@ void IHidServer::ActivateTouchScreen(HLERequestContext& ctx) { | |||
| 257 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 257 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 258 | 258 | ||
| 259 | Result result = ResultSuccess; | 259 | Result result = ResultSuccess; |
| 260 | auto& touch_screen = | 260 | auto touch_screen = GetResourceManager()->GetTouchScreen(); |
| 261 | GetResourceManager()->GetController<Controller_Touchscreen>(HidController::Touchscreen); | ||
| 262 | 261 | ||
| 263 | if (!firmware_settings->IsDeviceManaged()) { | 262 | if (!firmware_settings->IsDeviceManaged()) { |
| 264 | result = touch_screen.Activate(); | 263 | result = touch_screen->Activate(); |
| 265 | } | 264 | } |
| 266 | 265 | ||
| 267 | if (result.IsSuccess()) { | 266 | if (result.IsSuccess()) { |
| 268 | result = touch_screen.Activate(applet_resource_user_id); | 267 | result = touch_screen->Activate(applet_resource_user_id); |
| 269 | } | 268 | } |
| 270 | 269 | ||
| 271 | IPC::ResponseBuilder rb{ctx, 2}; | 270 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -279,14 +278,14 @@ void IHidServer::ActivateMouse(HLERequestContext& ctx) { | |||
| 279 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 278 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 280 | 279 | ||
| 281 | Result result = ResultSuccess; | 280 | Result result = ResultSuccess; |
| 282 | auto& mouse = GetResourceManager()->GetController<Controller_Mouse>(HidController::Mouse); | 281 | auto mouse = GetResourceManager()->GetMouse(); |
| 283 | 282 | ||
| 284 | if (!firmware_settings->IsDeviceManaged()) { | 283 | if (!firmware_settings->IsDeviceManaged()) { |
| 285 | result = mouse.Activate(); | 284 | result = mouse->Activate(); |
| 286 | } | 285 | } |
| 287 | 286 | ||
| 288 | if (result.IsSuccess()) { | 287 | if (result.IsSuccess()) { |
| 289 | result = mouse.Activate(applet_resource_user_id); | 288 | result = mouse->Activate(applet_resource_user_id); |
| 290 | } | 289 | } |
| 291 | 290 | ||
| 292 | IPC::ResponseBuilder rb{ctx, 2}; | 291 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -300,15 +299,14 @@ void IHidServer::ActivateKeyboard(HLERequestContext& ctx) { | |||
| 300 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 299 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 301 | 300 | ||
| 302 | Result result = ResultSuccess; | 301 | Result result = ResultSuccess; |
| 303 | auto& keyboard = | 302 | auto keyboard = GetResourceManager()->GetKeyboard(); |
| 304 | GetResourceManager()->GetController<Controller_Keyboard>(HidController::Keyboard); | ||
| 305 | 303 | ||
| 306 | if (!firmware_settings->IsDeviceManaged()) { | 304 | if (!firmware_settings->IsDeviceManaged()) { |
| 307 | result = keyboard.Activate(); | 305 | result = keyboard->Activate(); |
| 308 | } | 306 | } |
| 309 | 307 | ||
| 310 | if (result.IsSuccess()) { | 308 | if (result.IsSuccess()) { |
| 311 | result = keyboard.Activate(applet_resource_user_id); | 309 | result = keyboard->Activate(applet_resource_user_id); |
| 312 | } | 310 | } |
| 313 | 311 | ||
| 314 | IPC::ResponseBuilder rb{ctx, 2}; | 312 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -502,8 +500,8 @@ void IHidServer::StartSixAxisSensor(HLERequestContext& ctx) { | |||
| 502 | 500 | ||
| 503 | const auto parameters{rp.PopRaw<Parameters>()}; | 501 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 504 | 502 | ||
| 505 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 503 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 506 | const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true); | 504 | const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, true); |
| 507 | 505 | ||
| 508 | LOG_DEBUG(Service_HID, | 506 | LOG_DEBUG(Service_HID, |
| 509 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 507 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -525,8 +523,8 @@ void IHidServer::StopSixAxisSensor(HLERequestContext& ctx) { | |||
| 525 | 523 | ||
| 526 | const auto parameters{rp.PopRaw<Parameters>()}; | 524 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 527 | 525 | ||
| 528 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 526 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 529 | const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false); | 527 | const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, false); |
| 530 | 528 | ||
| 531 | LOG_DEBUG(Service_HID, | 529 | LOG_DEBUG(Service_HID, |
| 532 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 530 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -549,9 +547,9 @@ void IHidServer::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) { | |||
| 549 | const auto parameters{rp.PopRaw<Parameters>()}; | 547 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 550 | 548 | ||
| 551 | bool is_enabled{}; | 549 | bool is_enabled{}; |
| 552 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 550 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 553 | const auto result = | 551 | const auto result = |
| 554 | controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); | 552 | six_axis->IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); |
| 555 | 553 | ||
| 556 | LOG_DEBUG(Service_HID, | 554 | LOG_DEBUG(Service_HID, |
| 557 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 555 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -575,9 +573,9 @@ void IHidServer::EnableSixAxisSensorFusion(HLERequestContext& ctx) { | |||
| 575 | 573 | ||
| 576 | const auto parameters{rp.PopRaw<Parameters>()}; | 574 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 577 | 575 | ||
| 578 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 576 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 579 | const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, | 577 | const auto result = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle, |
| 580 | parameters.enable_sixaxis_sensor_fusion); | 578 | parameters.enable_sixaxis_sensor_fusion); |
| 581 | 579 | ||
| 582 | LOG_DEBUG(Service_HID, | 580 | LOG_DEBUG(Service_HID, |
| 583 | "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " | 581 | "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " |
| @@ -602,9 +600,9 @@ void IHidServer::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) { | |||
| 602 | 600 | ||
| 603 | const auto parameters{rp.PopRaw<Parameters>()}; | 601 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 604 | 602 | ||
| 605 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 603 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 606 | const auto result = | 604 | const auto result = |
| 607 | controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); | 605 | six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); |
| 608 | 606 | ||
| 609 | LOG_DEBUG(Service_HID, | 607 | LOG_DEBUG(Service_HID, |
| 610 | "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " | 608 | "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " |
| @@ -629,10 +627,9 @@ void IHidServer::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) { | |||
| 629 | const auto parameters{rp.PopRaw<Parameters>()}; | 627 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 630 | 628 | ||
| 631 | Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; | 629 | Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; |
| 632 | const auto& controller = | 630 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 633 | GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | ||
| 634 | const auto result = | 631 | const auto result = |
| 635 | controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); | 632 | six_axis->GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); |
| 636 | 633 | ||
| 637 | LOG_DEBUG(Service_HID, | 634 | LOG_DEBUG(Service_HID, |
| 638 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 635 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -660,10 +657,10 @@ void IHidServer::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) { | |||
| 660 | .parameter1 = 0.03f, | 657 | .parameter1 = 0.03f, |
| 661 | .parameter2 = 0.4f, | 658 | .parameter2 = 0.4f, |
| 662 | }; | 659 | }; |
| 663 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 660 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 664 | const auto result1 = | 661 | const auto result1 = |
| 665 | controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); | 662 | six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); |
| 666 | const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); | 663 | const auto result2 = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); |
| 667 | 664 | ||
| 668 | LOG_DEBUG(Service_HID, | 665 | LOG_DEBUG(Service_HID, |
| 669 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 666 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -684,8 +681,8 @@ void IHidServer::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) { | |||
| 684 | const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()}; | 681 | const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()}; |
| 685 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 682 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 686 | 683 | ||
| 687 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 684 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 688 | const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); | 685 | const auto result = six_axis->SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); |
| 689 | 686 | ||
| 690 | LOG_DEBUG(Service_HID, | 687 | LOG_DEBUG(Service_HID, |
| 691 | "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " | 688 | "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " |
| @@ -709,8 +706,8 @@ void IHidServer::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) { | |||
| 709 | const auto parameters{rp.PopRaw<Parameters>()}; | 706 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 710 | 707 | ||
| 711 | auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; | 708 | auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; |
| 712 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 709 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 713 | const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); | 710 | const auto result = six_axis->GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); |
| 714 | 711 | ||
| 715 | LOG_DEBUG(Service_HID, | 712 | LOG_DEBUG(Service_HID, |
| 716 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 713 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -734,8 +731,8 @@ void IHidServer::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) { | |||
| 734 | const auto parameters{rp.PopRaw<Parameters>()}; | 731 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 735 | 732 | ||
| 736 | const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; | 733 | const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; |
| 737 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 734 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 738 | const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); | 735 | const auto result = six_axis->SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); |
| 739 | 736 | ||
| 740 | LOG_DEBUG(Service_HID, | 737 | LOG_DEBUG(Service_HID, |
| 741 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 738 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -758,8 +755,8 @@ void IHidServer::IsSixAxisSensorAtRest(HLERequestContext& ctx) { | |||
| 758 | const auto parameters{rp.PopRaw<Parameters>()}; | 755 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 759 | 756 | ||
| 760 | bool is_at_rest{}; | 757 | bool is_at_rest{}; |
| 761 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 758 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 762 | controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); | 759 | six_axis->IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); |
| 763 | 760 | ||
| 764 | LOG_DEBUG(Service_HID, | 761 | LOG_DEBUG(Service_HID, |
| 765 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 762 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -783,9 +780,9 @@ void IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ct | |||
| 783 | const auto parameters{rp.PopRaw<Parameters>()}; | 780 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 784 | 781 | ||
| 785 | bool is_firmware_available{}; | 782 | bool is_firmware_available{}; |
| 786 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 783 | auto controller = GetResourceManager()->GetNpad(); |
| 787 | controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, | 784 | controller->IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, |
| 788 | is_firmware_available); | 785 | is_firmware_available); |
| 789 | 786 | ||
| 790 | LOG_WARNING( | 787 | LOG_WARNING( |
| 791 | Service_HID, | 788 | Service_HID, |
| @@ -809,9 +806,9 @@ void IHidServer::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) | |||
| 809 | 806 | ||
| 810 | const auto parameters{rp.PopRaw<Parameters>()}; | 807 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 811 | 808 | ||
| 812 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 809 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 813 | const auto result = controller.EnableSixAxisSensorUnalteredPassthrough( | 810 | const auto result = six_axis->EnableSixAxisSensorUnalteredPassthrough(parameters.sixaxis_handle, |
| 814 | parameters.sixaxis_handle, parameters.enabled); | 811 | parameters.enabled); |
| 815 | 812 | ||
| 816 | LOG_DEBUG(Service_HID, | 813 | LOG_DEBUG(Service_HID, |
| 817 | "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " | 814 | "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " |
| @@ -836,8 +833,8 @@ void IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& c | |||
| 836 | const auto parameters{rp.PopRaw<Parameters>()}; | 833 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 837 | 834 | ||
| 838 | bool is_unaltered_sisxaxis_enabled{}; | 835 | bool is_unaltered_sisxaxis_enabled{}; |
| 839 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 836 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 840 | const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled( | 837 | const auto result = six_axis->IsSixAxisSensorUnalteredPassthroughEnabled( |
| 841 | parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); | 838 | parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); |
| 842 | 839 | ||
| 843 | LOG_DEBUG( | 840 | LOG_DEBUG( |
| @@ -863,9 +860,9 @@ void IHidServer::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) { | |||
| 863 | const auto parameters{rp.PopRaw<Parameters>()}; | 860 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 864 | 861 | ||
| 865 | Core::HID::SixAxisSensorCalibrationParameter calibration{}; | 862 | Core::HID::SixAxisSensorCalibrationParameter calibration{}; |
| 866 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 863 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 867 | const auto result = | 864 | const auto result = |
| 868 | controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration); | 865 | six_axis->LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration); |
| 869 | 866 | ||
| 870 | LOG_WARNING( | 867 | LOG_WARNING( |
| 871 | Service_HID, | 868 | Service_HID, |
| @@ -893,9 +890,9 @@ void IHidServer::GetSixAxisSensorIcInformation(HLERequestContext& ctx) { | |||
| 893 | const auto parameters{rp.PopRaw<Parameters>()}; | 890 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 894 | 891 | ||
| 895 | Core::HID::SixAxisSensorIcInformation ic_information{}; | 892 | Core::HID::SixAxisSensorIcInformation ic_information{}; |
| 896 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 893 | auto six_axis = GetResourceManager()->GetSixAxis(); |
| 897 | const auto result = | 894 | const auto result = |
| 898 | controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information); | 895 | six_axis->GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information); |
| 899 | 896 | ||
| 900 | LOG_WARNING( | 897 | LOG_WARNING( |
| 901 | Service_HID, | 898 | Service_HID, |
| @@ -922,9 +919,9 @@ void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) | |||
| 922 | 919 | ||
| 923 | const auto parameters{rp.PopRaw<Parameters>()}; | 920 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 924 | 921 | ||
| 925 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 922 | auto controller = GetResourceManager()->GetNpad(); |
| 926 | const auto result = | 923 | const auto result = |
| 927 | controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); | 924 | controller->ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); |
| 928 | 925 | ||
| 929 | LOG_WARNING( | 926 | LOG_WARNING( |
| 930 | Service_HID, | 927 | Service_HID, |
| @@ -951,15 +948,15 @@ void IHidServer::ActivateGesture(HLERequestContext& ctx) { | |||
| 951 | parameters.basic_gesture_id, parameters.applet_resource_user_id); | 948 | parameters.basic_gesture_id, parameters.applet_resource_user_id); |
| 952 | 949 | ||
| 953 | Result result = ResultSuccess; | 950 | Result result = ResultSuccess; |
| 954 | auto& gesture = GetResourceManager()->GetController<Controller_Gesture>(HidController::Gesture); | 951 | auto gesture = GetResourceManager()->GetGesture(); |
| 955 | 952 | ||
| 956 | if (!firmware_settings->IsDeviceManaged()) { | 953 | if (!firmware_settings->IsDeviceManaged()) { |
| 957 | result = gesture.Activate(); | 954 | result = gesture->Activate(); |
| 958 | } | 955 | } |
| 959 | 956 | ||
| 960 | if (result.IsSuccess()) { | 957 | if (result.IsSuccess()) { |
| 961 | // TODO: Use gesture id here | 958 | // TODO: Use gesture id here |
| 962 | result = gesture.Activate(parameters.applet_resource_user_id); | 959 | result = gesture->Activate(parameters.applet_resource_user_id); |
| 963 | } | 960 | } |
| 964 | 961 | ||
| 965 | IPC::ResponseBuilder rb{ctx, 2}; | 962 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -977,9 +974,7 @@ void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) { | |||
| 977 | 974 | ||
| 978 | const auto parameters{rp.PopRaw<Parameters>()}; | 975 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 979 | 976 | ||
| 980 | GetResourceManager() | 977 | GetResourceManager()->GetNpad()->SetSupportedStyleSet({parameters.supported_styleset}); |
| 981 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 982 | .SetSupportedStyleSet({parameters.supported_styleset}); | ||
| 983 | 978 | ||
| 984 | LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", | 979 | LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", |
| 985 | parameters.supported_styleset, parameters.applet_resource_user_id); | 980 | parameters.supported_styleset, parameters.applet_resource_user_id); |
| @@ -996,19 +991,14 @@ void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) { | |||
| 996 | 991 | ||
| 997 | IPC::ResponseBuilder rb{ctx, 3}; | 992 | IPC::ResponseBuilder rb{ctx, 3}; |
| 998 | rb.Push(ResultSuccess); | 993 | rb.Push(ResultSuccess); |
| 999 | rb.PushEnum(GetResourceManager() | 994 | rb.PushEnum(GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw); |
| 1000 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1001 | .GetSupportedStyleSet() | ||
| 1002 | .raw); | ||
| 1003 | } | 995 | } |
| 1004 | 996 | ||
| 1005 | void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) { | 997 | void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) { |
| 1006 | IPC::RequestParser rp{ctx}; | 998 | IPC::RequestParser rp{ctx}; |
| 1007 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 999 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 1008 | 1000 | ||
| 1009 | const auto result = GetResourceManager() | 1001 | const auto result = GetResourceManager()->GetNpad()->SetSupportedNpadIdTypes(ctx.ReadBuffer()); |
| 1010 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1011 | .SetSupportedNpadIdTypes(ctx.ReadBuffer()); | ||
| 1012 | 1002 | ||
| 1013 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 1003 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 1014 | 1004 | ||
| @@ -1022,10 +1012,10 @@ void IHidServer::ActivateNpad(HLERequestContext& ctx) { | |||
| 1022 | 1012 | ||
| 1023 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 1013 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 1024 | 1014 | ||
| 1025 | auto& npad = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1015 | auto npad = GetResourceManager()->GetNpad(); |
| 1026 | 1016 | ||
| 1027 | // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0); | 1017 | // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0); |
| 1028 | const Result result = npad.Activate(applet_resource_user_id); | 1018 | const Result result = npad->Activate(applet_resource_user_id); |
| 1029 | 1019 | ||
| 1030 | IPC::ResponseBuilder rb{ctx, 2}; | 1020 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1031 | rb.Push(result); | 1021 | rb.Push(result); |
| @@ -1059,15 +1049,12 @@ void IHidServer::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) { | |||
| 1059 | parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); | 1049 | parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); |
| 1060 | 1050 | ||
| 1061 | // Games expect this event to be signaled after calling this function | 1051 | // Games expect this event to be signaled after calling this function |
| 1062 | GetResourceManager() | 1052 | GetResourceManager()->GetNpad()->SignalStyleSetChangedEvent(parameters.npad_id); |
| 1063 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1064 | .SignalStyleSetChangedEvent(parameters.npad_id); | ||
| 1065 | 1053 | ||
| 1066 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 1054 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 1067 | rb.Push(ResultSuccess); | 1055 | rb.Push(ResultSuccess); |
| 1068 | rb.PushCopyObjects(GetResourceManager() | 1056 | rb.PushCopyObjects( |
| 1069 | ->GetController<Controller_NPad>(HidController::NPad) | 1057 | GetResourceManager()->GetNpad()->GetStyleSetChangedEvent(parameters.npad_id)); |
| 1070 | .GetStyleSetChangedEvent(parameters.npad_id)); | ||
| 1071 | } | 1058 | } |
| 1072 | 1059 | ||
| 1073 | void IHidServer::DisconnectNpad(HLERequestContext& ctx) { | 1060 | void IHidServer::DisconnectNpad(HLERequestContext& ctx) { |
| @@ -1081,8 +1068,8 @@ void IHidServer::DisconnectNpad(HLERequestContext& ctx) { | |||
| 1081 | 1068 | ||
| 1082 | const auto parameters{rp.PopRaw<Parameters>()}; | 1069 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1083 | 1070 | ||
| 1084 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1071 | auto controller = GetResourceManager()->GetNpad(); |
| 1085 | controller.DisconnectNpad(parameters.npad_id); | 1072 | controller->DisconnectNpad(parameters.npad_id); |
| 1086 | 1073 | ||
| 1087 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | 1074 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, |
| 1088 | parameters.applet_resource_user_id); | 1075 | parameters.applet_resource_user_id); |
| @@ -1096,8 +1083,8 @@ void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) { | |||
| 1096 | const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; | 1083 | const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; |
| 1097 | 1084 | ||
| 1098 | Core::HID::LedPattern pattern{0, 0, 0, 0}; | 1085 | Core::HID::LedPattern pattern{0, 0, 0, 0}; |
| 1099 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1086 | auto controller = GetResourceManager()->GetNpad(); |
| 1100 | const auto result = controller.GetLedPattern(npad_id, pattern); | 1087 | const auto result = controller->GetLedPattern(npad_id, pattern); |
| 1101 | 1088 | ||
| 1102 | LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); | 1089 | LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); |
| 1103 | 1090 | ||
| @@ -1109,7 +1096,7 @@ void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) { | |||
| 1109 | void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) { | 1096 | void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) { |
| 1110 | IPC::RequestParser rp{ctx}; | 1097 | IPC::RequestParser rp{ctx}; |
| 1111 | struct Parameters { | 1098 | struct Parameters { |
| 1112 | Controller_NPad::NpadRevision revision; | 1099 | NPad::NpadRevision revision; |
| 1113 | INSERT_PADDING_WORDS_NOINIT(1); | 1100 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1114 | u64 applet_resource_user_id; | 1101 | u64 applet_resource_user_id; |
| 1115 | }; | 1102 | }; |
| @@ -1120,10 +1107,10 @@ void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) { | |||
| 1120 | LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision, | 1107 | LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision, |
| 1121 | parameters.applet_resource_user_id); | 1108 | parameters.applet_resource_user_id); |
| 1122 | 1109 | ||
| 1123 | auto& npad = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1110 | auto npad = GetResourceManager()->GetNpad(); |
| 1124 | 1111 | ||
| 1125 | // TODO: npad->SetRevision(applet_resource_user_id, revision); | 1112 | // TODO: npad->SetRevision(applet_resource_user_id, revision); |
| 1126 | const auto result = npad.Activate(parameters.applet_resource_user_id); | 1113 | const auto result = npad->Activate(parameters.applet_resource_user_id); |
| 1127 | 1114 | ||
| 1128 | IPC::ResponseBuilder rb{ctx, 2}; | 1115 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1129 | rb.Push(result); | 1116 | rb.Push(result); |
| @@ -1132,11 +1119,9 @@ void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) { | |||
| 1132 | void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) { | 1119 | void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) { |
| 1133 | IPC::RequestParser rp{ctx}; | 1120 | IPC::RequestParser rp{ctx}; |
| 1134 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1121 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 1135 | const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()}; | 1122 | const auto hold_type{rp.PopEnum<NPad::NpadJoyHoldType>()}; |
| 1136 | 1123 | ||
| 1137 | GetResourceManager() | 1124 | GetResourceManager()->GetNpad()->SetHoldType(hold_type); |
| 1138 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1139 | .SetHoldType(hold_type); | ||
| 1140 | 1125 | ||
| 1141 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", | 1126 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", |
| 1142 | applet_resource_user_id, hold_type); | 1127 | applet_resource_user_id, hold_type); |
| @@ -1153,8 +1138,7 @@ void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) { | |||
| 1153 | 1138 | ||
| 1154 | IPC::ResponseBuilder rb{ctx, 4}; | 1139 | IPC::ResponseBuilder rb{ctx, 4}; |
| 1155 | rb.Push(ResultSuccess); | 1140 | rb.Push(ResultSuccess); |
| 1156 | rb.PushEnum( | 1141 | rb.PushEnum(GetResourceManager()->GetNpad()->GetHoldType()); |
| 1157 | GetResourceManager()->GetController<Controller_NPad>(HidController::NPad).GetHoldType()); | ||
| 1158 | } | 1142 | } |
| 1159 | 1143 | ||
| 1160 | void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) { | 1144 | void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) { |
| @@ -1169,10 +1153,9 @@ void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) | |||
| 1169 | const auto parameters{rp.PopRaw<Parameters>()}; | 1153 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1170 | 1154 | ||
| 1171 | Core::HID::NpadIdType new_npad_id{}; | 1155 | Core::HID::NpadIdType new_npad_id{}; |
| 1172 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1156 | auto controller = GetResourceManager()->GetNpad(); |
| 1173 | controller.SetNpadMode(new_npad_id, parameters.npad_id, | 1157 | controller->SetNpadMode(new_npad_id, parameters.npad_id, NPad::NpadJoyDeviceType::Left, |
| 1174 | Controller_NPad::NpadJoyDeviceType::Left, | 1158 | NPad::NpadJoyAssignmentMode::Single); |
| 1175 | Controller_NPad::NpadJoyAssignmentMode::Single); | ||
| 1176 | 1159 | ||
| 1177 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | 1160 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, |
| 1178 | parameters.applet_resource_user_id); | 1161 | parameters.applet_resource_user_id); |
| @@ -1187,16 +1170,16 @@ void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) { | |||
| 1187 | Core::HID::NpadIdType npad_id; | 1170 | Core::HID::NpadIdType npad_id; |
| 1188 | INSERT_PADDING_WORDS_NOINIT(1); | 1171 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1189 | u64 applet_resource_user_id; | 1172 | u64 applet_resource_user_id; |
| 1190 | Controller_NPad::NpadJoyDeviceType npad_joy_device_type; | 1173 | NPad::NpadJoyDeviceType npad_joy_device_type; |
| 1191 | }; | 1174 | }; |
| 1192 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | 1175 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); |
| 1193 | 1176 | ||
| 1194 | const auto parameters{rp.PopRaw<Parameters>()}; | 1177 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1195 | 1178 | ||
| 1196 | Core::HID::NpadIdType new_npad_id{}; | 1179 | Core::HID::NpadIdType new_npad_id{}; |
| 1197 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1180 | auto controller = GetResourceManager()->GetNpad(); |
| 1198 | controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, | 1181 | controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, |
| 1199 | Controller_NPad::NpadJoyAssignmentMode::Single); | 1182 | NPad::NpadJoyAssignmentMode::Single); |
| 1200 | 1183 | ||
| 1201 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", | 1184 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", |
| 1202 | parameters.npad_id, parameters.applet_resource_user_id, | 1185 | parameters.npad_id, parameters.applet_resource_user_id, |
| @@ -1218,12 +1201,11 @@ void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) { | |||
| 1218 | const auto parameters{rp.PopRaw<Parameters>()}; | 1201 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1219 | 1202 | ||
| 1220 | Core::HID::NpadIdType new_npad_id{}; | 1203 | Core::HID::NpadIdType new_npad_id{}; |
| 1221 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1204 | auto controller = GetResourceManager()->GetNpad(); |
| 1222 | controller.SetNpadMode(new_npad_id, parameters.npad_id, {}, | 1205 | controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NPad::NpadJoyAssignmentMode::Dual); |
| 1223 | Controller_NPad::NpadJoyAssignmentMode::Dual); | ||
| 1224 | 1206 | ||
| 1225 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, | 1207 | LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, |
| 1226 | parameters.applet_resource_user_id); | 1208 | parameters.applet_resource_user_id); // Spams a lot when controller applet is open |
| 1227 | 1209 | ||
| 1228 | IPC::ResponseBuilder rb{ctx, 2}; | 1210 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1229 | rb.Push(ResultSuccess); | 1211 | rb.Push(ResultSuccess); |
| @@ -1235,8 +1217,8 @@ void IHidServer::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) { | |||
| 1235 | const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; | 1217 | const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; |
| 1236 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1218 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 1237 | 1219 | ||
| 1238 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1220 | auto controller = GetResourceManager()->GetNpad(); |
| 1239 | const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); | 1221 | const auto result = controller->MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); |
| 1240 | 1222 | ||
| 1241 | LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", | 1223 | LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", |
| 1242 | npad_id_1, npad_id_2, applet_resource_user_id); | 1224 | npad_id_1, npad_id_2, applet_resource_user_id); |
| @@ -1249,9 +1231,7 @@ void IHidServer::StartLrAssignmentMode(HLERequestContext& ctx) { | |||
| 1249 | IPC::RequestParser rp{ctx}; | 1231 | IPC::RequestParser rp{ctx}; |
| 1250 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1232 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 1251 | 1233 | ||
| 1252 | GetResourceManager() | 1234 | GetResourceManager()->GetNpad()->StartLRAssignmentMode(); |
| 1253 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1254 | .StartLRAssignmentMode(); | ||
| 1255 | 1235 | ||
| 1256 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 1236 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 1257 | 1237 | ||
| @@ -1263,9 +1243,7 @@ void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) { | |||
| 1263 | IPC::RequestParser rp{ctx}; | 1243 | IPC::RequestParser rp{ctx}; |
| 1264 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1244 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 1265 | 1245 | ||
| 1266 | GetResourceManager() | 1246 | GetResourceManager()->GetNpad()->StopLRAssignmentMode(); |
| 1267 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1268 | .StopLRAssignmentMode(); | ||
| 1269 | 1247 | ||
| 1270 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 1248 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 1271 | 1249 | ||
| @@ -1276,11 +1254,9 @@ void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) { | |||
| 1276 | void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) { | 1254 | void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) { |
| 1277 | IPC::RequestParser rp{ctx}; | 1255 | IPC::RequestParser rp{ctx}; |
| 1278 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1256 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 1279 | const auto activation_mode{rp.PopEnum<Controller_NPad::NpadHandheldActivationMode>()}; | 1257 | const auto activation_mode{rp.PopEnum<NPad::NpadHandheldActivationMode>()}; |
| 1280 | 1258 | ||
| 1281 | GetResourceManager() | 1259 | GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode); |
| 1282 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1283 | .SetNpadHandheldActivationMode(activation_mode); | ||
| 1284 | 1260 | ||
| 1285 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", | 1261 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", |
| 1286 | applet_resource_user_id, activation_mode); | 1262 | applet_resource_user_id, activation_mode); |
| @@ -1297,9 +1273,7 @@ void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) { | |||
| 1297 | 1273 | ||
| 1298 | IPC::ResponseBuilder rb{ctx, 4}; | 1274 | IPC::ResponseBuilder rb{ctx, 4}; |
| 1299 | rb.Push(ResultSuccess); | 1275 | rb.Push(ResultSuccess); |
| 1300 | rb.PushEnum(GetResourceManager() | 1276 | rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode()); |
| 1301 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1302 | .GetNpadHandheldActivationMode()); | ||
| 1303 | } | 1277 | } |
| 1304 | 1278 | ||
| 1305 | void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) { | 1279 | void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) { |
| @@ -1308,8 +1282,8 @@ void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) { | |||
| 1308 | const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; | 1282 | const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; |
| 1309 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1283 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 1310 | 1284 | ||
| 1311 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1285 | auto controller = GetResourceManager()->GetNpad(); |
| 1312 | const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2); | 1286 | const auto result = controller->SwapNpadAssignment(npad_id_1, npad_id_2); |
| 1313 | 1287 | ||
| 1314 | LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", | 1288 | LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", |
| 1315 | npad_id_1, npad_id_2, applet_resource_user_id); | 1289 | npad_id_1, npad_id_2, applet_resource_user_id); |
| @@ -1330,9 +1304,9 @@ void IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& | |||
| 1330 | const auto parameters{rp.PopRaw<Parameters>()}; | 1304 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1331 | 1305 | ||
| 1332 | bool is_enabled = false; | 1306 | bool is_enabled = false; |
| 1333 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1307 | auto controller = GetResourceManager()->GetNpad(); |
| 1334 | const auto result = | 1308 | const auto result = |
| 1335 | controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); | 1309 | controller->IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); |
| 1336 | 1310 | ||
| 1337 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", | 1311 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", |
| 1338 | parameters.npad_id, parameters.applet_resource_user_id); | 1312 | parameters.npad_id, parameters.applet_resource_user_id); |
| @@ -1354,8 +1328,8 @@ void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ct | |||
| 1354 | 1328 | ||
| 1355 | const auto parameters{rp.PopRaw<Parameters>()}; | 1329 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1356 | 1330 | ||
| 1357 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1331 | auto controller = GetResourceManager()->GetNpad(); |
| 1358 | const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled( | 1332 | const auto result = controller->SetUnintendedHomeButtonInputProtectionEnabled( |
| 1359 | parameters.is_enabled, parameters.npad_id); | 1333 | parameters.is_enabled, parameters.npad_id); |
| 1360 | 1334 | ||
| 1361 | LOG_DEBUG(Service_HID, | 1335 | LOG_DEBUG(Service_HID, |
| @@ -1372,17 +1346,17 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext | |||
| 1372 | Core::HID::NpadIdType npad_id; | 1346 | Core::HID::NpadIdType npad_id; |
| 1373 | INSERT_PADDING_WORDS_NOINIT(1); | 1347 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1374 | u64 applet_resource_user_id; | 1348 | u64 applet_resource_user_id; |
| 1375 | Controller_NPad::NpadJoyDeviceType npad_joy_device_type; | 1349 | NPad::NpadJoyDeviceType npad_joy_device_type; |
| 1376 | }; | 1350 | }; |
| 1377 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | 1351 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); |
| 1378 | 1352 | ||
| 1379 | const auto parameters{rp.PopRaw<Parameters>()}; | 1353 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1380 | 1354 | ||
| 1381 | Core::HID::NpadIdType new_npad_id{}; | 1355 | Core::HID::NpadIdType new_npad_id{}; |
| 1382 | auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | 1356 | auto controller = GetResourceManager()->GetNpad(); |
| 1383 | const auto is_reassigned = | 1357 | const auto is_reassigned = |
| 1384 | controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, | 1358 | controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, |
| 1385 | Controller_NPad::NpadJoyAssignmentMode::Single); | 1359 | NPad::NpadJoyAssignmentMode::Single); |
| 1386 | 1360 | ||
| 1387 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", | 1361 | LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", |
| 1388 | parameters.npad_id, parameters.applet_resource_user_id, | 1362 | parameters.npad_id, parameters.applet_resource_user_id, |
| @@ -1405,9 +1379,8 @@ void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) { | |||
| 1405 | 1379 | ||
| 1406 | const auto parameters{rp.PopRaw<Parameters>()}; | 1380 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1407 | 1381 | ||
| 1408 | GetResourceManager() | 1382 | GetResourceManager()->GetNpad()->SetAnalogStickUseCenterClamp( |
| 1409 | ->GetController<Controller_NPad>(HidController::NPad) | 1383 | parameters.analog_stick_use_center_clamp); |
| 1410 | .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp); | ||
| 1411 | 1384 | ||
| 1412 | LOG_WARNING(Service_HID, | 1385 | LOG_WARNING(Service_HID, |
| 1413 | "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", | 1386 | "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", |
| @@ -1451,8 +1424,7 @@ void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) { | |||
| 1451 | void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) { | 1424 | void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) { |
| 1452 | IPC::RequestParser rp{ctx}; | 1425 | IPC::RequestParser rp{ctx}; |
| 1453 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; | 1426 | const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; |
| 1454 | const auto& controller = | 1427 | const auto controller = GetResourceManager()->GetNpad(); |
| 1455 | GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); | ||
| 1456 | 1428 | ||
| 1457 | Core::HID::VibrationDeviceInfo vibration_device_info; | 1429 | Core::HID::VibrationDeviceInfo vibration_device_info; |
| 1458 | bool check_device_index = false; | 1430 | bool check_device_index = false; |
| @@ -1496,7 +1468,7 @@ void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) { | |||
| 1496 | LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", | 1468 | LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", |
| 1497 | vibration_device_info.type, vibration_device_info.position); | 1469 | vibration_device_info.type, vibration_device_info.position); |
| 1498 | 1470 | ||
| 1499 | const auto result = controller.IsDeviceHandleValid(vibration_device_handle); | 1471 | const auto result = IsVibrationHandleValid(vibration_device_handle); |
| 1500 | if (result.IsError()) { | 1472 | if (result.IsError()) { |
| 1501 | IPC::ResponseBuilder rb{ctx, 2}; | 1473 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1502 | rb.Push(result); | 1474 | rb.Push(result); |
| @@ -1520,9 +1492,8 @@ void IHidServer::SendVibrationValue(HLERequestContext& ctx) { | |||
| 1520 | 1492 | ||
| 1521 | const auto parameters{rp.PopRaw<Parameters>()}; | 1493 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1522 | 1494 | ||
| 1523 | GetResourceManager() | 1495 | GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle, |
| 1524 | ->GetController<Controller_NPad>(HidController::NPad) | 1496 | parameters.vibration_value); |
| 1525 | .VibrateController(parameters.vibration_device_handle, parameters.vibration_value); | ||
| 1526 | 1497 | ||
| 1527 | LOG_DEBUG(Service_HID, | 1498 | LOG_DEBUG(Service_HID, |
| 1528 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", | 1499 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", |
| @@ -1553,9 +1524,8 @@ void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) { | |||
| 1553 | 1524 | ||
| 1554 | IPC::ResponseBuilder rb{ctx, 6}; | 1525 | IPC::ResponseBuilder rb{ctx, 6}; |
| 1555 | rb.Push(ResultSuccess); | 1526 | rb.Push(ResultSuccess); |
| 1556 | rb.PushRaw(GetResourceManager() | 1527 | rb.PushRaw( |
| 1557 | ->GetController<Controller_NPad>(HidController::NPad) | 1528 | GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle)); |
| 1558 | .GetLastVibration(parameters.vibration_device_handle)); | ||
| 1559 | } | 1529 | } |
| 1560 | 1530 | ||
| 1561 | void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { | 1531 | void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { |
| @@ -1563,7 +1533,7 @@ void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { | |||
| 1563 | 1533 | ||
| 1564 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 1534 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 1565 | rb.Push(ResultSuccess); | 1535 | rb.Push(ResultSuccess); |
| 1566 | rb.PushIpcInterface<IActiveVibrationDeviceList>(system, resource_manager); | 1536 | rb.PushIpcInterface<IActiveVibrationDeviceList>(system, GetResourceManager()); |
| 1567 | } | 1537 | } |
| 1568 | 1538 | ||
| 1569 | void IHidServer::PermitVibration(HLERequestContext& ctx) { | 1539 | void IHidServer::PermitVibration(HLERequestContext& ctx) { |
| @@ -1606,9 +1576,7 @@ void IHidServer::SendVibrationValues(HLERequestContext& ctx) { | |||
| 1606 | auto vibration_values = std::span( | 1576 | auto vibration_values = std::span( |
| 1607 | reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count); | 1577 | reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count); |
| 1608 | 1578 | ||
| 1609 | GetResourceManager() | 1579 | GetResourceManager()->GetNpad()->VibrateControllers(vibration_device_handles, vibration_values); |
| 1610 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1611 | .VibrateControllers(vibration_device_handles, vibration_values); | ||
| 1612 | 1580 | ||
| 1613 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 1581 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 1614 | 1582 | ||
| @@ -1662,9 +1630,8 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) { | |||
| 1662 | } | 1630 | } |
| 1663 | }(); | 1631 | }(); |
| 1664 | 1632 | ||
| 1665 | GetResourceManager() | 1633 | GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle, |
| 1666 | ->GetController<Controller_NPad>(HidController::NPad) | 1634 | vibration_value); |
| 1667 | .VibrateController(parameters.vibration_device_handle, vibration_value); | ||
| 1668 | 1635 | ||
| 1669 | LOG_DEBUG(Service_HID, | 1636 | LOG_DEBUG(Service_HID, |
| 1670 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " | 1637 | "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " |
| @@ -1688,9 +1655,8 @@ void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) { | |||
| 1688 | 1655 | ||
| 1689 | const auto parameters{rp.PopRaw<Parameters>()}; | 1656 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 1690 | 1657 | ||
| 1691 | const auto last_vibration = GetResourceManager() | 1658 | const auto last_vibration = |
| 1692 | ->GetController<Controller_NPad>(HidController::NPad) | 1659 | GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle); |
| 1693 | .GetLastVibration(parameters.vibration_device_handle); | ||
| 1694 | 1660 | ||
| 1695 | const auto gc_erm_command = [last_vibration] { | 1661 | const auto gc_erm_command = [last_vibration] { |
| 1696 | if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { | 1662 | if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { |
| @@ -1725,9 +1691,7 @@ void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) { | |||
| 1725 | IPC::RequestParser rp{ctx}; | 1691 | IPC::RequestParser rp{ctx}; |
| 1726 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1692 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 1727 | 1693 | ||
| 1728 | GetResourceManager() | 1694 | GetResourceManager()->GetNpad()->SetPermitVibrationSession(true); |
| 1729 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1730 | .SetPermitVibrationSession(true); | ||
| 1731 | 1695 | ||
| 1732 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 1696 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 1733 | 1697 | ||
| @@ -1736,9 +1700,7 @@ void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) { | |||
| 1736 | } | 1700 | } |
| 1737 | 1701 | ||
| 1738 | void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) { | 1702 | void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) { |
| 1739 | GetResourceManager() | 1703 | GetResourceManager()->GetNpad()->SetPermitVibrationSession(false); |
| 1740 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 1741 | .SetPermitVibrationSession(false); | ||
| 1742 | 1704 | ||
| 1743 | LOG_DEBUG(Service_HID, "called"); | 1705 | LOG_DEBUG(Service_HID, "called"); |
| 1744 | 1706 | ||
| @@ -1765,9 +1727,8 @@ void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) { | |||
| 1765 | 1727 | ||
| 1766 | IPC::ResponseBuilder rb{ctx, 3}; | 1728 | IPC::ResponseBuilder rb{ctx, 3}; |
| 1767 | rb.Push(ResultSuccess); | 1729 | rb.Push(ResultSuccess); |
| 1768 | rb.Push(GetResourceManager() | 1730 | rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted( |
| 1769 | ->GetController<Controller_NPad>(HidController::NPad) | 1731 | parameters.vibration_device_handle)); |
| 1770 | .IsVibrationDeviceMounted(parameters.vibration_device_handle)); | ||
| 1771 | } | 1732 | } |
| 1772 | 1733 | ||
| 1773 | void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { | 1734 | void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { |
| @@ -1777,15 +1738,14 @@ void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { | |||
| 1777 | LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 1738 | LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 1778 | 1739 | ||
| 1779 | Result result = ResultSuccess; | 1740 | Result result = ResultSuccess; |
| 1780 | auto console_sixaxis = GetResourceManager()->GetController<Controller_ConsoleSixAxis>( | 1741 | auto console_sixaxis = GetResourceManager()->GetConsoleSixAxis(); |
| 1781 | HidController::ConsoleSixAxisSensor); | ||
| 1782 | 1742 | ||
| 1783 | if (!firmware_settings->IsDeviceManaged()) { | 1743 | if (!firmware_settings->IsDeviceManaged()) { |
| 1784 | result = console_sixaxis.Activate(); | 1744 | result = console_sixaxis->Activate(); |
| 1785 | } | 1745 | } |
| 1786 | 1746 | ||
| 1787 | if (result.IsSuccess()) { | 1747 | if (result.IsSuccess()) { |
| 1788 | result = console_sixaxis.Activate(applet_resource_user_id); | 1748 | result = console_sixaxis->Activate(applet_resource_user_id); |
| 1789 | } | 1749 | } |
| 1790 | 1750 | ||
| 1791 | IPC::ResponseBuilder rb{ctx, 2}; | 1751 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -1839,15 +1799,14 @@ void IHidServer::ActivateSevenSixAxisSensor(HLERequestContext& ctx) { | |||
| 1839 | LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 1799 | LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 1840 | 1800 | ||
| 1841 | Result result = ResultSuccess; | 1801 | Result result = ResultSuccess; |
| 1842 | auto console_sixaxis = GetResourceManager()->GetController<Controller_ConsoleSixAxis>( | 1802 | auto seven_sixaxis = GetResourceManager()->GetSevenSixAxis(); |
| 1843 | HidController::ConsoleSixAxisSensor); | ||
| 1844 | 1803 | ||
| 1845 | if (!firmware_settings->IsDeviceManaged()) { | 1804 | if (!firmware_settings->IsDeviceManaged()) { |
| 1846 | result = console_sixaxis.Activate(); | 1805 | result = seven_sixaxis->Activate(); |
| 1847 | } | 1806 | } |
| 1848 | 1807 | ||
| 1849 | if (result.IsSuccess()) { | 1808 | if (result.IsSuccess()) { |
| 1850 | console_sixaxis.Activate(applet_resource_user_id); | 1809 | seven_sixaxis->Activate(applet_resource_user_id); |
| 1851 | } | 1810 | } |
| 1852 | 1811 | ||
| 1853 | IPC::ResponseBuilder rb{ctx, 2}; | 1812 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -1911,13 +1870,10 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) { | |||
| 1911 | ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size"); | 1870 | ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size"); |
| 1912 | 1871 | ||
| 1913 | // Activate console six axis controller | 1872 | // Activate console six axis controller |
| 1914 | GetResourceManager() | 1873 | GetResourceManager()->GetConsoleSixAxis()->Activate(); |
| 1915 | ->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) | 1874 | GetResourceManager()->GetSevenSixAxis()->Activate(); |
| 1916 | .Activate(); | ||
| 1917 | 1875 | ||
| 1918 | GetResourceManager() | 1876 | GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress()); |
| 1919 | ->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) | ||
| 1920 | .SetTransferMemoryAddress(t_mem_1->GetSourceAddress()); | ||
| 1921 | 1877 | ||
| 1922 | LOG_WARNING(Service_HID, | 1878 | LOG_WARNING(Service_HID, |
| 1923 | "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, " | 1879 | "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, " |
| @@ -1943,9 +1899,7 @@ void IHidServer::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) { | |||
| 1943 | IPC::RequestParser rp{ctx}; | 1899 | IPC::RequestParser rp{ctx}; |
| 1944 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 1900 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 1945 | 1901 | ||
| 1946 | GetResourceManager() | 1902 | GetResourceManager()->GetSevenSixAxis()->ResetTimestamp(); |
| 1947 | ->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) | ||
| 1948 | .ResetTimestamp(); | ||
| 1949 | 1903 | ||
| 1950 | LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 1904 | LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 1951 | 1905 | ||
| @@ -1977,9 +1931,9 @@ void IHidServer::GetPalmaConnectionHandle(HLERequestContext& ctx) { | |||
| 1977 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", | 1931 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", |
| 1978 | parameters.npad_id, parameters.applet_resource_user_id); | 1932 | parameters.npad_id, parameters.applet_resource_user_id); |
| 1979 | 1933 | ||
| 1980 | Controller_Palma::PalmaConnectionHandle handle; | 1934 | Palma::PalmaConnectionHandle handle; |
| 1981 | auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); | 1935 | auto controller = GetResourceManager()->GetPalma(); |
| 1982 | const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle); | 1936 | const auto result = controller->GetPalmaConnectionHandle(parameters.npad_id, handle); |
| 1983 | 1937 | ||
| 1984 | IPC::ResponseBuilder rb{ctx, 4}; | 1938 | IPC::ResponseBuilder rb{ctx, 4}; |
| 1985 | rb.Push(result); | 1939 | rb.Push(result); |
| @@ -1988,12 +1942,12 @@ void IHidServer::GetPalmaConnectionHandle(HLERequestContext& ctx) { | |||
| 1988 | 1942 | ||
| 1989 | void IHidServer::InitializePalma(HLERequestContext& ctx) { | 1943 | void IHidServer::InitializePalma(HLERequestContext& ctx) { |
| 1990 | IPC::RequestParser rp{ctx}; | 1944 | IPC::RequestParser rp{ctx}; |
| 1991 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 1945 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 1992 | 1946 | ||
| 1993 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | 1947 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); |
| 1994 | 1948 | ||
| 1995 | auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); | 1949 | auto controller = GetResourceManager()->GetPalma(); |
| 1996 | const auto result = controller.InitializePalma(connection_handle); | 1950 | const auto result = controller->InitializePalma(connection_handle); |
| 1997 | 1951 | ||
| 1998 | IPC::ResponseBuilder rb{ctx, 2}; | 1952 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1999 | rb.Push(result); | 1953 | rb.Push(result); |
| @@ -2001,27 +1955,27 @@ void IHidServer::InitializePalma(HLERequestContext& ctx) { | |||
| 2001 | 1955 | ||
| 2002 | void IHidServer::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) { | 1956 | void IHidServer::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) { |
| 2003 | IPC::RequestParser rp{ctx}; | 1957 | IPC::RequestParser rp{ctx}; |
| 2004 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 1958 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2005 | 1959 | ||
| 2006 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | 1960 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); |
| 2007 | 1961 | ||
| 2008 | auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); | 1962 | auto controller = GetResourceManager()->GetPalma(); |
| 2009 | 1963 | ||
| 2010 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 1964 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 2011 | rb.Push(ResultSuccess); | 1965 | rb.Push(ResultSuccess); |
| 2012 | rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle)); | 1966 | rb.PushCopyObjects(controller->AcquirePalmaOperationCompleteEvent(connection_handle)); |
| 2013 | } | 1967 | } |
| 2014 | 1968 | ||
| 2015 | void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) { | 1969 | void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) { |
| 2016 | IPC::RequestParser rp{ctx}; | 1970 | IPC::RequestParser rp{ctx}; |
| 2017 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 1971 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2018 | 1972 | ||
| 2019 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | 1973 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); |
| 2020 | 1974 | ||
| 2021 | Controller_Palma::PalmaOperationType operation_type; | 1975 | Palma::PalmaOperationType operation_type; |
| 2022 | Controller_Palma::PalmaOperationData data; | 1976 | Palma::PalmaOperationData data; |
| 2023 | auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); | 1977 | auto controller = GetResourceManager()->GetPalma(); |
| 2024 | const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data); | 1978 | const auto result = controller->GetPalmaOperationInfo(connection_handle, operation_type, data); |
| 2025 | 1979 | ||
| 2026 | if (result.IsError()) { | 1980 | if (result.IsError()) { |
| 2027 | IPC::ResponseBuilder rb{ctx, 2}; | 1981 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -2036,14 +1990,14 @@ void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) { | |||
| 2036 | 1990 | ||
| 2037 | void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) { | 1991 | void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) { |
| 2038 | IPC::RequestParser rp{ctx}; | 1992 | IPC::RequestParser rp{ctx}; |
| 2039 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 1993 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2040 | const auto palma_activity{rp.Pop<u64>()}; | 1994 | const auto palma_activity{rp.Pop<u64>()}; |
| 2041 | 1995 | ||
| 2042 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", | 1996 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", |
| 2043 | connection_handle.npad_id, palma_activity); | 1997 | connection_handle.npad_id, palma_activity); |
| 2044 | 1998 | ||
| 2045 | auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); | 1999 | auto controller = GetResourceManager()->GetPalma(); |
| 2046 | const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity); | 2000 | const auto result = controller->PlayPalmaActivity(connection_handle, palma_activity); |
| 2047 | 2001 | ||
| 2048 | IPC::ResponseBuilder rb{ctx, 2}; | 2002 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2049 | rb.Push(result); | 2003 | rb.Push(result); |
| @@ -2051,14 +2005,14 @@ void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) { | |||
| 2051 | 2005 | ||
| 2052 | void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) { | 2006 | void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) { |
| 2053 | IPC::RequestParser rp{ctx}; | 2007 | IPC::RequestParser rp{ctx}; |
| 2054 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 2008 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2055 | const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()}; | 2009 | const auto fr_mode{rp.PopEnum<Palma::PalmaFrModeType>()}; |
| 2056 | 2010 | ||
| 2057 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", | 2011 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", |
| 2058 | connection_handle.npad_id, fr_mode); | 2012 | connection_handle.npad_id, fr_mode); |
| 2059 | 2013 | ||
| 2060 | auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); | 2014 | auto controller = GetResourceManager()->GetPalma(); |
| 2061 | const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode); | 2015 | const auto result = controller->SetPalmaFrModeType(connection_handle, fr_mode); |
| 2062 | 2016 | ||
| 2063 | IPC::ResponseBuilder rb{ctx, 2}; | 2017 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2064 | rb.Push(result); | 2018 | rb.Push(result); |
| @@ -2066,12 +2020,12 @@ void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) { | |||
| 2066 | 2020 | ||
| 2067 | void IHidServer::ReadPalmaStep(HLERequestContext& ctx) { | 2021 | void IHidServer::ReadPalmaStep(HLERequestContext& ctx) { |
| 2068 | IPC::RequestParser rp{ctx}; | 2022 | IPC::RequestParser rp{ctx}; |
| 2069 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 2023 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2070 | 2024 | ||
| 2071 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | 2025 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); |
| 2072 | 2026 | ||
| 2073 | auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); | 2027 | auto controller = GetResourceManager()->GetPalma(); |
| 2074 | const auto result = controller.ReadPalmaStep(connection_handle); | 2028 | const auto result = controller->ReadPalmaStep(connection_handle); |
| 2075 | 2029 | ||
| 2076 | IPC::ResponseBuilder rb{ctx, 2}; | 2030 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2077 | rb.Push(result); | 2031 | rb.Push(result); |
| @@ -2082,7 +2036,7 @@ void IHidServer::EnablePalmaStep(HLERequestContext& ctx) { | |||
| 2082 | struct Parameters { | 2036 | struct Parameters { |
| 2083 | bool is_enabled; | 2037 | bool is_enabled; |
| 2084 | INSERT_PADDING_WORDS_NOINIT(1); | 2038 | INSERT_PADDING_WORDS_NOINIT(1); |
| 2085 | Controller_Palma::PalmaConnectionHandle connection_handle; | 2039 | Palma::PalmaConnectionHandle connection_handle; |
| 2086 | }; | 2040 | }; |
| 2087 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | 2041 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); |
| 2088 | 2042 | ||
| @@ -2091,9 +2045,9 @@ void IHidServer::EnablePalmaStep(HLERequestContext& ctx) { | |||
| 2091 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", | 2045 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", |
| 2092 | parameters.connection_handle.npad_id, parameters.is_enabled); | 2046 | parameters.connection_handle.npad_id, parameters.is_enabled); |
| 2093 | 2047 | ||
| 2094 | auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); | 2048 | auto controller = GetResourceManager()->GetPalma(); |
| 2095 | const auto result = | 2049 | const auto result = |
| 2096 | controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); | 2050 | controller->EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); |
| 2097 | 2051 | ||
| 2098 | IPC::ResponseBuilder rb{ctx, 2}; | 2052 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2099 | rb.Push(result); | 2053 | rb.Push(result); |
| @@ -2101,12 +2055,12 @@ void IHidServer::EnablePalmaStep(HLERequestContext& ctx) { | |||
| 2101 | 2055 | ||
| 2102 | void IHidServer::ResetPalmaStep(HLERequestContext& ctx) { | 2056 | void IHidServer::ResetPalmaStep(HLERequestContext& ctx) { |
| 2103 | IPC::RequestParser rp{ctx}; | 2057 | IPC::RequestParser rp{ctx}; |
| 2104 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 2058 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2105 | 2059 | ||
| 2106 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | 2060 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); |
| 2107 | 2061 | ||
| 2108 | auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); | 2062 | auto controller = GetResourceManager()->GetPalma(); |
| 2109 | const auto result = controller.ResetPalmaStep(connection_handle); | 2063 | const auto result = controller->ResetPalmaStep(connection_handle); |
| 2110 | 2064 | ||
| 2111 | IPC::ResponseBuilder rb{ctx, 2}; | 2065 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2112 | rb.Push(result); | 2066 | rb.Push(result); |
| @@ -2128,13 +2082,11 @@ void IHidServer::WritePalmaApplicationSection(HLERequestContext& ctx) { | |||
| 2128 | 2082 | ||
| 2129 | void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) { | 2083 | void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) { |
| 2130 | IPC::RequestParser rp{ctx}; | 2084 | IPC::RequestParser rp{ctx}; |
| 2131 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 2085 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2132 | 2086 | ||
| 2133 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | 2087 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); |
| 2134 | 2088 | ||
| 2135 | GetResourceManager() | 2089 | GetResourceManager()->GetPalma()->ReadPalmaUniqueCode(connection_handle); |
| 2136 | ->GetController<Controller_Palma>(HidController::Palma) | ||
| 2137 | .ReadPalmaUniqueCode(connection_handle); | ||
| 2138 | 2090 | ||
| 2139 | IPC::ResponseBuilder rb{ctx, 2}; | 2091 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2140 | rb.Push(ResultSuccess); | 2092 | rb.Push(ResultSuccess); |
| @@ -2142,13 +2094,11 @@ void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) { | |||
| 2142 | 2094 | ||
| 2143 | void IHidServer::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) { | 2095 | void IHidServer::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) { |
| 2144 | IPC::RequestParser rp{ctx}; | 2096 | IPC::RequestParser rp{ctx}; |
| 2145 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 2097 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2146 | 2098 | ||
| 2147 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | 2099 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); |
| 2148 | 2100 | ||
| 2149 | GetResourceManager() | 2101 | GetResourceManager()->GetPalma()->SetPalmaUniqueCodeInvalid(connection_handle); |
| 2150 | ->GetController<Controller_Palma>(HidController::Palma) | ||
| 2151 | .SetPalmaUniqueCodeInvalid(connection_handle); | ||
| 2152 | 2102 | ||
| 2153 | IPC::ResponseBuilder rb{ctx, 2}; | 2103 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2154 | rb.Push(ResultSuccess); | 2104 | rb.Push(ResultSuccess); |
| @@ -2163,7 +2113,7 @@ void IHidServer::WritePalmaActivityEntry(HLERequestContext& ctx) { | |||
| 2163 | 2113 | ||
| 2164 | void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) { | 2114 | void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) { |
| 2165 | IPC::RequestParser rp{ctx}; | 2115 | IPC::RequestParser rp{ctx}; |
| 2166 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 2116 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2167 | const auto unknown{rp.Pop<u64>()}; | 2117 | const auto unknown{rp.Pop<u64>()}; |
| 2168 | 2118 | ||
| 2169 | [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); | 2119 | [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); |
| @@ -2171,9 +2121,7 @@ void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) { | |||
| 2171 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", | 2121 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", |
| 2172 | connection_handle.npad_id, unknown); | 2122 | connection_handle.npad_id, unknown); |
| 2173 | 2123 | ||
| 2174 | GetResourceManager() | 2124 | GetResourceManager()->GetPalma()->WritePalmaRgbLedPatternEntry(connection_handle, unknown); |
| 2175 | ->GetController<Controller_Palma>(HidController::Palma) | ||
| 2176 | .WritePalmaRgbLedPatternEntry(connection_handle, unknown); | ||
| 2177 | 2125 | ||
| 2178 | IPC::ResponseBuilder rb{ctx, 2}; | 2126 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2179 | rb.Push(ResultSuccess); | 2127 | rb.Push(ResultSuccess); |
| @@ -2181,8 +2129,8 @@ void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) { | |||
| 2181 | 2129 | ||
| 2182 | void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) { | 2130 | void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) { |
| 2183 | IPC::RequestParser rp{ctx}; | 2131 | IPC::RequestParser rp{ctx}; |
| 2184 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 2132 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2185 | const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()}; | 2133 | const auto wave_set{rp.PopEnum<Palma::PalmaWaveSet>()}; |
| 2186 | const auto unknown{rp.Pop<u64>()}; | 2134 | const auto unknown{rp.Pop<u64>()}; |
| 2187 | const auto t_mem_size{rp.Pop<u64>()}; | 2135 | const auto t_mem_size{rp.Pop<u64>()}; |
| 2188 | const auto t_mem_handle{ctx.GetCopyHandle(0)}; | 2136 | const auto t_mem_handle{ctx.GetCopyHandle(0)}; |
| @@ -2207,9 +2155,8 @@ void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) { | |||
| 2207 | "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", | 2155 | "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", |
| 2208 | connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); | 2156 | connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); |
| 2209 | 2157 | ||
| 2210 | GetResourceManager() | 2158 | GetResourceManager()->GetPalma()->WritePalmaWaveEntry(connection_handle, wave_set, |
| 2211 | ->GetController<Controller_Palma>(HidController::Palma) | 2159 | t_mem->GetSourceAddress(), t_mem_size); |
| 2212 | .WritePalmaWaveEntry(connection_handle, wave_set, t_mem->GetSourceAddress(), t_mem_size); | ||
| 2213 | 2160 | ||
| 2214 | IPC::ResponseBuilder rb{ctx, 2}; | 2161 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2215 | rb.Push(ResultSuccess); | 2162 | rb.Push(ResultSuccess); |
| @@ -2220,7 +2167,7 @@ void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { | |||
| 2220 | struct Parameters { | 2167 | struct Parameters { |
| 2221 | s32 database_id_version; | 2168 | s32 database_id_version; |
| 2222 | INSERT_PADDING_WORDS_NOINIT(1); | 2169 | INSERT_PADDING_WORDS_NOINIT(1); |
| 2223 | Controller_Palma::PalmaConnectionHandle connection_handle; | 2170 | Palma::PalmaConnectionHandle connection_handle; |
| 2224 | }; | 2171 | }; |
| 2225 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | 2172 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); |
| 2226 | 2173 | ||
| @@ -2229,10 +2176,8 @@ void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { | |||
| 2229 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", | 2176 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", |
| 2230 | parameters.connection_handle.npad_id, parameters.database_id_version); | 2177 | parameters.connection_handle.npad_id, parameters.database_id_version); |
| 2231 | 2178 | ||
| 2232 | GetResourceManager() | 2179 | GetResourceManager()->GetPalma()->SetPalmaDataBaseIdentificationVersion( |
| 2233 | ->GetController<Controller_Palma>(HidController::Palma) | 2180 | parameters.connection_handle, parameters.database_id_version); |
| 2234 | .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle, | ||
| 2235 | parameters.database_id_version); | ||
| 2236 | 2181 | ||
| 2237 | IPC::ResponseBuilder rb{ctx, 2}; | 2182 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2238 | rb.Push(ResultSuccess); | 2183 | rb.Push(ResultSuccess); |
| @@ -2240,13 +2185,11 @@ void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { | |||
| 2240 | 2185 | ||
| 2241 | void IHidServer::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { | 2186 | void IHidServer::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { |
| 2242 | IPC::RequestParser rp{ctx}; | 2187 | IPC::RequestParser rp{ctx}; |
| 2243 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 2188 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2244 | 2189 | ||
| 2245 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | 2190 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); |
| 2246 | 2191 | ||
| 2247 | GetResourceManager() | 2192 | GetResourceManager()->GetPalma()->GetPalmaDataBaseIdentificationVersion(connection_handle); |
| 2248 | ->GetController<Controller_Palma>(HidController::Palma) | ||
| 2249 | .GetPalmaDataBaseIdentificationVersion(connection_handle); | ||
| 2250 | 2193 | ||
| 2251 | IPC::ResponseBuilder rb{ctx, 2}; | 2194 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2252 | rb.Push(ResultSuccess); | 2195 | rb.Push(ResultSuccess); |
| @@ -2261,13 +2204,12 @@ void IHidServer::SuspendPalmaFeature(HLERequestContext& ctx) { | |||
| 2261 | 2204 | ||
| 2262 | void IHidServer::GetPalmaOperationResult(HLERequestContext& ctx) { | 2205 | void IHidServer::GetPalmaOperationResult(HLERequestContext& ctx) { |
| 2263 | IPC::RequestParser rp{ctx}; | 2206 | IPC::RequestParser rp{ctx}; |
| 2264 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 2207 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2265 | 2208 | ||
| 2266 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | 2209 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); |
| 2267 | 2210 | ||
| 2268 | const auto result = GetResourceManager() | 2211 | const auto result = |
| 2269 | ->GetController<Controller_Palma>(HidController::Palma) | 2212 | GetResourceManager()->GetPalma()->GetPalmaOperationResult(connection_handle); |
| 2270 | .GetPalmaOperationResult(connection_handle); | ||
| 2271 | 2213 | ||
| 2272 | IPC::ResponseBuilder rb{ctx, 2}; | 2214 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2273 | rb.Push(result); | 2215 | rb.Push(result); |
| @@ -2302,9 +2244,7 @@ void IHidServer::SetIsPalmaAllConnectable(HLERequestContext& ctx) { | |||
| 2302 | "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", | 2244 | "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", |
| 2303 | parameters.is_palma_all_connectable, parameters.applet_resource_user_id); | 2245 | parameters.is_palma_all_connectable, parameters.applet_resource_user_id); |
| 2304 | 2246 | ||
| 2305 | GetResourceManager() | 2247 | GetResourceManager()->GetPalma()->SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); |
| 2306 | ->GetController<Controller_Palma>(HidController::Palma) | ||
| 2307 | .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); | ||
| 2308 | 2248 | ||
| 2309 | IPC::ResponseBuilder rb{ctx, 2}; | 2249 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2310 | rb.Push(ResultSuccess); | 2250 | rb.Push(ResultSuccess); |
| @@ -2319,13 +2259,11 @@ void IHidServer::SetIsPalmaPairedConnectable(HLERequestContext& ctx) { | |||
| 2319 | 2259 | ||
| 2320 | void IHidServer::PairPalma(HLERequestContext& ctx) { | 2260 | void IHidServer::PairPalma(HLERequestContext& ctx) { |
| 2321 | IPC::RequestParser rp{ctx}; | 2261 | IPC::RequestParser rp{ctx}; |
| 2322 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | 2262 | const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()}; |
| 2323 | 2263 | ||
| 2324 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | 2264 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); |
| 2325 | 2265 | ||
| 2326 | GetResourceManager() | 2266 | GetResourceManager()->GetPalma()->PairPalma(connection_handle); |
| 2327 | ->GetController<Controller_Palma>(HidController::Palma) | ||
| 2328 | .PairPalma(connection_handle); | ||
| 2329 | 2267 | ||
| 2330 | IPC::ResponseBuilder rb{ctx, 2}; | 2268 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2331 | rb.Push(ResultSuccess); | 2269 | rb.Push(ResultSuccess); |
| @@ -2337,9 +2275,7 @@ void IHidServer::SetPalmaBoostMode(HLERequestContext& ctx) { | |||
| 2337 | 2275 | ||
| 2338 | LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); | 2276 | LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); |
| 2339 | 2277 | ||
| 2340 | GetResourceManager() | 2278 | GetResourceManager()->GetPalma()->SetPalmaBoostMode(palma_boost_mode); |
| 2341 | ->GetController<Controller_Palma>(HidController::Palma) | ||
| 2342 | .SetPalmaBoostMode(palma_boost_mode); | ||
| 2343 | 2279 | ||
| 2344 | IPC::ResponseBuilder rb{ctx, 2}; | 2280 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2345 | rb.Push(ResultSuccess); | 2281 | rb.Push(ResultSuccess); |
| @@ -2376,11 +2312,9 @@ void IHidServer::SetDisallowedPalmaConnection(HLERequestContext& ctx) { | |||
| 2376 | void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) { | 2312 | void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) { |
| 2377 | IPC::RequestParser rp{ctx}; | 2313 | IPC::RequestParser rp{ctx}; |
| 2378 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 2314 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 2379 | const auto communication_mode{rp.PopEnum<Controller_NPad::NpadCommunicationMode>()}; | 2315 | const auto communication_mode{rp.PopEnum<NPad::NpadCommunicationMode>()}; |
| 2380 | 2316 | ||
| 2381 | GetResourceManager() | 2317 | GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode); |
| 2382 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 2383 | .SetNpadCommunicationMode(communication_mode); | ||
| 2384 | 2318 | ||
| 2385 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}", | 2319 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}", |
| 2386 | applet_resource_user_id, communication_mode); | 2320 | applet_resource_user_id, communication_mode); |
| @@ -2396,9 +2330,7 @@ void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) { | |||
| 2396 | 2330 | ||
| 2397 | IPC::ResponseBuilder rb{ctx, 4}; | 2331 | IPC::ResponseBuilder rb{ctx, 4}; |
| 2398 | rb.Push(ResultSuccess); | 2332 | rb.Push(ResultSuccess); |
| 2399 | rb.PushEnum(GetResourceManager() | 2333 | rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadCommunicationMode()); |
| 2400 | ->GetController<Controller_NPad>(HidController::NPad) | ||
| 2401 | .GetNpadCommunicationMode()); | ||
| 2402 | } | 2334 | } |
| 2403 | 2335 | ||
| 2404 | void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { | 2336 | void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { |
| @@ -2432,6 +2364,21 @@ void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) { | |||
| 2432 | rb.Push(false); | 2364 | rb.Push(false); |
| 2433 | } | 2365 | } |
| 2434 | 2366 | ||
| 2367 | void IHidServer::SetTouchScreenResolution(HLERequestContext& ctx) { | ||
| 2368 | IPC::RequestParser rp{ctx}; | ||
| 2369 | const auto width{rp.Pop<u32>()}; | ||
| 2370 | const auto height{rp.Pop<u32>()}; | ||
| 2371 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 2372 | |||
| 2373 | GetResourceManager()->GetTouchScreen()->SetTouchscreenDimensions(width, height); | ||
| 2374 | |||
| 2375 | LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height, | ||
| 2376 | applet_resource_user_id); | ||
| 2377 | |||
| 2378 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2379 | rb.Push(ResultSuccess); | ||
| 2380 | } | ||
| 2381 | |||
| 2435 | std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() { | 2382 | std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() { |
| 2436 | resource_manager->Initialize(); | 2383 | resource_manager->Initialize(); |
| 2437 | return resource_manager; | 2384 | return resource_manager; |
diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h index eb2e8e7f4..cc7c4ebdd 100644 --- a/src/core/hle/service/hid/hid_server.h +++ b/src/core/hle/service/hid/hid_server.h | |||
| @@ -141,6 +141,7 @@ private: | |||
| 141 | void GetNpadCommunicationMode(HLERequestContext& ctx); | 141 | void GetNpadCommunicationMode(HLERequestContext& ctx); |
| 142 | void SetTouchScreenConfiguration(HLERequestContext& ctx); | 142 | void SetTouchScreenConfiguration(HLERequestContext& ctx); |
| 143 | void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx); | 143 | void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx); |
| 144 | void SetTouchScreenResolution(HLERequestContext& ctx); | ||
| 144 | 145 | ||
| 145 | std::shared_ptr<ResourceManager> resource_manager; | 146 | std::shared_ptr<ResourceManager> resource_manager; |
| 146 | std::shared_ptr<HidFirmwareSettings> firmware_settings; | 147 | std::shared_ptr<HidFirmwareSettings> firmware_settings; |
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index 83cfadada..b56d0347a 100644 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp | |||
| @@ -16,251 +16,461 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour | |||
| 16 | resource_manager{resource} { | 16 | resource_manager{resource} { |
| 17 | // clang-format off | 17 | // clang-format off |
| 18 | static const FunctionInfo functions[] = { | 18 | static const FunctionInfo functions[] = { |
| 19 | {31, nullptr, "SendKeyboardLockKeyEvent"}, | 19 | {31, nullptr, "SendKeyboardLockKeyEvent"}, |
| 20 | {101, nullptr, "AcquireHomeButtonEventHandle"}, | 20 | {101, nullptr, "AcquireHomeButtonEventHandle"}, |
| 21 | {111, nullptr, "ActivateHomeButton"}, | 21 | {111, nullptr, "ActivateHomeButton"}, |
| 22 | {121, nullptr, "AcquireSleepButtonEventHandle"}, | 22 | {121, nullptr, "AcquireSleepButtonEventHandle"}, |
| 23 | {131, nullptr, "ActivateSleepButton"}, | 23 | {131, nullptr, "ActivateSleepButton"}, |
| 24 | {141, nullptr, "AcquireCaptureButtonEventHandle"}, | 24 | {141, nullptr, "AcquireCaptureButtonEventHandle"}, |
| 25 | {151, nullptr, "ActivateCaptureButton"}, | 25 | {151, nullptr, "ActivateCaptureButton"}, |
| 26 | {161, nullptr, "GetPlatformConfig"}, | 26 | {161, nullptr, "GetPlatformConfig"}, |
| 27 | {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, | 27 | {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, |
| 28 | {211, nullptr, "GetNpadsWithNfc"}, | 28 | {211, nullptr, "GetNpadsWithNfc"}, |
| 29 | {212, nullptr, "AcquireNfcActivateEventHandle"}, | 29 | {212, nullptr, "AcquireNfcActivateEventHandle"}, |
| 30 | {213, nullptr, "ActivateNfc"}, | 30 | {213, nullptr, "ActivateNfc"}, |
| 31 | {214, nullptr, "GetXcdHandleForNpadWithNfc"}, | 31 | {214, nullptr, "GetXcdHandleForNpadWithNfc"}, |
| 32 | {215, nullptr, "IsNfcActivated"}, | 32 | {215, nullptr, "IsNfcActivated"}, |
| 33 | {230, nullptr, "AcquireIrSensorEventHandle"}, | 33 | {230, nullptr, "AcquireIrSensorEventHandle"}, |
| 34 | {231, nullptr, "ActivateIrSensor"}, | 34 | {231, nullptr, "ActivateIrSensor"}, |
| 35 | {232, nullptr, "GetIrSensorState"}, | 35 | {232, nullptr, "GetIrSensorState"}, |
| 36 | {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, | 36 | {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, |
| 37 | {301, nullptr, "ActivateNpadSystem"}, | 37 | {301, nullptr, "ActivateNpadSystem"}, |
| 38 | {303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, | 38 | {303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, |
| 39 | {304, nullptr, "EnableAssigningSingleOnSlSrPress"}, | 39 | {304, &IHidSystemServer::EnableAssigningSingleOnSlSrPress, "EnableAssigningSingleOnSlSrPress"}, |
| 40 | {305, nullptr, "DisableAssigningSingleOnSlSrPress"}, | 40 | {305, &IHidSystemServer::DisableAssigningSingleOnSlSrPress, "DisableAssigningSingleOnSlSrPress"}, |
| 41 | {306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"}, | 41 | {306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"}, |
| 42 | {307, nullptr, "GetNpadSystemExtStyle"}, | 42 | {307, nullptr, "GetNpadSystemExtStyle"}, |
| 43 | {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, | 43 | {308, &IHidSystemServer::ApplyNpadSystemCommonPolicyFull, "ApplyNpadSystemCommonPolicyFull"}, |
| 44 | {309, nullptr, "GetNpadFullKeyGripColor"}, | 44 | {309, &IHidSystemServer::GetNpadFullKeyGripColor, "GetNpadFullKeyGripColor"}, |
| 45 | {310, nullptr, "GetMaskedSupportedNpadStyleSet"}, | 45 | {310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"}, |
| 46 | {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, | 46 | {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, |
| 47 | {312, nullptr, "SetSupportedNpadStyleSetAll"}, | 47 | {312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"}, |
| 48 | {313, nullptr, "GetNpadCaptureButtonAssignment"}, | 48 | {313, nullptr, "GetNpadCaptureButtonAssignment"}, |
| 49 | {314, nullptr, "GetAppletFooterUiType"}, | 49 | {314, nullptr, "GetAppletFooterUiType"}, |
| 50 | {315, nullptr, "GetAppletDetailedUiType"}, | 50 | {315, &IHidSystemServer::GetAppletDetailedUiType, "GetAppletDetailedUiType"}, |
| 51 | {316, nullptr, "GetNpadInterfaceType"}, | 51 | {316, &IHidSystemServer::GetNpadInterfaceType, "GetNpadInterfaceType"}, |
| 52 | {317, nullptr, "GetNpadLeftRightInterfaceType"}, | 52 | {317, &IHidSystemServer::GetNpadLeftRightInterfaceType, "GetNpadLeftRightInterfaceType"}, |
| 53 | {318, nullptr, "HasBattery"}, | 53 | {318, &IHidSystemServer::HasBattery, "HasBattery"}, |
| 54 | {319, nullptr, "HasLeftRightBattery"}, | 54 | {319, &IHidSystemServer::HasLeftRightBattery, "HasLeftRightBattery"}, |
| 55 | {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"}, | 55 | {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"}, |
| 56 | {322, nullptr, "GetIrSensorState"}, | 56 | {322, &IHidSystemServer::GetIrSensorState, "GetIrSensorState"}, |
| 57 | {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, | 57 | {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, |
| 58 | {324, nullptr, "GetUniquePadButtonSet"}, | 58 | {324, nullptr, "GetUniquePadButtonSet"}, |
| 59 | {325, nullptr, "GetUniquePadColor"}, | 59 | {325, nullptr, "GetUniquePadColor"}, |
| 60 | {326, nullptr, "GetUniquePadAppletDetailedUiType"}, | 60 | {326, nullptr, "GetUniquePadAppletDetailedUiType"}, |
| 61 | {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, | 61 | {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, |
| 62 | {328, nullptr, "AttachAbstractedPadToNpad"}, | 62 | {328, nullptr, "AttachAbstractedPadToNpad"}, |
| 63 | {329, nullptr, "DetachAbstractedPadAll"}, | 63 | {329, nullptr, "DetachAbstractedPadAll"}, |
| 64 | {330, nullptr, "CheckAbstractedPadConnection"}, | 64 | {330, nullptr, "CheckAbstractedPadConnection"}, |
| 65 | {500, nullptr, "SetAppletResourceUserId"}, | 65 | {500, nullptr, "SetAppletResourceUserId"}, |
| 66 | {501, nullptr, "RegisterAppletResourceUserId"}, | 66 | {501, nullptr, "RegisterAppletResourceUserId"}, |
| 67 | {502, nullptr, "UnregisterAppletResourceUserId"}, | 67 | {502, nullptr, "UnregisterAppletResourceUserId"}, |
| 68 | {503, nullptr, "EnableAppletToGetInput"}, | 68 | {503, nullptr, "EnableAppletToGetInput"}, |
| 69 | {504, nullptr, "SetAruidValidForVibration"}, | 69 | {504, nullptr, "SetAruidValidForVibration"}, |
| 70 | {505, nullptr, "EnableAppletToGetSixAxisSensor"}, | 70 | {505, nullptr, "EnableAppletToGetSixAxisSensor"}, |
| 71 | {506, nullptr, "EnableAppletToGetPadInput"}, | 71 | {506, nullptr, "EnableAppletToGetPadInput"}, |
| 72 | {507, nullptr, "EnableAppletToGetTouchScreen"}, | 72 | {507, nullptr, "EnableAppletToGetTouchScreen"}, |
| 73 | {510, nullptr, "SetVibrationMasterVolume"}, | 73 | {510, nullptr, "SetVibrationMasterVolume"}, |
| 74 | {511, nullptr, "GetVibrationMasterVolume"}, | 74 | {511, nullptr, "GetVibrationMasterVolume"}, |
| 75 | {512, nullptr, "BeginPermitVibrationSession"}, | 75 | {512, nullptr, "BeginPermitVibrationSession"}, |
| 76 | {513, nullptr, "EndPermitVibrationSession"}, | 76 | {513, nullptr, "EndPermitVibrationSession"}, |
| 77 | {514, nullptr, "Unknown514"}, | 77 | {514, nullptr, "Unknown514"}, |
| 78 | {520, nullptr, "EnableHandheldHids"}, | 78 | {520, nullptr, "EnableHandheldHids"}, |
| 79 | {521, nullptr, "DisableHandheldHids"}, | 79 | {521, nullptr, "DisableHandheldHids"}, |
| 80 | {522, nullptr, "SetJoyConRailEnabled"}, | 80 | {522, nullptr, "SetJoyConRailEnabled"}, |
| 81 | {523, nullptr, "IsJoyConRailEnabled"}, | 81 | {523, nullptr, "IsJoyConRailEnabled"}, |
| 82 | {524, nullptr, "IsHandheldHidsEnabled"}, | 82 | {524, nullptr, "IsHandheldHidsEnabled"}, |
| 83 | {525, nullptr, "IsJoyConAttachedOnAllRail"}, | 83 | {525, nullptr, "IsJoyConAttachedOnAllRail"}, |
| 84 | {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, | 84 | {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, |
| 85 | {541, nullptr, "GetPlayReportControllerUsages"}, | 85 | {541, nullptr, "GetPlayReportControllerUsages"}, |
| 86 | {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, | 86 | {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, |
| 87 | {543, nullptr, "GetRegisteredDevicesOld"}, | 87 | {543, nullptr, "GetRegisteredDevicesOld"}, |
| 88 | {544, nullptr, "AcquireConnectionTriggerTimeoutEvent"}, | 88 | {544, &IHidSystemServer::AcquireConnectionTriggerTimeoutEvent, "AcquireConnectionTriggerTimeoutEvent"}, |
| 89 | {545, nullptr, "SendConnectionTrigger"}, | 89 | {545, nullptr, "SendConnectionTrigger"}, |
| 90 | {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, | 90 | {546, &IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport, "AcquireDeviceRegisteredEventForControllerSupport"}, |
| 91 | {547, nullptr, "GetAllowedBluetoothLinksCount"}, | 91 | {547, nullptr, "GetAllowedBluetoothLinksCount"}, |
| 92 | {548, nullptr, "GetRegisteredDevices"}, | 92 | {548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"}, |
| 93 | {549, nullptr, "GetConnectableRegisteredDevices"}, | 93 | {549, nullptr, "GetConnectableRegisteredDevices"}, |
| 94 | {700, nullptr, "ActivateUniquePad"}, | 94 | {700, nullptr, "ActivateUniquePad"}, |
| 95 | {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, | 95 | {702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"}, |
| 96 | {703, nullptr, "GetUniquePadIds"}, | 96 | {703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"}, |
| 97 | {751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"}, | 97 | {751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"}, |
| 98 | {800, nullptr, "ListSixAxisSensorHandles"}, | 98 | {800, nullptr, "ListSixAxisSensorHandles"}, |
| 99 | {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, | 99 | {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, |
| 100 | {802, nullptr, "ResetSixAxisSensorCalibrationValues"}, | 100 | {802, nullptr, "ResetSixAxisSensorCalibrationValues"}, |
| 101 | {803, nullptr, "StartSixAxisSensorUserCalibration"}, | 101 | {803, nullptr, "StartSixAxisSensorUserCalibration"}, |
| 102 | {804, nullptr, "CancelSixAxisSensorUserCalibration"}, | 102 | {804, nullptr, "CancelSixAxisSensorUserCalibration"}, |
| 103 | {805, nullptr, "GetUniquePadBluetoothAddress"}, | 103 | {805, nullptr, "GetUniquePadBluetoothAddress"}, |
| 104 | {806, nullptr, "DisconnectUniquePad"}, | 104 | {806, nullptr, "DisconnectUniquePad"}, |
| 105 | {807, nullptr, "GetUniquePadType"}, | 105 | {807, nullptr, "GetUniquePadType"}, |
| 106 | {808, nullptr, "GetUniquePadInterface"}, | 106 | {808, nullptr, "GetUniquePadInterface"}, |
| 107 | {809, nullptr, "GetUniquePadSerialNumber"}, | 107 | {809, nullptr, "GetUniquePadSerialNumber"}, |
| 108 | {810, nullptr, "GetUniquePadControllerNumber"}, | 108 | {810, nullptr, "GetUniquePadControllerNumber"}, |
| 109 | {811, nullptr, "GetSixAxisSensorUserCalibrationStage"}, | 109 | {811, nullptr, "GetSixAxisSensorUserCalibrationStage"}, |
| 110 | {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"}, | 110 | {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"}, |
| 111 | {821, nullptr, "StartAnalogStickManualCalibration"}, | 111 | {821, nullptr, "StartAnalogStickManualCalibration"}, |
| 112 | {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"}, | 112 | {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"}, |
| 113 | {823, nullptr, "CancelAnalogStickManualCalibration"}, | 113 | {823, nullptr, "CancelAnalogStickManualCalibration"}, |
| 114 | {824, nullptr, "ResetAnalogStickManualCalibration"}, | 114 | {824, nullptr, "ResetAnalogStickManualCalibration"}, |
| 115 | {825, nullptr, "GetAnalogStickState"}, | 115 | {825, nullptr, "GetAnalogStickState"}, |
| 116 | {826, nullptr, "GetAnalogStickManualCalibrationStage"}, | 116 | {826, nullptr, "GetAnalogStickManualCalibrationStage"}, |
| 117 | {827, nullptr, "IsAnalogStickButtonPressed"}, | 117 | {827, nullptr, "IsAnalogStickButtonPressed"}, |
| 118 | {828, nullptr, "IsAnalogStickInReleasePosition"}, | 118 | {828, nullptr, "IsAnalogStickInReleasePosition"}, |
| 119 | {829, nullptr, "IsAnalogStickInCircumference"}, | 119 | {829, nullptr, "IsAnalogStickInCircumference"}, |
| 120 | {830, nullptr, "SetNotificationLedPattern"}, | 120 | {830, nullptr, "SetNotificationLedPattern"}, |
| 121 | {831, nullptr, "SetNotificationLedPatternWithTimeout"}, | 121 | {831, nullptr, "SetNotificationLedPatternWithTimeout"}, |
| 122 | {832, nullptr, "PrepareHidsForNotificationWake"}, | 122 | {832, nullptr, "PrepareHidsForNotificationWake"}, |
| 123 | {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, | 123 | {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, |
| 124 | {851, nullptr, "EnableUsbFullKeyController"}, | 124 | {851, nullptr, "EnableUsbFullKeyController"}, |
| 125 | {852, nullptr, "IsUsbConnected"}, | 125 | {852, nullptr, "IsUsbConnected"}, |
| 126 | {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, | 126 | {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"}, |
| 127 | {900, nullptr, "ActivateInputDetector"}, | 127 | {900, nullptr, "ActivateInputDetector"}, |
| 128 | {901, nullptr, "NotifyInputDetector"}, | 128 | {901, nullptr, "NotifyInputDetector"}, |
| 129 | {1000, nullptr, "InitializeFirmwareUpdate"}, | 129 | {1000, &IHidSystemServer::InitializeFirmwareUpdate, "InitializeFirmwareUpdate"}, |
| 130 | {1001, nullptr, "GetFirmwareVersion"}, | 130 | {1001, nullptr, "GetFirmwareVersion"}, |
| 131 | {1002, nullptr, "GetAvailableFirmwareVersion"}, | 131 | {1002, nullptr, "GetAvailableFirmwareVersion"}, |
| 132 | {1003, nullptr, "IsFirmwareUpdateAvailable"}, | 132 | {1003, nullptr, "IsFirmwareUpdateAvailable"}, |
| 133 | {1004, nullptr, "CheckFirmwareUpdateRequired"}, | 133 | {1004, nullptr, "CheckFirmwareUpdateRequired"}, |
| 134 | {1005, nullptr, "StartFirmwareUpdate"}, | 134 | {1005, nullptr, "StartFirmwareUpdate"}, |
| 135 | {1006, nullptr, "AbortFirmwareUpdate"}, | 135 | {1006, nullptr, "AbortFirmwareUpdate"}, |
| 136 | {1007, nullptr, "GetFirmwareUpdateState"}, | 136 | {1007, nullptr, "GetFirmwareUpdateState"}, |
| 137 | {1008, nullptr, "ActivateAudioControl"}, | 137 | {1008, nullptr, "ActivateAudioControl"}, |
| 138 | {1009, nullptr, "AcquireAudioControlEventHandle"}, | 138 | {1009, nullptr, "AcquireAudioControlEventHandle"}, |
| 139 | {1010, nullptr, "GetAudioControlStates"}, | 139 | {1010, nullptr, "GetAudioControlStates"}, |
| 140 | {1011, nullptr, "DeactivateAudioControl"}, | 140 | {1011, nullptr, "DeactivateAudioControl"}, |
| 141 | {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"}, | 141 | {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"}, |
| 142 | {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"}, | 142 | {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"}, |
| 143 | {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, | 143 | {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, |
| 144 | {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, | 144 | {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, |
| 145 | {1100, nullptr, "GetHidbusSystemServiceObject"}, | 145 | {1100, nullptr, "GetHidbusSystemServiceObject"}, |
| 146 | {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, | 146 | {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, |
| 147 | {1130, nullptr, "InitializeUsbFirmwareUpdate"}, | 147 | {1130, nullptr, "InitializeUsbFirmwareUpdate"}, |
| 148 | {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, | 148 | {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, |
| 149 | {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, | 149 | {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, |
| 150 | {1133, nullptr, "StartUsbFirmwareUpdate"}, | 150 | {1133, nullptr, "StartUsbFirmwareUpdate"}, |
| 151 | {1134, nullptr, "GetUsbFirmwareUpdateState"}, | 151 | {1134, nullptr, "GetUsbFirmwareUpdateState"}, |
| 152 | {1150, nullptr, "SetTouchScreenMagnification"}, | 152 | {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"}, |
| 153 | {1151, nullptr, "GetTouchScreenFirmwareVersion"}, | 153 | {1150, nullptr, "SetTouchScreenMagnification"}, |
| 154 | {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, | 154 | {1151, nullptr, "GetTouchScreenFirmwareVersion"}, |
| 155 | {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, | 155 | {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, |
| 156 | {1154, nullptr, "IsFirmwareAvailableForNotification"}, | 156 | {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, |
| 157 | {1155, nullptr, "SetForceHandheldStyleVibration"}, | 157 | {1154, nullptr, "IsFirmwareAvailableForNotification"}, |
| 158 | {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, | 158 | {1155, nullptr, "SetForceHandheldStyleVibration"}, |
| 159 | {1157, nullptr, "CancelConnectionTrigger"}, | 159 | {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, |
| 160 | {1200, nullptr, "IsButtonConfigSupported"}, | 160 | {1157, nullptr, "CancelConnectionTrigger"}, |
| 161 | {1201, nullptr, "IsButtonConfigEmbeddedSupported"}, | 161 | {1200, nullptr, "IsButtonConfigSupported"}, |
| 162 | {1202, nullptr, "DeleteButtonConfig"}, | 162 | {1201, nullptr, "IsButtonConfigEmbeddedSupported"}, |
| 163 | {1203, nullptr, "DeleteButtonConfigEmbedded"}, | 163 | {1202, nullptr, "DeleteButtonConfig"}, |
| 164 | {1204, nullptr, "SetButtonConfigEnabled"}, | 164 | {1203, nullptr, "DeleteButtonConfigEmbedded"}, |
| 165 | {1205, nullptr, "SetButtonConfigEmbeddedEnabled"}, | 165 | {1204, nullptr, "SetButtonConfigEnabled"}, |
| 166 | {1206, nullptr, "IsButtonConfigEnabled"}, | 166 | {1205, nullptr, "SetButtonConfigEmbeddedEnabled"}, |
| 167 | {1207, nullptr, "IsButtonConfigEmbeddedEnabled"}, | 167 | {1206, nullptr, "IsButtonConfigEnabled"}, |
| 168 | {1208, nullptr, "SetButtonConfigEmbedded"}, | 168 | {1207, nullptr, "IsButtonConfigEmbeddedEnabled"}, |
| 169 | {1209, nullptr, "SetButtonConfigFull"}, | 169 | {1208, nullptr, "SetButtonConfigEmbedded"}, |
| 170 | {1210, nullptr, "SetButtonConfigLeft"}, | 170 | {1209, nullptr, "SetButtonConfigFull"}, |
| 171 | {1211, nullptr, "SetButtonConfigRight"}, | 171 | {1210, nullptr, "SetButtonConfigLeft"}, |
| 172 | {1212, nullptr, "GetButtonConfigEmbedded"}, | 172 | {1211, nullptr, "SetButtonConfigRight"}, |
| 173 | {1213, nullptr, "GetButtonConfigFull"}, | 173 | {1212, nullptr, "GetButtonConfigEmbedded"}, |
| 174 | {1214, nullptr, "GetButtonConfigLeft"}, | 174 | {1213, nullptr, "GetButtonConfigFull"}, |
| 175 | {1215, nullptr, "GetButtonConfigRight"}, | 175 | {1214, nullptr, "GetButtonConfigLeft"}, |
| 176 | {1250, nullptr, "IsCustomButtonConfigSupported"}, | 176 | {1215, nullptr, "GetButtonConfigRight"}, |
| 177 | {1251, nullptr, "IsDefaultButtonConfigEmbedded"}, | 177 | {1250, nullptr, "IsCustomButtonConfigSupported"}, |
| 178 | {1252, nullptr, "IsDefaultButtonConfigFull"}, | 178 | {1251, nullptr, "IsDefaultButtonConfigEmbedded"}, |
| 179 | {1253, nullptr, "IsDefaultButtonConfigLeft"}, | 179 | {1252, nullptr, "IsDefaultButtonConfigFull"}, |
| 180 | {1254, nullptr, "IsDefaultButtonConfigRight"}, | 180 | {1253, nullptr, "IsDefaultButtonConfigLeft"}, |
| 181 | {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"}, | 181 | {1254, nullptr, "IsDefaultButtonConfigRight"}, |
| 182 | {1256, nullptr, "IsButtonConfigStorageFullEmpty"}, | 182 | {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"}, |
| 183 | {1257, nullptr, "IsButtonConfigStorageLeftEmpty"}, | 183 | {1256, nullptr, "IsButtonConfigStorageFullEmpty"}, |
| 184 | {1258, nullptr, "IsButtonConfigStorageRightEmpty"}, | 184 | {1257, nullptr, "IsButtonConfigStorageLeftEmpty"}, |
| 185 | {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"}, | 185 | {1258, nullptr, "IsButtonConfigStorageRightEmpty"}, |
| 186 | {1260, nullptr, "GetButtonConfigStorageFullDeprecated"}, | 186 | {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"}, |
| 187 | {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"}, | 187 | {1260, nullptr, "GetButtonConfigStorageFullDeprecated"}, |
| 188 | {1262, nullptr, "GetButtonConfigStorageRightDeprecated"}, | 188 | {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"}, |
| 189 | {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"}, | 189 | {1262, nullptr, "GetButtonConfigStorageRightDeprecated"}, |
| 190 | {1264, nullptr, "SetButtonConfigStorageFullDeprecated"}, | 190 | {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"}, |
| 191 | {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"}, | 191 | {1264, nullptr, "SetButtonConfigStorageFullDeprecated"}, |
| 192 | {1266, nullptr, "SetButtonConfigStorageRightDeprecated"}, | 192 | {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"}, |
| 193 | {1267, nullptr, "DeleteButtonConfigStorageEmbedded"}, | 193 | {1266, nullptr, "SetButtonConfigStorageRightDeprecated"}, |
| 194 | {1268, nullptr, "DeleteButtonConfigStorageFull"}, | 194 | {1267, nullptr, "DeleteButtonConfigStorageEmbedded"}, |
| 195 | {1269, nullptr, "DeleteButtonConfigStorageLeft"}, | 195 | {1268, nullptr, "DeleteButtonConfigStorageFull"}, |
| 196 | {1270, nullptr, "DeleteButtonConfigStorageRight"}, | 196 | {1269, nullptr, "DeleteButtonConfigStorageLeft"}, |
| 197 | {1271, nullptr, "IsUsingCustomButtonConfig"}, | 197 | {1270, nullptr, "DeleteButtonConfigStorageRight"}, |
| 198 | {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, | 198 | {1271, nullptr, "IsUsingCustomButtonConfig"}, |
| 199 | {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, | 199 | {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, |
| 200 | {1274, nullptr, "SetDefaultButtonConfig"}, | 200 | {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, |
| 201 | {1275, nullptr, "SetAllDefaultButtonConfig"}, | 201 | {1274, nullptr, "SetDefaultButtonConfig"}, |
| 202 | {1276, nullptr, "SetHidButtonConfigEmbedded"}, | 202 | {1275, nullptr, "SetAllDefaultButtonConfig"}, |
| 203 | {1277, nullptr, "SetHidButtonConfigFull"}, | 203 | {1276, nullptr, "SetHidButtonConfigEmbedded"}, |
| 204 | {1278, nullptr, "SetHidButtonConfigLeft"}, | 204 | {1277, nullptr, "SetHidButtonConfigFull"}, |
| 205 | {1279, nullptr, "SetHidButtonConfigRight"}, | 205 | {1278, nullptr, "SetHidButtonConfigLeft"}, |
| 206 | {1280, nullptr, "GetHidButtonConfigEmbedded"}, | 206 | {1279, nullptr, "SetHidButtonConfigRight"}, |
| 207 | {1281, nullptr, "GetHidButtonConfigFull"}, | 207 | {1280, nullptr, "GetHidButtonConfigEmbedded"}, |
| 208 | {1282, nullptr, "GetHidButtonConfigLeft"}, | 208 | {1281, nullptr, "GetHidButtonConfigFull"}, |
| 209 | {1283, nullptr, "GetHidButtonConfigRight"}, | 209 | {1282, nullptr, "GetHidButtonConfigLeft"}, |
| 210 | {1284, nullptr, "GetButtonConfigStorageEmbedded"}, | 210 | {1283, nullptr, "GetHidButtonConfigRight"}, |
| 211 | {1285, nullptr, "GetButtonConfigStorageFull"}, | 211 | {1284, nullptr, "GetButtonConfigStorageEmbedded"}, |
| 212 | {1286, nullptr, "GetButtonConfigStorageLeft"}, | 212 | {1285, nullptr, "GetButtonConfigStorageFull"}, |
| 213 | {1287, nullptr, "GetButtonConfigStorageRight"}, | 213 | {1286, nullptr, "GetButtonConfigStorageLeft"}, |
| 214 | {1288, nullptr, "SetButtonConfigStorageEmbedded"}, | 214 | {1287, nullptr, "GetButtonConfigStorageRight"}, |
| 215 | {1289, nullptr, "SetButtonConfigStorageFull"}, | 215 | {1288, nullptr, "SetButtonConfigStorageEmbedded"}, |
| 216 | {1290, nullptr, "DeleteButtonConfigStorageRight"}, | 216 | {1289, nullptr, "SetButtonConfigStorageFull"}, |
| 217 | {1291, nullptr, "DeleteButtonConfigStorageRight"}, | 217 | {1290, nullptr, "DeleteButtonConfigStorageRight"}, |
| 218 | {1291, nullptr, "DeleteButtonConfigStorageRight"}, | ||
| 218 | }; | 219 | }; |
| 219 | // clang-format on | 220 | // clang-format on |
| 220 | 221 | ||
| 221 | RegisterHandlers(functions); | 222 | RegisterHandlers(functions); |
| 222 | 223 | ||
| 223 | joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent"); | 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"); | ||
| 224 | } | 231 | } |
| 225 | 232 | ||
| 226 | IHidSystemServer::~IHidSystemServer() { | 233 | IHidSystemServer::~IHidSystemServer() { |
| 227 | service_context.CloseEvent(joy_detach_event); | 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); | ||
| 228 | }; | 238 | }; |
| 229 | 239 | ||
| 230 | void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { | 240 | void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { |
| 231 | LOG_WARNING(Service_HID, "called"); | 241 | LOG_WARNING(Service_HID, "called"); |
| 232 | 242 | ||
| 233 | GetResourceManager() | 243 | GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(); |
| 234 | ->GetController<Controller_NPad>(HidController::NPad) | 244 | |
| 235 | .ApplyNpadSystemCommonPolicy(); | 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"); | ||
| 236 | 258 | ||
| 237 | IPC::ResponseBuilder rb{ctx, 2}; | 259 | IPC::ResponseBuilder rb{ctx, 2}; |
| 238 | rb.Push(ResultSuccess); | 260 | rb.Push(ResultSuccess); |
| 239 | } | 261 | } |
| 240 | 262 | ||
| 241 | void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) { | 263 | void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) { |
| 242 | LOG_DEBUG(Service_HID, "(STUBBED) called"); | 264 | LOG_DEBUG(Service_HID, "(STUBBED) called"); // Spams a lot when controller applet is running |
| 243 | 265 | ||
| 244 | IPC::ResponseBuilder rb{ctx, 3}; | 266 | IPC::ResponseBuilder rb{ctx, 3}; |
| 245 | rb.Push(ResultSuccess); | 267 | rb.Push(ResultSuccess); |
| 246 | rb.PushEnum(system.HIDCore().GetLastActiveController()); | 268 | rb.PushEnum(system.HIDCore().GetLastActiveController()); |
| 247 | } | 269 | } |
| 248 | 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 | |||
| 249 | void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) { | 397 | void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) { |
| 250 | IPC::RequestParser rp{ctx}; | 398 | IPC::RequestParser rp{ctx}; |
| 251 | const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; | 399 | const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; |
| 252 | 400 | ||
| 253 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type); | 401 | LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", |
| 402 | npad_id_type); // Spams a lot when controller applet is running | ||
| 254 | 403 | ||
| 255 | const std::vector<Core::HID::UniquePadId> unique_pads{}; | 404 | const std::vector<Core::HID::UniquePadId> unique_pads{}; |
| 256 | 405 | ||
| 257 | ctx.WriteBuffer(unique_pads); | 406 | if (!unique_pads.empty()) { |
| 407 | ctx.WriteBuffer(unique_pads); | ||
| 408 | } | ||
| 258 | 409 | ||
| 259 | IPC::ResponseBuilder rb{ctx, 3}; | 410 | IPC::ResponseBuilder rb{ctx, 3}; |
| 260 | rb.Push(ResultSuccess); | 411 | rb.Push(ResultSuccess); |
| 261 | rb.Push(static_cast<u32>(unique_pads.size())); | 412 | rb.Push(static_cast<u32>(unique_pads.size())); |
| 262 | } | 413 | } |
| 263 | 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 | |||
| 264 | void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) { | 474 | void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) { |
| 265 | LOG_INFO(Service_AM, "called"); | 475 | LOG_INFO(Service_AM, "called"); |
| 266 | 476 | ||
| @@ -279,6 +489,31 @@ void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { | |||
| 279 | rb.Push(is_enabled); | 489 | rb.Push(is_enabled); |
| 280 | } | 490 | } |
| 281 | 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 | |||
| 282 | void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { | 517 | void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { |
| 283 | LOG_WARNING(Service_HID, "(STUBBED) called"); | 518 | LOG_WARNING(Service_HID, "(STUBBED) called"); |
| 284 | 519 | ||
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h index d4b3910fa..822d5e5b9 100644 --- a/src/core/hle/service/hid/hid_system_server.h +++ b/src/core/hle/service/hid/hid_system_server.h | |||
| @@ -24,15 +24,38 @@ public: | |||
| 24 | 24 | ||
| 25 | private: | 25 | private: |
| 26 | void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx); | 26 | void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx); |
| 27 | void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx); | ||
| 28 | void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx); | ||
| 27 | void GetLastActiveNpad(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); | ||
| 28 | void GetUniquePadsFromNpad(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); | ||
| 29 | void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx); | 46 | void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx); |
| 30 | void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); | 47 | void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); |
| 48 | void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx); | ||
| 49 | void InitializeFirmwareUpdate(HLERequestContext& ctx); | ||
| 50 | void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); | ||
| 31 | void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); | 51 | void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); |
| 32 | 52 | ||
| 33 | std::shared_ptr<ResourceManager> GetResourceManager(); | 53 | std::shared_ptr<ResourceManager> GetResourceManager(); |
| 34 | 54 | ||
| 55 | Kernel::KEvent* acquire_connection_trigger_timeout_event; | ||
| 56 | Kernel::KEvent* acquire_device_registered_event; | ||
| 35 | Kernel::KEvent* joy_detach_event; | 57 | Kernel::KEvent* joy_detach_event; |
| 58 | Kernel::KEvent* unique_pad_connection_event; | ||
| 36 | KernelHelpers::ServiceContext service_context; | 59 | KernelHelpers::ServiceContext service_context; |
| 37 | std::shared_ptr<ResourceManager> resource_manager; | 60 | std::shared_ptr<ResourceManager> resource_manager; |
| 38 | }; | 61 | }; |
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 d383a266d..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" |
| @@ -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/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp index d6f42c646..e76d4eea9 100644 --- a/src/core/hle/service/hid/resource_manager.cpp +++ b/src/core/hle/service/hid/resource_manager.cpp | |||
| @@ -9,14 +9,15 @@ | |||
| 9 | #include "core/hle/service/hid/resource_manager.h" | 9 | #include "core/hle/service/hid/resource_manager.h" |
| 10 | #include "core/hle/service/ipc_helpers.h" | 10 | #include "core/hle/service/ipc_helpers.h" |
| 11 | 11 | ||
| 12 | #include "core/hle/service/hid/controllers/console_sixaxis.h" | 12 | #include "core/hle/service/hid/controllers/console_six_axis.h" |
| 13 | #include "core/hle/service/hid/controllers/controller_base.h" | ||
| 14 | #include "core/hle/service/hid/controllers/debug_pad.h" | 13 | #include "core/hle/service/hid/controllers/debug_pad.h" |
| 15 | #include "core/hle/service/hid/controllers/gesture.h" | 14 | #include "core/hle/service/hid/controllers/gesture.h" |
| 16 | #include "core/hle/service/hid/controllers/keyboard.h" | 15 | #include "core/hle/service/hid/controllers/keyboard.h" |
| 17 | #include "core/hle/service/hid/controllers/mouse.h" | 16 | #include "core/hle/service/hid/controllers/mouse.h" |
| 18 | #include "core/hle/service/hid/controllers/npad.h" | 17 | #include "core/hle/service/hid/controllers/npad.h" |
| 19 | #include "core/hle/service/hid/controllers/palma.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" | ||
| 20 | #include "core/hle/service/hid/controllers/stubbed.h" | 21 | #include "core/hle/service/hid/controllers/stubbed.h" |
| 21 | #include "core/hle/service/hid/controllers/touchscreen.h" | 22 | #include "core/hle/service/hid/controllers/touchscreen.h" |
| 22 | #include "core/hle/service/hid/controllers/xpad.h" | 23 | #include "core/hle/service/hid/controllers/xpad.h" |
| @@ -42,76 +43,132 @@ void ResourceManager::Initialize() { | |||
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); | 45 | u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); |
| 45 | MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory); | 46 | debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory); |
| 46 | MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory); | 47 | mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory); |
| 47 | MakeController<Controller_Mouse>(HidController::Mouse, shared_memory); | 48 | debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory); |
| 48 | MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory); | 49 | keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory); |
| 49 | MakeController<Controller_XPad>(HidController::XPad, shared_memory); | 50 | unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory); |
| 50 | MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory); | 51 | npad = std::make_shared<NPad>(system.HIDCore(), shared_memory, service_context); |
| 51 | MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory); | 52 | gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory); |
| 52 | MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory); | 53 | touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory); |
| 53 | MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory); | 54 | xpad = std::make_shared<XPad>(system.HIDCore(), shared_memory); |
| 54 | MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory); | 55 | |
| 55 | MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory); | 56 | palma = std::make_shared<Palma>(system.HIDCore(), shared_memory, service_context); |
| 56 | MakeController<Controller_Gesture>(HidController::Gesture, shared_memory); | 57 | |
| 57 | MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory); | 58 | home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory); |
| 58 | MakeController<Controller_Stubbed>(HidController::DebugMouse, shared_memory); | 59 | sleep_button = std::make_shared<SleepButton>(system.HIDCore(), shared_memory); |
| 59 | MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, 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); | ||
| 60 | 71 | ||
| 61 | // Homebrew doesn't try to activate some controllers, so we activate them by default | 72 | // Homebrew doesn't try to activate some controllers, so we activate them by default |
| 62 | GetController<Controller_NPad>(HidController::NPad).Activate(); | 73 | npad->Activate(); |
| 63 | GetController<Controller_Touchscreen>(HidController::Touchscreen).Activate(); | 74 | six_axis->Activate(); |
| 64 | 75 | touch_screen->Activate(); | |
| 65 | GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00); | ||
| 66 | GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00); | ||
| 67 | GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000); | ||
| 68 | GetController<Controller_Stubbed>(HidController::InputDetector).SetCommonHeaderOffset(0x5200); | ||
| 69 | GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00); | ||
| 70 | GetController<Controller_Stubbed>(HidController::DebugMouse).SetCommonHeaderOffset(0x3DC00); | ||
| 71 | 76 | ||
| 72 | system.HIDCore().ReloadInputDevices(); | 77 | system.HIDCore().ReloadInputDevices(); |
| 73 | is_initialized = true; | 78 | is_initialized = true; |
| 74 | } | 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 | } | ||
| 75 | 139 | ||
| 76 | void ResourceManager::UpdateControllers(std::uintptr_t user_data, | 140 | void ResourceManager::UpdateControllers(std::uintptr_t user_data, |
| 77 | std::chrono::nanoseconds ns_late) { | 141 | std::chrono::nanoseconds ns_late) { |
| 78 | auto& core_timing = system.CoreTiming(); | 142 | auto& core_timing = system.CoreTiming(); |
| 79 | 143 | debug_pad->OnUpdate(core_timing); | |
| 80 | for (const auto& controller : controllers) { | 144 | unique_pad->OnUpdate(core_timing); |
| 81 | // Keyboard has it's own update event | 145 | gesture->OnUpdate(core_timing); |
| 82 | if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) { | 146 | touch_screen->OnUpdate(core_timing); |
| 83 | continue; | 147 | palma->OnUpdate(core_timing); |
| 84 | } | 148 | home_button->OnUpdate(core_timing); |
| 85 | // Mouse has it's own update event | 149 | sleep_button->OnUpdate(core_timing); |
| 86 | if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) { | 150 | capture_button->OnUpdate(core_timing); |
| 87 | continue; | 151 | xpad->OnUpdate(core_timing); |
| 88 | } | ||
| 89 | // Npad has it's own update event | ||
| 90 | if (controller == controllers[static_cast<size_t>(HidController::NPad)]) { | ||
| 91 | continue; | ||
| 92 | } | ||
| 93 | controller->OnUpdate(core_timing); | ||
| 94 | } | ||
| 95 | } | 152 | } |
| 96 | 153 | ||
| 97 | void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 154 | void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 98 | auto& core_timing = system.CoreTiming(); | 155 | auto& core_timing = system.CoreTiming(); |
| 99 | 156 | npad->OnUpdate(core_timing); | |
| 100 | controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing); | ||
| 101 | } | 157 | } |
| 102 | 158 | ||
| 103 | void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data, | 159 | void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data, |
| 104 | std::chrono::nanoseconds ns_late) { | 160 | std::chrono::nanoseconds ns_late) { |
| 105 | auto& core_timing = system.CoreTiming(); | 161 | auto& core_timing = system.CoreTiming(); |
| 106 | 162 | mouse->OnUpdate(core_timing); | |
| 107 | controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); | 163 | debug_mouse->OnUpdate(core_timing); |
| 108 | controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); | 164 | keyboard->OnUpdate(core_timing); |
| 109 | } | 165 | } |
| 110 | 166 | ||
| 111 | void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 167 | void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 112 | auto& core_timing = system.CoreTiming(); | 168 | auto& core_timing = system.CoreTiming(); |
| 113 | 169 | six_axis->OnUpdate(core_timing); | |
| 114 | controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); | 170 | seven_six_axis->OnUpdate(core_timing); |
| 171 | console_six_axis->OnUpdate(core_timing); | ||
| 115 | } | 172 | } |
| 116 | 173 | ||
| 117 | IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource) | 174 | IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource) |
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h index 34dbf36bc..2b6a9b5e6 100644 --- a/src/core/hle/service/hid/resource_manager.h +++ b/src/core/hle/service/hid/resource_manager.h | |||
| @@ -3,10 +3,6 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <chrono> | ||
| 7 | |||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/hle/service/hid/controllers/controller_base.h" | ||
| 10 | #include "core/hle/service/kernel_helpers.h" | 6 | #include "core/hle/service/kernel_helpers.h" |
| 11 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 12 | 8 | ||
| @@ -14,74 +10,85 @@ namespace Core::Timing { | |||
| 14 | struct EventType; | 10 | struct EventType; |
| 15 | } | 11 | } |
| 16 | 12 | ||
| 17 | namespace Core::HID { | ||
| 18 | class HIDCore; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace Service::HID { | 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; | ||
| 22 | 32 | ||
| 23 | enum class HidController : std::size_t { | ||
| 24 | DebugPad, | ||
| 25 | Touchscreen, | ||
| 26 | Mouse, | ||
| 27 | Keyboard, | ||
| 28 | XPad, | ||
| 29 | HomeButton, | ||
| 30 | SleepButton, | ||
| 31 | CaptureButton, | ||
| 32 | InputDetector, | ||
| 33 | UniquePad, | ||
| 34 | NPad, | ||
| 35 | Gesture, | ||
| 36 | ConsoleSixAxisSensor, | ||
| 37 | DebugMouse, | ||
| 38 | Palma, | ||
| 39 | |||
| 40 | MaxControllers, | ||
| 41 | }; | ||
| 42 | class ResourceManager { | 33 | class ResourceManager { |
| 34 | |||
| 43 | public: | 35 | public: |
| 44 | explicit ResourceManager(Core::System& system_); | 36 | explicit ResourceManager(Core::System& system_); |
| 45 | ~ResourceManager(); | 37 | ~ResourceManager(); |
| 46 | 38 | ||
| 47 | template <typename T> | ||
| 48 | T& GetController(HidController controller) { | ||
| 49 | return static_cast<T&>(*controllers[static_cast<size_t>(controller)]); | ||
| 50 | } | ||
| 51 | |||
| 52 | template <typename T> | ||
| 53 | const T& GetController(HidController controller) const { | ||
| 54 | return static_cast<T&>(*controllers[static_cast<size_t>(controller)]); | ||
| 55 | } | ||
| 56 | |||
| 57 | void Initialize(); | 39 | void Initialize(); |
| 58 | 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 | |||
| 59 | void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | 57 | void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); |
| 60 | void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | 58 | void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); |
| 61 | void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | 59 | void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); |
| 62 | void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | 60 | void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); |
| 63 | 61 | ||
| 64 | private: | 62 | private: |
| 65 | template <typename T> | ||
| 66 | void MakeController(HidController controller, u8* shared_memory) { | ||
| 67 | if constexpr (std::is_constructible_v<T, Core::System&, u8*>) { | ||
| 68 | controllers[static_cast<std::size_t>(controller)] = | ||
| 69 | std::make_unique<T>(system, shared_memory); | ||
| 70 | } else { | ||
| 71 | controllers[static_cast<std::size_t>(controller)] = | ||
| 72 | std::make_unique<T>(system.HIDCore(), shared_memory); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | template <typename T> | ||
| 77 | void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) { | ||
| 78 | controllers[static_cast<std::size_t>(controller)] = | ||
| 79 | std::make_unique<T>(system.HIDCore(), shared_memory, service_context); | ||
| 80 | } | ||
| 81 | |||
| 82 | bool is_initialized{false}; | 63 | bool is_initialized{false}; |
| 83 | std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> | 64 | |
| 84 | controllers{}; | 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; | ||
| 85 | 92 | ||
| 86 | Core::System& system; | 93 | Core::System& system; |
| 87 | KernelHelpers::ServiceContext service_context; | 94 | KernelHelpers::ServiceContext service_context; |
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/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp index a019cc9f7..c27646fcf 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.cpp +++ b/src/core/hle/service/mii/types/ver3_store_data.cpp | |||
| @@ -98,7 +98,7 @@ void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const { | |||
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { | 100 | void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { |
| 101 | version = 1; | 101 | version = 3; |
| 102 | mii_information.gender.Assign(static_cast<u8>(store_data.GetGender())); | 102 | mii_information.gender.Assign(static_cast<u8>(store_data.GetGender())); |
| 103 | mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor())); | 103 | mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor())); |
| 104 | height = store_data.GetHeight(); | 104 | height = store_data.GetHeight(); |
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index e7a00deb3..f97e5b44c 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp | |||
| @@ -401,6 +401,12 @@ Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& time | |||
| 401 | } | 401 | } |
| 402 | 402 | ||
| 403 | Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target_) { | 403 | Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target_) { |
| 404 | bool is_corrupted = false; | ||
| 405 | |||
| 406 | if (model_type != NFP::ModelType::Amiibo) { | ||
| 407 | return ResultInvalidArgument; | ||
| 408 | } | ||
| 409 | |||
| 404 | if (device_state != DeviceState::TagFound) { | 410 | if (device_state != DeviceState::TagFound) { |
| 405 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | 411 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); |
| 406 | return ResultWrongDeviceState; | 412 | return ResultWrongDeviceState; |
| @@ -420,26 +426,32 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target | |||
| 420 | if (is_plain_amiibo) { | 426 | if (is_plain_amiibo) { |
| 421 | std::vector<u8> data(sizeof(NFP::NTAG215File)); | 427 | std::vector<u8> data(sizeof(NFP::NTAG215File)); |
| 422 | memcpy(data.data(), &tag_data, sizeof(tag_data)); | 428 | memcpy(data.data(), &tag_data, sizeof(tag_data)); |
| 423 | WriteBackupData(tag_data.uid, data); | ||
| 424 | |||
| 425 | device_state = DeviceState::TagMounted; | ||
| 426 | mount_target = mount_target_; | ||
| 427 | return ResultSuccess; | ||
| 428 | } | 429 | } |
| 429 | 430 | ||
| 430 | if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { | 431 | if (!is_plain_amiibo && !NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { |
| 431 | bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess(); | 432 | LOG_ERROR(Service_NFP, "Can't decode amiibo"); |
| 432 | LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup); | 433 | is_corrupted = true; |
| 433 | return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; | ||
| 434 | } | 434 | } |
| 435 | 435 | ||
| 436 | std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); | 436 | if (tag_data.settings.settings.amiibo_initialized && !tag_data.owner_mii.IsValid()) { |
| 437 | memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); | 437 | LOG_ERROR(Service_NFP, "Invalid mii data"); |
| 438 | WriteBackupData(encrypted_tag_data.uuid, data); | 438 | is_corrupted = true; |
| 439 | } | ||
| 439 | 440 | ||
| 440 | device_state = DeviceState::TagMounted; | 441 | device_state = DeviceState::TagMounted; |
| 441 | mount_target = mount_target_; | 442 | mount_target = mount_target_; |
| 442 | 443 | ||
| 444 | if (!is_corrupted && mount_target != NFP::MountTarget::Rom) { | ||
| 445 | std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); | ||
| 446 | memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); | ||
| 447 | WriteBackupData(encrypted_tag_data.uuid, data); | ||
| 448 | } | ||
| 449 | |||
| 450 | if (is_corrupted && mount_target != NFP::MountTarget::Rom) { | ||
| 451 | bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess(); | ||
| 452 | return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; | ||
| 453 | } | ||
| 454 | |||
| 443 | return ResultSuccess; | 455 | return ResultSuccess; |
| 444 | } | 456 | } |
| 445 | 457 | ||
| @@ -606,6 +618,17 @@ Result NfcDevice::Restore() { | |||
| 606 | } | 618 | } |
| 607 | } | 619 | } |
| 608 | 620 | ||
| 621 | // Restore mii data in case is corrupted by previous instances of yuzu | ||
| 622 | if (tag_data.settings.settings.amiibo_initialized && !tag_data.owner_mii.IsValid()) { | ||
| 623 | LOG_ERROR(Service_NFP, "Regenerating mii data"); | ||
| 624 | Mii::StoreData new_mii{}; | ||
| 625 | new_mii.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All); | ||
| 626 | new_mii.SetNickname({u'y', u'u', u'z', u'u', u'\0'}); | ||
| 627 | |||
| 628 | tag_data.owner_mii.BuildFromStoreData(new_mii); | ||
| 629 | tag_data.mii_extension.SetFromStoreData(new_mii); | ||
| 630 | } | ||
| 631 | |||
| 609 | // Overwrite tag contents with backup and mount the tag | 632 | // Overwrite tag contents with backup and mount the tag |
| 610 | tag_data = temporary_tag_data; | 633 | tag_data = temporary_tag_data; |
| 611 | encrypted_tag_data = temporary_encrypted_tag_data; | 634 | encrypted_tag_data = temporary_encrypted_tag_data; |
| @@ -851,25 +874,6 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe | |||
| 851 | return Flush(); | 874 | return Flush(); |
| 852 | } | 875 | } |
| 853 | 876 | ||
| 854 | Result NfcDevice::RestoreAmiibo() { | ||
| 855 | if (device_state != DeviceState::TagMounted) { | ||
| 856 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 857 | if (device_state == DeviceState::TagRemoved) { | ||
| 858 | return ResultTagRemoved; | ||
| 859 | } | ||
| 860 | return ResultWrongDeviceState; | ||
| 861 | } | ||
| 862 | |||
| 863 | if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { | ||
| 864 | LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||
| 865 | return ResultWrongDeviceState; | ||
| 866 | } | ||
| 867 | |||
| 868 | // TODO: Load amiibo from backup on system | ||
| 869 | LOG_ERROR(Service_NFP, "Not Implemented"); | ||
| 870 | return ResultSuccess; | ||
| 871 | } | ||
| 872 | |||
| 873 | Result NfcDevice::Format() { | 877 | Result NfcDevice::Format() { |
| 874 | Result result = ResultSuccess; | 878 | Result result = ResultSuccess; |
| 875 | 879 | ||
| @@ -877,7 +881,9 @@ Result NfcDevice::Format() { | |||
| 877 | result = Mount(NFP::ModelType::Amiibo, NFP::MountTarget::All); | 881 | result = Mount(NFP::ModelType::Amiibo, NFP::MountTarget::All); |
| 878 | } | 882 | } |
| 879 | 883 | ||
| 880 | if (result.IsError()) { | 884 | // We are formatting all data. Corruption is not an issue. |
| 885 | if (result.IsError() && | ||
| 886 | (result != ResultCorruptedData && result != ResultCorruptedDataWithBackup)) { | ||
| 881 | return result; | 887 | return result; |
| 882 | } | 888 | } |
| 883 | 889 | ||
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index 0ed1ff34c..d8efe25ec 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h | |||
| @@ -68,7 +68,6 @@ public: | |||
| 68 | 68 | ||
| 69 | Result DeleteRegisterInfo(); | 69 | Result DeleteRegisterInfo(); |
| 70 | Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info); | 70 | Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info); |
| 71 | Result RestoreAmiibo(); | ||
| 72 | Result Format(); | 71 | Result Format(); |
| 73 | 72 | ||
| 74 | Result OpenApplicationArea(u32 access_id); | 73 | Result OpenApplicationArea(u32 access_id); |
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/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index ec3af80af..48304e6d1 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 | ||
| @@ -45,46 +34,43 @@ void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx, | |||
| 45 | nca = bis_system->GetEntry(FirmwareVersionSystemDataId, FileSys::ContentRecordType::Data); | 34 | nca = bis_system->GetEntry(FirmwareVersionSystemDataId, FileSys::ContentRecordType::Data); |
| 46 | } | 35 | } |
| 47 | if (nca) { | 36 | if (nca) { |
| 48 | romfs = FileSys::ExtractRomFS(nca->GetRomFS()); | 37 | if (auto nca_romfs = nca->GetRomFS(); nca_romfs) { |
| 38 | romfs = FileSys::ExtractRomFS(nca_romfs); | ||
| 39 | } | ||
| 49 | } | 40 | } |
| 50 | if (!romfs) { | 41 | if (!romfs) { |
| 51 | romfs = FileSys::ExtractRomFS( | 42 | romfs = FileSys::ExtractRomFS( |
| 52 | FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); | 43 | FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); |
| 53 | } | 44 | } |
| 54 | 45 | ||
| 55 | const auto early_exit_failure = [&ctx](std::string_view desc, Result code) { | 46 | const auto early_exit_failure = [](std::string_view desc, Result code) { |
| 56 | LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", | 47 | LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", |
| 57 | desc); | 48 | desc); |
| 58 | IPC::ResponseBuilder rb{ctx, 2}; | 49 | return code; |
| 59 | rb.Push(code); | ||
| 60 | }; | 50 | }; |
| 61 | 51 | ||
| 62 | const auto ver_file = romfs->GetFile("file"); | 52 | const auto ver_file = romfs->GetFile("file"); |
| 63 | if (ver_file == nullptr) { | 53 | if (ver_file == nullptr) { |
| 64 | early_exit_failure("The system version archive didn't contain the file 'file'.", | 54 | return early_exit_failure("The system version archive didn't contain the file 'file'.", |
| 65 | FileSys::ERROR_INVALID_ARGUMENT); | 55 | FileSys::ERROR_INVALID_ARGUMENT); |
| 66 | return; | ||
| 67 | } | 56 | } |
| 68 | 57 | ||
| 69 | auto data = ver_file->ReadAllBytes(); | 58 | auto data = ver_file->ReadAllBytes(); |
| 70 | if (data.size() != 0x100) { | 59 | if (data.size() != sizeof(FirmwareVersionFormat)) { |
| 71 | early_exit_failure("The system version file 'file' was not the correct size.", | 60 | return early_exit_failure("The system version file 'file' was not the correct size.", |
| 72 | FileSys::ERROR_OUT_OF_BOUNDS); | 61 | FileSys::ERROR_OUT_OF_BOUNDS); |
| 73 | return; | ||
| 74 | } | 62 | } |
| 75 | 63 | ||
| 64 | std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat)); | ||
| 65 | |||
| 76 | // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will | 66 | // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will |
| 77 | // zero out the REVISION_MINOR field. | 67 | // zero out the REVISION_MINOR field. |
| 78 | if (type == GetFirmwareVersionType::Version1) { | 68 | if (type == GetFirmwareVersionType::Version1) { |
| 79 | data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0; | 69 | out_firmware.revision_minor = 0; |
| 80 | } | 70 | } |
| 81 | 71 | ||
| 82 | ctx.WriteBuffer(data); | 72 | return ResultSuccess; |
| 83 | |||
| 84 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 85 | rb.Push(ResultSuccess); | ||
| 86 | } | 73 | } |
| 87 | } // Anonymous namespace | ||
| 88 | 74 | ||
| 89 | void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { | 75 | void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { |
| 90 | IPC::RequestParser rp{ctx}; | 76 | IPC::RequestParser rp{ctx}; |
| @@ -98,12 +84,32 @@ void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { | |||
| 98 | 84 | ||
| 99 | void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { | 85 | void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { |
| 100 | LOG_DEBUG(Service_SET, "called"); | 86 | LOG_DEBUG(Service_SET, "called"); |
| 101 | GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version1); | 87 | |
| 88 | FirmwareVersionFormat firmware_data{}; | ||
| 89 | const auto result = | ||
| 90 | GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1); | ||
| 91 | |||
| 92 | if (result.IsSuccess()) { | ||
| 93 | ctx.WriteBuffer(firmware_data); | ||
| 94 | } | ||
| 95 | |||
| 96 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 97 | rb.Push(result); | ||
| 102 | } | 98 | } |
| 103 | 99 | ||
| 104 | void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { | 100 | void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { |
| 105 | LOG_DEBUG(Service_SET, "called"); | 101 | LOG_DEBUG(Service_SET, "called"); |
| 106 | GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version2); | 102 | |
| 103 | FirmwareVersionFormat firmware_data{}; | ||
| 104 | const auto result = | ||
| 105 | GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2); | ||
| 106 | |||
| 107 | if (result.IsSuccess()) { | ||
| 108 | ctx.WriteBuffer(firmware_data); | ||
| 109 | } | ||
| 110 | |||
| 111 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 112 | rb.Push(result); | ||
| 107 | } | 113 | } |
| 108 | 114 | ||
| 109 | void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { | 115 | void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { |
| @@ -431,8 +437,7 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) { | |||
| 431 | void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { | 437 | void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { |
| 432 | u8 battery_percentage_flag{1}; | 438 | u8 battery_percentage_flag{1}; |
| 433 | 439 | ||
| 434 | LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}", | 440 | LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag); |
| 435 | battery_percentage_flag); | ||
| 436 | 441 | ||
| 437 | IPC::ResponseBuilder rb{ctx, 3}; | 442 | IPC::ResponseBuilder rb{ctx, 3}; |
| 438 | rb.Push(ResultSuccess); | 443 | rb.Push(ResultSuccess); |
| @@ -492,6 +497,29 @@ void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) { | |||
| 492 | rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); | 497 | rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); |
| 493 | } | 498 | } |
| 494 | 499 | ||
| 500 | void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) { | ||
| 501 | LOG_DEBUG(Service_SET, "(STUBBED) called"); | ||
| 502 | |||
| 503 | const HomeMenuScheme default_color = { | ||
| 504 | .main = 0xFF323232, | ||
| 505 | .back = 0xFF323232, | ||
| 506 | .sub = 0xFFFFFFFF, | ||
| 507 | .bezel = 0xFFFFFFFF, | ||
| 508 | .extra = 0xFF000000, | ||
| 509 | }; | ||
| 510 | |||
| 511 | IPC::ResponseBuilder rb{ctx, 7}; | ||
| 512 | rb.Push(ResultSuccess); | ||
| 513 | rb.PushRaw(default_color); | ||
| 514 | } | ||
| 515 | |||
| 516 | void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) { | ||
| 517 | LOG_WARNING(Service_SET, "(STUBBED) called"); | ||
| 518 | |||
| 519 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 520 | rb.Push(ResultSuccess); | ||
| 521 | rb.Push(0); | ||
| 522 | } | ||
| 495 | void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { | 523 | void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { |
| 496 | LOG_WARNING(Service_SET, "(STUBBED) called"); | 524 | LOG_WARNING(Service_SET, "(STUBBED) called"); |
| 497 | 525 | ||
| @@ -674,7 +702,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { | |||
| 674 | {171, nullptr, "SetChineseTraditionalInputMethod"}, | 702 | {171, nullptr, "SetChineseTraditionalInputMethod"}, |
| 675 | {172, nullptr, "GetPtmCycleCountReliability"}, | 703 | {172, nullptr, "GetPtmCycleCountReliability"}, |
| 676 | {173, nullptr, "SetPtmCycleCountReliability"}, | 704 | {173, nullptr, "SetPtmCycleCountReliability"}, |
| 677 | {174, nullptr, "GetHomeMenuScheme"}, | 705 | {174, &SET_SYS::GetHomeMenuScheme, "GetHomeMenuScheme"}, |
| 678 | {175, nullptr, "GetThemeSettings"}, | 706 | {175, nullptr, "GetThemeSettings"}, |
| 679 | {176, nullptr, "SetThemeSettings"}, | 707 | {176, nullptr, "SetThemeSettings"}, |
| 680 | {177, nullptr, "GetThemeKey"}, | 708 | {177, nullptr, "GetThemeKey"}, |
| @@ -685,7 +713,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { | |||
| 685 | {182, nullptr, "SetT"}, | 713 | {182, nullptr, "SetT"}, |
| 686 | {183, nullptr, "GetPlatformRegion"}, | 714 | {183, nullptr, "GetPlatformRegion"}, |
| 687 | {184, nullptr, "SetPlatformRegion"}, | 715 | {184, nullptr, "SetPlatformRegion"}, |
| 688 | {185, nullptr, "GetHomeMenuSchemeModel"}, | 716 | {185, &SET_SYS::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, |
| 689 | {186, nullptr, "GetMemoryUsageRateFlag"}, | 717 | {186, nullptr, "GetMemoryUsageRateFlag"}, |
| 690 | {187, nullptr, "GetTouchScreenMode"}, | 718 | {187, nullptr, "GetTouchScreenMode"}, |
| 691 | {188, nullptr, "SetTouchScreenMode"}, | 719 | {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/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/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 5c36b71e5..60ee78e89 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <cstring> | 4 | #include <cstring> |
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "common/settings.h" | ||
| 6 | #include "core/core.h" | 7 | #include "core/core.h" |
| 7 | #include "core/file_sys/content_archive.h" | 8 | #include "core/file_sys/content_archive.h" |
| 8 | #include "core/file_sys/control_metadata.h" | 9 | #include "core/file_sys/control_metadata.h" |
| @@ -14,6 +15,10 @@ | |||
| 14 | #include "core/loader/deconstructed_rom_directory.h" | 15 | #include "core/loader/deconstructed_rom_directory.h" |
| 15 | #include "core/loader/nso.h" | 16 | #include "core/loader/nso.h" |
| 16 | 17 | ||
| 18 | #ifdef HAS_NCE | ||
| 19 | #include "core/arm/nce/patcher.h" | ||
| 20 | #endif | ||
| 21 | |||
| 17 | namespace Loader { | 22 | namespace Loader { |
| 18 | 23 | ||
| 19 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, | 24 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, |
| @@ -124,21 +129,43 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 124 | } | 129 | } |
| 125 | metadata.Print(); | 130 | metadata.Print(); |
| 126 | 131 | ||
| 127 | const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", | 132 | // Enable NCE only for programs with 39-bit address space. |
| 128 | "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", | 133 | const bool is_39bit = |
| 129 | "subsdk8", "subsdk9", "sdk"}; | 134 | metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit; |
| 135 | Settings::SetNceEnabled(is_39bit); | ||
| 136 | |||
| 137 | const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", | ||
| 138 | "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", | ||
| 139 | "subsdk8", "subsdk9", "sdk"}; | ||
| 130 | 140 | ||
| 131 | // Use the NSO module loader to figure out the code layout | ||
| 132 | std::size_t code_size{}; | 141 | std::size_t code_size{}; |
| 133 | for (const auto& module : static_modules) { | 142 | |
| 143 | // Define an nce patch context for each potential module. | ||
| 144 | #ifdef HAS_NCE | ||
| 145 | std::array<Core::NCE::Patcher, 13> module_patchers; | ||
| 146 | #endif | ||
| 147 | |||
| 148 | const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* { | ||
| 149 | #ifdef HAS_NCE | ||
| 150 | if (Settings::IsNceEnabled()) { | ||
| 151 | return &module_patchers[i]; | ||
| 152 | } | ||
| 153 | #endif | ||
| 154 | return nullptr; | ||
| 155 | }; | ||
| 156 | |||
| 157 | // Use the NSO module loader to figure out the code layout | ||
| 158 | for (size_t i = 0; i < static_modules.size(); i++) { | ||
| 159 | const auto& module = static_modules[i]; | ||
| 134 | const FileSys::VirtualFile module_file{dir->GetFile(module)}; | 160 | const FileSys::VirtualFile module_file{dir->GetFile(module)}; |
| 135 | if (!module_file) { | 161 | if (!module_file) { |
| 136 | continue; | 162 | continue; |
| 137 | } | 163 | } |
| 138 | 164 | ||
| 139 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; | 165 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; |
| 140 | const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( | 166 | const auto tentative_next_load_addr = |
| 141 | process, system, *module_file, code_size, should_pass_arguments, false); | 167 | AppLoader_NSO::LoadModule(process, system, *module_file, code_size, |
| 168 | should_pass_arguments, false, {}, GetPatcher(i)); | ||
| 142 | if (!tentative_next_load_addr) { | 169 | if (!tentative_next_load_addr) { |
| 143 | return {ResultStatus::ErrorLoadingNSO, {}}; | 170 | return {ResultStatus::ErrorLoadingNSO, {}}; |
| 144 | } | 171 | } |
| @@ -146,8 +173,18 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 146 | code_size = *tentative_next_load_addr; | 173 | code_size = *tentative_next_load_addr; |
| 147 | } | 174 | } |
| 148 | 175 | ||
| 176 | // Enable direct memory mapping in case of NCE. | ||
| 177 | const u64 fastmem_base = [&]() -> size_t { | ||
| 178 | if (Settings::IsNceEnabled()) { | ||
| 179 | auto& buffer = system.DeviceMemory().buffer; | ||
| 180 | buffer.EnableDirectMappedAddress(); | ||
| 181 | return reinterpret_cast<u64>(buffer.VirtualBasePointer()); | ||
| 182 | } | ||
| 183 | return 0; | ||
| 184 | }(); | ||
| 185 | |||
| 149 | // Setup the process code layout | 186 | // Setup the process code layout |
| 150 | if (process.LoadFromMetadata(metadata, code_size, is_hbl).IsError()) { | 187 | if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) { |
| 151 | return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; | 188 | return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; |
| 152 | } | 189 | } |
| 153 | 190 | ||
| @@ -157,7 +194,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 157 | VAddr next_load_addr{base_address}; | 194 | VAddr next_load_addr{base_address}; |
| 158 | const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(), | 195 | const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(), |
| 159 | system.GetContentProvider()}; | 196 | system.GetContentProvider()}; |
| 160 | for (const auto& module : static_modules) { | 197 | for (size_t i = 0; i < static_modules.size(); i++) { |
| 198 | const auto& module = static_modules[i]; | ||
| 161 | const FileSys::VirtualFile module_file{dir->GetFile(module)}; | 199 | const FileSys::VirtualFile module_file{dir->GetFile(module)}; |
| 162 | if (!module_file) { | 200 | if (!module_file) { |
| 163 | continue; | 201 | continue; |
| @@ -165,15 +203,16 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 165 | 203 | ||
| 166 | const VAddr load_addr{next_load_addr}; | 204 | const VAddr load_addr{next_load_addr}; |
| 167 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; | 205 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; |
| 168 | const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( | 206 | const auto tentative_next_load_addr = |
| 169 | process, system, *module_file, load_addr, should_pass_arguments, true, pm); | 207 | AppLoader_NSO::LoadModule(process, system, *module_file, load_addr, |
| 208 | should_pass_arguments, true, pm, GetPatcher(i)); | ||
| 170 | if (!tentative_next_load_addr) { | 209 | if (!tentative_next_load_addr) { |
| 171 | return {ResultStatus::ErrorLoadingNSO, {}}; | 210 | return {ResultStatus::ErrorLoadingNSO, {}}; |
| 172 | } | 211 | } |
| 173 | 212 | ||
| 174 | next_load_addr = *tentative_next_load_addr; | 213 | next_load_addr = *tentative_next_load_addr; |
| 175 | modules.insert_or_assign(load_addr, module); | 214 | modules.insert_or_assign(load_addr, module); |
| 176 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | 215 | LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr); |
| 177 | } | 216 | } |
| 178 | 217 | ||
| 179 | // Find the RomFS by searching for a ".romfs" file in this directory | 218 | // Find the RomFS by searching for a ".romfs" file in this directory |
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index bf56a08b4..cd6982921 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp | |||
| @@ -91,7 +91,8 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process, | |||
| 91 | 91 | ||
| 92 | // Setup the process code layout | 92 | // Setup the process code layout |
| 93 | if (process | 93 | if (process |
| 94 | .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), false) | 94 | .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), 0, |
| 95 | false) | ||
| 95 | .IsError()) { | 96 | .IsError()) { |
| 96 | return {ResultStatus::ErrorNotInitialized, {}}; | 97 | return {ResultStatus::ErrorNotInitialized, {}}; |
| 97 | } | 98 | } |
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 4feb6968a..814407535 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp | |||
| @@ -74,10 +74,8 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S | |||
| 74 | return load_result; | 74 | return load_result; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { | 77 | system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( |
| 78 | system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( | 78 | *this, system.GetContentProvider(), system.GetFileSystemController())); |
| 79 | *this, system.GetContentProvider(), system.GetFileSystemController())); | ||
| 80 | } | ||
| 81 | 79 | ||
| 82 | is_loaded = true; | 80 | is_loaded = true; |
| 83 | return load_result; | 81 | return load_result; |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 69f1a54ed..e74697cda 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -22,6 +22,10 @@ | |||
| 22 | #include "core/loader/nso.h" | 22 | #include "core/loader/nso.h" |
| 23 | #include "core/memory.h" | 23 | #include "core/memory.h" |
| 24 | 24 | ||
| 25 | #ifdef HAS_NCE | ||
| 26 | #include "core/arm/nce/patcher.h" | ||
| 27 | #endif | ||
| 28 | |||
| 25 | namespace Loader { | 29 | namespace Loader { |
| 26 | 30 | ||
| 27 | struct NroSegmentHeader { | 31 | struct NroSegmentHeader { |
| @@ -139,7 +143,8 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 139 | return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); | 143 | return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); |
| 140 | } | 144 | } |
| 141 | 145 | ||
| 142 | static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) { | 146 | static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process, |
| 147 | const std::vector<u8>& data) { | ||
| 143 | if (data.size() < sizeof(NroHeader)) { | 148 | if (data.size() < sizeof(NroHeader)) { |
| 144 | return {}; | 149 | return {}; |
| 145 | } | 150 | } |
| @@ -194,14 +199,61 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) | |||
| 194 | 199 | ||
| 195 | codeset.DataSegment().size += bss_size; | 200 | codeset.DataSegment().size += bss_size; |
| 196 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); | 201 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); |
| 202 | size_t image_size = program_image.size(); | ||
| 203 | |||
| 204 | #ifdef HAS_NCE | ||
| 205 | const auto& code = codeset.CodeSegment(); | ||
| 206 | |||
| 207 | // NROs always have a 39-bit address space. | ||
| 208 | Settings::SetNceEnabled(true); | ||
| 209 | |||
| 210 | // Create NCE patcher | ||
| 211 | Core::NCE::Patcher patch{}; | ||
| 212 | |||
| 213 | if (Settings::IsNceEnabled()) { | ||
| 214 | // Patch SVCs and MRS calls in the guest code | ||
| 215 | patch.PatchText(program_image, code); | ||
| 216 | |||
| 217 | // We only support PostData patching for NROs. | ||
| 218 | ASSERT(patch.GetPatchMode() == Core::NCE::PatchMode::PostData); | ||
| 219 | |||
| 220 | // Update patch section. | ||
| 221 | auto& patch_segment = codeset.PatchSegment(); | ||
| 222 | patch_segment.addr = image_size; | ||
| 223 | patch_segment.size = static_cast<u32>(patch.GetSectionSize()); | ||
| 224 | |||
| 225 | // Add patch section size to the module size. | ||
| 226 | image_size += patch_segment.size; | ||
| 227 | } | ||
| 228 | #endif | ||
| 229 | |||
| 230 | // Enable direct memory mapping in case of NCE. | ||
| 231 | const u64 fastmem_base = [&]() -> size_t { | ||
| 232 | if (Settings::IsNceEnabled()) { | ||
| 233 | auto& buffer = system.DeviceMemory().buffer; | ||
| 234 | buffer.EnableDirectMappedAddress(); | ||
| 235 | return reinterpret_cast<u64>(buffer.VirtualBasePointer()); | ||
| 236 | } | ||
| 237 | return 0; | ||
| 238 | }(); | ||
| 197 | 239 | ||
| 198 | // Setup the process code layout | 240 | // Setup the process code layout |
| 199 | if (process | 241 | if (process |
| 200 | .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), false) | 242 | .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), image_size, fastmem_base, |
| 243 | false) | ||
| 201 | .IsError()) { | 244 | .IsError()) { |
| 202 | return false; | 245 | return false; |
| 203 | } | 246 | } |
| 204 | 247 | ||
| 248 | // Relocate code patch and copy to the program_image if running under NCE. | ||
| 249 | // This needs to be after LoadFromMetadata so we can use the process entry point. | ||
| 250 | #ifdef HAS_NCE | ||
| 251 | if (Settings::IsNceEnabled()) { | ||
| 252 | patch.RelocateAndCopy(process.GetEntryPoint(), code, program_image, | ||
| 253 | &process.GetPostHandlers()); | ||
| 254 | } | ||
| 255 | #endif | ||
| 256 | |||
| 205 | // Load codeset for current process | 257 | // Load codeset for current process |
| 206 | codeset.memory = std::move(program_image); | 258 | codeset.memory = std::move(program_image); |
| 207 | process.LoadModule(std::move(codeset), process.GetEntryPoint()); | 259 | process.LoadModule(std::move(codeset), process.GetEntryPoint()); |
| @@ -209,8 +261,9 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) | |||
| 209 | return true; | 261 | return true; |
| 210 | } | 262 | } |
| 211 | 263 | ||
| 212 | bool AppLoader_NRO::LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file) { | 264 | bool AppLoader_NRO::LoadNro(Core::System& system, Kernel::KProcess& process, |
| 213 | return LoadNroImpl(process, nro_file.ReadAllBytes()); | 265 | const FileSys::VfsFile& nro_file) { |
| 266 | return LoadNroImpl(system, process, nro_file.ReadAllBytes()); | ||
| 214 | } | 267 | } |
| 215 | 268 | ||
| 216 | AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) { | 269 | AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) { |
| @@ -218,7 +271,7 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S | |||
| 218 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 271 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 219 | } | 272 | } |
| 220 | 273 | ||
| 221 | if (!LoadNro(process, *file)) { | 274 | if (!LoadNro(system, process, *file)) { |
| 222 | return {ResultStatus::ErrorLoadingNRO, {}}; | 275 | return {ResultStatus::ErrorLoadingNRO, {}}; |
| 223 | } | 276 | } |
| 224 | 277 | ||
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 8de6eebc6..d2928cba0 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -54,7 +54,7 @@ public: | |||
| 54 | bool IsRomFSUpdatable() const override; | 54 | bool IsRomFSUpdatable() const override; |
| 55 | 55 | ||
| 56 | private: | 56 | private: |
| 57 | bool LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file); | 57 | bool LoadNro(Core::System& system, Kernel::KProcess& process, const FileSys::VfsFile& nro_file); |
| 58 | 58 | ||
| 59 | std::vector<u8> icon_data; | 59 | std::vector<u8> icon_data; |
| 60 | std::unique_ptr<FileSys::NACP> nacp; | 60 | std::unique_ptr<FileSys::NACP> nacp; |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 1350da8dc..b053a0d14 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -20,6 +20,10 @@ | |||
| 20 | #include "core/loader/nso.h" | 20 | #include "core/loader/nso.h" |
| 21 | #include "core/memory.h" | 21 | #include "core/memory.h" |
| 22 | 22 | ||
| 23 | #ifdef HAS_NCE | ||
| 24 | #include "core/arm/nce/patcher.h" | ||
| 25 | #endif | ||
| 26 | |||
| 23 | namespace Loader { | 27 | namespace Loader { |
| 24 | namespace { | 28 | namespace { |
| 25 | struct MODHeader { | 29 | struct MODHeader { |
| @@ -72,7 +76,8 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) { | |||
| 72 | std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, | 76 | std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, |
| 73 | const FileSys::VfsFile& nso_file, VAddr load_base, | 77 | const FileSys::VfsFile& nso_file, VAddr load_base, |
| 74 | bool should_pass_arguments, bool load_into_process, | 78 | bool should_pass_arguments, bool load_into_process, |
| 75 | std::optional<FileSys::PatchManager> pm) { | 79 | std::optional<FileSys::PatchManager> pm, |
| 80 | Core::NCE::Patcher* patch) { | ||
| 76 | if (nso_file.GetSize() < sizeof(NSOHeader)) { | 81 | if (nso_file.GetSize() < sizeof(NSOHeader)) { |
| 77 | return std::nullopt; | 82 | return std::nullopt; |
| 78 | } | 83 | } |
| @@ -86,6 +91,16 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: | |||
| 86 | return std::nullopt; | 91 | return std::nullopt; |
| 87 | } | 92 | } |
| 88 | 93 | ||
| 94 | // Allocate some space at the beginning if we are patching in PreText mode. | ||
| 95 | const size_t module_start = [&]() -> size_t { | ||
| 96 | #ifdef HAS_NCE | ||
| 97 | if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PreText) { | ||
| 98 | return patch->GetSectionSize(); | ||
| 99 | } | ||
| 100 | #endif | ||
| 101 | return 0; | ||
| 102 | }(); | ||
| 103 | |||
| 89 | // Build program image | 104 | // Build program image |
| 90 | Kernel::CodeSet codeset; | 105 | Kernel::CodeSet codeset; |
| 91 | Kernel::PhysicalMemory program_image; | 106 | Kernel::PhysicalMemory program_image; |
| @@ -95,11 +110,12 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: | |||
| 95 | if (nso_header.IsSegmentCompressed(i)) { | 110 | if (nso_header.IsSegmentCompressed(i)) { |
| 96 | data = DecompressSegment(data, nso_header.segments[i]); | 111 | data = DecompressSegment(data, nso_header.segments[i]); |
| 97 | } | 112 | } |
| 98 | program_image.resize(nso_header.segments[i].location + static_cast<u32>(data.size())); | 113 | program_image.resize(module_start + nso_header.segments[i].location + |
| 99 | std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(), | 114 | static_cast<u32>(data.size())); |
| 100 | data.size()); | 115 | std::memcpy(program_image.data() + module_start + nso_header.segments[i].location, |
| 101 | codeset.segments[i].addr = nso_header.segments[i].location; | 116 | data.data(), data.size()); |
| 102 | codeset.segments[i].offset = nso_header.segments[i].location; | 117 | codeset.segments[i].addr = module_start + nso_header.segments[i].location; |
| 118 | codeset.segments[i].offset = module_start + nso_header.segments[i].location; | ||
| 103 | codeset.segments[i].size = nso_header.segments[i].size; | 119 | codeset.segments[i].size = nso_header.segments[i].size; |
| 104 | } | 120 | } |
| 105 | 121 | ||
| @@ -118,7 +134,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: | |||
| 118 | } | 134 | } |
| 119 | 135 | ||
| 120 | codeset.DataSegment().size += nso_header.segments[2].bss_size; | 136 | codeset.DataSegment().size += nso_header.segments[2].bss_size; |
| 121 | const u32 image_size{ | 137 | u32 image_size{ |
| 122 | PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)}; | 138 | PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)}; |
| 123 | program_image.resize(image_size); | 139 | program_image.resize(image_size); |
| 124 | 140 | ||
| @@ -129,15 +145,44 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: | |||
| 129 | // Apply patches if necessary | 145 | // Apply patches if necessary |
| 130 | const auto name = nso_file.GetName(); | 146 | const auto name = nso_file.GetName(); |
| 131 | if (pm && (pm->HasNSOPatch(nso_header.build_id, name) || Settings::values.dump_nso)) { | 147 | if (pm && (pm->HasNSOPatch(nso_header.build_id, name) || Settings::values.dump_nso)) { |
| 132 | std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size()); | 148 | std::span<u8> patchable_section(program_image.data() + module_start, |
| 149 | program_image.size() - module_start); | ||
| 150 | std::vector<u8> pi_header(sizeof(NSOHeader) + patchable_section.size()); | ||
| 133 | std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader)); | 151 | std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader)); |
| 134 | std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(), | 152 | std::memcpy(pi_header.data() + sizeof(NSOHeader), patchable_section.data(), |
| 135 | program_image.size()); | 153 | patchable_section.size()); |
| 136 | 154 | ||
| 137 | pi_header = pm->PatchNSO(pi_header, name); | 155 | pi_header = pm->PatchNSO(pi_header, name); |
| 138 | 156 | ||
| 139 | std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data()); | 157 | std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), patchable_section.data()); |
| 158 | } | ||
| 159 | |||
| 160 | #ifdef HAS_NCE | ||
| 161 | // If we are computing the process code layout and using nce backend, patch. | ||
| 162 | const auto& code = codeset.CodeSegment(); | ||
| 163 | if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::None) { | ||
| 164 | // Patch SVCs and MRS calls in the guest code | ||
| 165 | patch->PatchText(program_image, code); | ||
| 166 | |||
| 167 | // Add patch section size to the module size. | ||
| 168 | image_size += static_cast<u32>(patch->GetSectionSize()); | ||
| 169 | } else if (patch) { | ||
| 170 | // Relocate code patch and copy to the program_image. | ||
| 171 | patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers()); | ||
| 172 | |||
| 173 | // Update patch section. | ||
| 174 | auto& patch_segment = codeset.PatchSegment(); | ||
| 175 | patch_segment.addr = | ||
| 176 | patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size; | ||
| 177 | patch_segment.size = static_cast<u32>(patch->GetSectionSize()); | ||
| 178 | |||
| 179 | // Add patch section size to the module size. In PreText mode image_size | ||
| 180 | // already contains the patch segment as part of module_start. | ||
| 181 | if (patch->GetPatchMode() == Core::NCE::PatchMode::PostData) { | ||
| 182 | image_size += patch_segment.size; | ||
| 183 | } | ||
| 140 | } | 184 | } |
| 185 | #endif | ||
| 141 | 186 | ||
| 142 | // If we aren't actually loading (i.e. just computing the process code layout), we are done | 187 | // If we aren't actually loading (i.e. just computing the process code layout), we are done |
| 143 | if (!load_into_process) { | 188 | if (!load_into_process) { |
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 0b53b4ecd..29b86ed4c 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -15,6 +15,10 @@ namespace Core { | |||
| 15 | class System; | 15 | class System; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | namespace Core::NCE { | ||
| 19 | class Patcher; | ||
| 20 | } | ||
| 21 | |||
| 18 | namespace Kernel { | 22 | namespace Kernel { |
| 19 | class KProcess; | 23 | class KProcess; |
| 20 | } | 24 | } |
| @@ -88,7 +92,8 @@ public: | |||
| 88 | static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system, | 92 | static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system, |
| 89 | const FileSys::VfsFile& nso_file, VAddr load_base, | 93 | const FileSys::VfsFile& nso_file, VAddr load_base, |
| 90 | bool should_pass_arguments, bool load_into_process, | 94 | bool should_pass_arguments, bool load_into_process, |
| 91 | std::optional<FileSys::PatchManager> pm = {}); | 95 | std::optional<FileSys::PatchManager> pm = {}, |
| 96 | Core::NCE::Patcher* patch = nullptr); | ||
| 92 | 97 | ||
| 93 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; | 98 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; |
| 94 | 99 | ||
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index a3431772a..5b376b202 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -53,7 +53,7 @@ struct Memory::Impl { | |||
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, | 55 | void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, |
| 56 | Common::PhysicalAddress target) { | 56 | Common::PhysicalAddress target, Common::MemoryPermission perms) { |
| 57 | ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); | 57 | ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); |
| 58 | ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base)); | 58 | ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base)); |
| 59 | ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", | 59 | ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", |
| @@ -63,7 +63,7 @@ struct Memory::Impl { | |||
| 63 | 63 | ||
| 64 | if (Settings::IsFastmemEnabled()) { | 64 | if (Settings::IsFastmemEnabled()) { |
| 65 | system.DeviceMemory().buffer.Map(GetInteger(base), | 65 | system.DeviceMemory().buffer.Map(GetInteger(base), |
| 66 | GetInteger(target) - DramMemoryMap::Base, size); | 66 | GetInteger(target) - DramMemoryMap::Base, size, perms); |
| 67 | } | 67 | } |
| 68 | } | 68 | } |
| 69 | 69 | ||
| @@ -78,6 +78,51 @@ struct Memory::Impl { | |||
| 78 | } | 78 | } |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | void ProtectRegion(Common::PageTable& page_table, VAddr vaddr, u64 size, | ||
| 82 | Common::MemoryPermission perms) { | ||
| 83 | ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); | ||
| 84 | ASSERT_MSG((vaddr & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", vaddr); | ||
| 85 | |||
| 86 | if (!Settings::IsFastmemEnabled()) { | ||
| 87 | return; | ||
| 88 | } | ||
| 89 | |||
| 90 | const bool is_r = True(perms & Common::MemoryPermission::Read); | ||
| 91 | const bool is_w = True(perms & Common::MemoryPermission::Write); | ||
| 92 | const bool is_x = | ||
| 93 | True(perms & Common::MemoryPermission::Execute) && Settings::IsNceEnabled(); | ||
| 94 | |||
| 95 | if (!current_page_table) { | ||
| 96 | system.DeviceMemory().buffer.Protect(vaddr, size, is_r, is_w, is_x); | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | |||
| 100 | u64 protect_bytes{}; | ||
| 101 | u64 protect_begin{}; | ||
| 102 | for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) { | ||
| 103 | const Common::PageType page_type{ | ||
| 104 | current_page_table->pointers[addr >> YUZU_PAGEBITS].Type()}; | ||
| 105 | switch (page_type) { | ||
| 106 | case Common::PageType::RasterizerCachedMemory: | ||
| 107 | if (protect_bytes > 0) { | ||
| 108 | system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, | ||
| 109 | is_x); | ||
| 110 | protect_bytes = 0; | ||
| 111 | } | ||
| 112 | break; | ||
| 113 | default: | ||
| 114 | if (protect_bytes == 0) { | ||
| 115 | protect_begin = addr; | ||
| 116 | } | ||
| 117 | protect_bytes += YUZU_PAGESIZE; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | if (protect_bytes > 0) { | ||
| 122 | system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, is_x); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 81 | [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const { | 126 | [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const { |
| 82 | const Common::PhysicalAddress paddr{ | 127 | const Common::PhysicalAddress paddr{ |
| 83 | current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]}; | 128 | current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]}; |
| @@ -831,14 +876,19 @@ void Memory::SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) { | |||
| 831 | } | 876 | } |
| 832 | 877 | ||
| 833 | void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, | 878 | void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, |
| 834 | Common::PhysicalAddress target) { | 879 | Common::PhysicalAddress target, Common::MemoryPermission perms) { |
| 835 | impl->MapMemoryRegion(page_table, base, size, target); | 880 | impl->MapMemoryRegion(page_table, base, size, target, perms); |
| 836 | } | 881 | } |
| 837 | 882 | ||
| 838 | void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { | 883 | void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { |
| 839 | impl->UnmapRegion(page_table, base, size); | 884 | impl->UnmapRegion(page_table, base, size); |
| 840 | } | 885 | } |
| 841 | 886 | ||
| 887 | void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size, | ||
| 888 | Common::MemoryPermission perms) { | ||
| 889 | impl->ProtectRegion(page_table, GetInteger(vaddr), size, perms); | ||
| 890 | } | ||
| 891 | |||
| 842 | bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { | 892 | bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { |
| 843 | const Kernel::KProcess& process = *system.ApplicationProcess(); | 893 | const Kernel::KProcess& process = *system.ApplicationProcess(); |
| 844 | const auto& page_table = process.GetPageTable().GetImpl(); | 894 | const auto& page_table = process.GetPageTable().GetImpl(); |
| @@ -1001,4 +1051,17 @@ void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) { | |||
| 1001 | impl->FlushRegion(dest_addr, size); | 1051 | impl->FlushRegion(dest_addr, size); |
| 1002 | } | 1052 | } |
| 1003 | 1053 | ||
| 1054 | bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { | ||
| 1055 | bool mapped = true; | ||
| 1056 | u8* const ptr = impl->GetPointerImpl( | ||
| 1057 | GetInteger(vaddr), | ||
| 1058 | [&] { | ||
| 1059 | LOG_ERROR(HW_Memory, "Unmapped InvalidateNCE for {} bytes @ {:#x}", size, | ||
| 1060 | GetInteger(vaddr)); | ||
| 1061 | mapped = false; | ||
| 1062 | }, | ||
| 1063 | [&] { impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); }); | ||
| 1064 | return mapped && ptr != nullptr; | ||
| 1065 | } | ||
| 1066 | |||
| 1004 | } // namespace Core::Memory | 1067 | } // namespace Core::Memory |
diff --git a/src/core/memory.h b/src/core/memory.h index 13047a545..ed8ebb5eb 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -15,8 +15,9 @@ | |||
| 15 | #include "core/hle/result.h" | 15 | #include "core/hle/result.h" |
| 16 | 16 | ||
| 17 | namespace Common { | 17 | namespace Common { |
| 18 | enum class MemoryPermission : u32; | ||
| 18 | struct PageTable; | 19 | struct PageTable; |
| 19 | } | 20 | } // namespace Common |
| 20 | 21 | ||
| 21 | namespace Core { | 22 | namespace Core { |
| 22 | class System; | 23 | class System; |
| @@ -82,9 +83,10 @@ public: | |||
| 82 | * @param size The amount of bytes to map. Must be page-aligned. | 83 | * @param size The amount of bytes to map. Must be page-aligned. |
| 83 | * @param target Buffer with the memory backing the mapping. Must be of length at least | 84 | * @param target Buffer with the memory backing the mapping. Must be of length at least |
| 84 | * `size`. | 85 | * `size`. |
| 86 | * @param perms The permissions to map the memory with. | ||
| 85 | */ | 87 | */ |
| 86 | void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, | 88 | void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, |
| 87 | Common::PhysicalAddress target); | 89 | Common::PhysicalAddress target, Common::MemoryPermission perms); |
| 88 | 90 | ||
| 89 | /** | 91 | /** |
| 90 | * Unmaps a region of the emulated process address space. | 92 | * Unmaps a region of the emulated process address space. |
| @@ -96,6 +98,17 @@ public: | |||
| 96 | void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size); | 98 | void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size); |
| 97 | 99 | ||
| 98 | /** | 100 | /** |
| 101 | * Protects a region of the emulated process address space with the new permissions. | ||
| 102 | * | ||
| 103 | * @param page_table The page table of the emulated process. | ||
| 104 | * @param base The start address to re-protect. Must be page-aligned. | ||
| 105 | * @param size The amount of bytes to protect. Must be page-aligned. | ||
| 106 | * @param perms The permissions the address range is mapped. | ||
| 107 | */ | ||
| 108 | void ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, | ||
| 109 | Common::MemoryPermission perms); | ||
| 110 | |||
| 111 | /** | ||
| 99 | * Checks whether or not the supplied address is a valid virtual | 112 | * Checks whether or not the supplied address is a valid virtual |
| 100 | * address for the current process. | 113 | * address for the current process. |
| 101 | * | 114 | * |
| @@ -472,6 +485,7 @@ public: | |||
| 472 | 485 | ||
| 473 | void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); | 486 | void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); |
| 474 | void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size); | 487 | void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size); |
| 488 | bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size); | ||
| 475 | void FlushRegion(Common::ProcessAddress dest_addr, size_t size); | 489 | void FlushRegion(Common::ProcessAddress dest_addr, size_t size); |
| 476 | 490 | ||
| 477 | private: | 491 | private: |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index da140c01c..db30ba598 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -68,10 +68,7 @@ u64 StandardVmCallbacks::HidKeysDown() { | |||
| 68 | return 0; | 68 | return 0; |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | const auto press_state = | 71 | const auto press_state = applet_resource->GetNpad()->GetAndResetPressState(); |
| 72 | applet_resource | ||
| 73 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad) | ||
| 74 | .GetAndResetPressState(); | ||
| 75 | return static_cast<u64>(press_state & HID::NpadButton::All); | 72 | return static_cast<u64>(press_state & HID::NpadButton::All); |
| 76 | } | 73 | } |
| 77 | 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..1a0491c2c --- /dev/null +++ b/src/frontend_common/config.cpp | |||
| @@ -0,0 +1,1010 @@ | |||
| 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 | #ifndef ANDROID | ||
| 928 | if (string.substr(0, 2) == "//") { | ||
| 929 | boost::replace_all(adjusted_string, "//", "/"); | ||
| 930 | adjusted_string.insert(0, "/"); | ||
| 931 | } else { | ||
| 932 | boost::replace_all(adjusted_string, "//", "/"); | ||
| 933 | } | ||
| 934 | #endif | ||
| 935 | |||
| 936 | // Needed for backwards compatibility with QSettings deserialization | ||
| 937 | for (const auto& special_character : special_characters) { | ||
| 938 | if (adjusted_string.find(special_character) != std::string::npos) { | ||
| 939 | adjusted_string.insert(0, "\""); | ||
| 940 | adjusted_string.append("\""); | ||
| 941 | break; | ||
| 942 | } | ||
| 943 | } | ||
| 944 | return adjusted_string; | ||
| 945 | } | ||
| 946 | |||
| 947 | std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) { | ||
| 948 | if (array_stack.empty()) { | ||
| 949 | return std::string(GetGroup()).append(AdjustKey(key)); | ||
| 950 | } | ||
| 951 | |||
| 952 | std::string array_key; | ||
| 953 | for (size_t i = 0; i < array_stack.size(); ++i) { | ||
| 954 | if (!array_stack[i].name.empty()) { | ||
| 955 | array_key.append(array_stack[i].name).append("\\"); | ||
| 956 | } | ||
| 957 | |||
| 958 | if (!skipArrayIndex || (array_stack.size() - 1 != i && array_stack.size() > 1)) { | ||
| 959 | array_key.append(ToString(array_stack[i].index)).append("\\"); | ||
| 960 | } | ||
| 961 | } | ||
| 962 | std::string final_key = std::string(GetGroup()).append(array_key).append(AdjustKey(key)); | ||
| 963 | return final_key; | ||
| 964 | } | ||
| 965 | |||
| 966 | int Config::BeginArray(const std::string& array) { | ||
| 967 | array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0}); | ||
| 968 | const int size = config->GetLongValue(GetSection().c_str(), | ||
| 969 | GetFullKey(std::string("size"), true).c_str(), 0); | ||
| 970 | array_stack.back().size = size; | ||
| 971 | return size; | ||
| 972 | } | ||
| 973 | |||
| 974 | void Config::EndArray() { | ||
| 975 | // You can't end a config array before starting one | ||
| 976 | ASSERT(!array_stack.empty()); | ||
| 977 | |||
| 978 | // Set the array size to 0 if the array is ended without changing the index | ||
| 979 | int size = 0; | ||
| 980 | if (array_stack.back().index != 0) { | ||
| 981 | size = array_stack.back().size; | ||
| 982 | } | ||
| 983 | |||
| 984 | // Write out the size to config | ||
| 985 | if (key_stack.size() == 1 && array_stack.back().name.empty()) { | ||
| 986 | // Edge-case where the first array created doesn't have a name | ||
| 987 | config->SetValue(GetSection().c_str(), std::string("size").c_str(), ToString(size).c_str()); | ||
| 988 | } else { | ||
| 989 | const auto key = GetFullKey(std::string("size"), true); | ||
| 990 | config->SetValue(GetSection().c_str(), key.c_str(), ToString(size).c_str()); | ||
| 991 | } | ||
| 992 | |||
| 993 | array_stack.pop_back(); | ||
| 994 | } | ||
| 995 | |||
| 996 | void Config::SetArrayIndex(const int index) { | ||
| 997 | // You can't set the array index if you haven't started one yet | ||
| 998 | ASSERT(!array_stack.empty()); | ||
| 999 | |||
| 1000 | const int array_index = index + 1; | ||
| 1001 | |||
| 1002 | // You can't exceed the known max size of the array by more than 1 | ||
| 1003 | ASSERT(array_stack.front().size + 1 >= array_index); | ||
| 1004 | |||
| 1005 | // Change the config array size to the current index since you may want | ||
| 1006 | // to reduce the number of elements that you read back from the config | ||
| 1007 | // in the future. | ||
| 1008 | array_stack.back().size = array_index; | ||
| 1009 | array_stack.back().index = array_index; | ||
| 1010 | } | ||
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/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp index 2705ab140..9319ea007 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | 5 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" |
| 6 | #include "shader_recompiler/frontend/ir/program.h" | 6 | #include "shader_recompiler/frontend/ir/program.h" |
| 7 | #include "shader_recompiler/frontend/ir/value.h" | 7 | #include "shader_recompiler/frontend/ir/value.h" |
| 8 | #include "shader_recompiler/profile.h" | ||
| 8 | #include "shader_recompiler/runtime_info.h" | 9 | #include "shader_recompiler/runtime_info.h" |
| 9 | 10 | ||
| 10 | namespace Shader::Backend::GLASM { | 11 | namespace Shader::Backend::GLASM { |
| @@ -35,7 +36,9 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std | |||
| 35 | continue; | 36 | continue; |
| 36 | } | 37 | } |
| 37 | const auto& ssbo{ctx.info.storage_buffers_descriptors[index]}; | 38 | const auto& ssbo{ctx.info.storage_buffers_descriptors[index]}; |
| 38 | ctx.Add("LDC.U64 DC.x,c{}[{}];" // ssbo_addr | 39 | const u64 ssbo_align_mask{~(ctx.profile.min_ssbo_alignment - 1U)}; |
| 40 | ctx.Add("LDC.U64 DC.x,c{}[{}];" // unaligned_ssbo_addr | ||
| 41 | "AND.U64 DC.x,DC.x,{};" // ssbo_addr = unaligned_ssbo_addr & ssbo_align_mask | ||
| 39 | "LDC.U32 RC.x,c{}[{}];" // ssbo_size_u32 | 42 | "LDC.U32 RC.x,c{}[{}];" // ssbo_size_u32 |
| 40 | "CVT.U64.U32 DC.y,RC.x;" // ssbo_size = ssbo_size_u32 | 43 | "CVT.U64.U32 DC.y,RC.x;" // ssbo_size = ssbo_size_u32 |
| 41 | "ADD.U64 DC.y,DC.y,DC.x;" // ssbo_end = ssbo_addr + ssbo_size | 44 | "ADD.U64 DC.y,DC.y,DC.x;" // ssbo_end = ssbo_addr + ssbo_size |
| @@ -44,8 +47,8 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std | |||
| 44 | "AND.U.CC RC.x,RC.x,RC.y;" // cond = a && b | 47 | "AND.U.CC RC.x,RC.x,RC.y;" // cond = a && b |
| 45 | "IF NE.x;" // if cond | 48 | "IF NE.x;" // if cond |
| 46 | "SUB.U64 DC.x,{}.x,DC.x;", // offset = input_addr - ssbo_addr | 49 | "SUB.U64 DC.x,{}.x,DC.x;", // offset = input_addr - ssbo_addr |
| 47 | ssbo.cbuf_index, ssbo.cbuf_offset, ssbo.cbuf_index, ssbo.cbuf_offset + 8, address, | 50 | ssbo.cbuf_index, ssbo.cbuf_offset, ssbo_align_mask, ssbo.cbuf_index, |
| 48 | address, address); | 51 | ssbo.cbuf_offset + 8, address, address, address); |
| 49 | if (pointer_based) { | 52 | if (pointer_based) { |
| 50 | ctx.Add("PK64.U DC.y,c[{}];" // host_ssbo = cbuf | 53 | ctx.Add("PK64.U DC.y,c[{}];" // host_ssbo = cbuf |
| 51 | "ADD.U64 DC.x,DC.x,DC.y;" // host_addr = host_ssbo + offset | 54 | "ADD.U64 DC.x,DC.x,DC.y;" // host_addr = host_ssbo + offset |
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index 9ff4028c2..b2ceeefc4 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp | |||
| @@ -1,6 +1,7 @@ | |||
| 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 | #include "common/div_ceil.h" | ||
| 4 | #include "shader_recompiler/backend/bindings.h" | 5 | #include "shader_recompiler/backend/bindings.h" |
| 5 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" | 6 | #include "shader_recompiler/backend/glsl/glsl_emit_context.h" |
| 6 | #include "shader_recompiler/frontend/ir/program.h" | 7 | #include "shader_recompiler/frontend/ir/program.h" |
| @@ -431,9 +432,11 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) { | |||
| 431 | } | 432 | } |
| 432 | for (const auto& desc : info.constant_buffer_descriptors) { | 433 | for (const auto& desc : info.constant_buffer_descriptors) { |
| 433 | const auto cbuf_type{profile.has_gl_cbuf_ftou_bug ? "uvec4" : "vec4"}; | 434 | const auto cbuf_type{profile.has_gl_cbuf_ftou_bug ? "uvec4" : "vec4"}; |
| 435 | const u32 cbuf_used_size{Common::DivCeil(info.constant_buffer_used_sizes[desc.index], 16U)}; | ||
| 436 | const u32 cbuf_binding_size{info.uses_global_memory ? 0x1000U : cbuf_used_size}; | ||
| 434 | header += fmt::format("layout(std140,binding={}) uniform {}_cbuf_{}{{{} {}_cbuf{}[{}];}};", | 437 | header += fmt::format("layout(std140,binding={}) uniform {}_cbuf_{}{{{} {}_cbuf{}[{}];}};", |
| 435 | bindings.uniform_buffer, stage_name, desc.index, cbuf_type, | 438 | bindings.uniform_buffer, stage_name, desc.index, cbuf_type, |
| 436 | stage_name, desc.index, 4 * 1024); | 439 | stage_name, desc.index, cbuf_binding_size); |
| 437 | bindings.uniform_buffer += desc.count; | 440 | bindings.uniform_buffer += desc.count; |
| 438 | } | 441 | } |
| 439 | } | 442 | } |
| @@ -601,7 +604,10 @@ std::string EmitContext::DefineGlobalMemoryFunctions() { | |||
| 601 | addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc)); | 604 | addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc)); |
| 602 | size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc)); | 605 | size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc)); |
| 603 | } | 606 | } |
| 604 | const auto addr_pack{fmt::format("packUint2x32(uvec2({},{}))", addr_xy[0], addr_xy[1])}; | 607 | const u32 ssbo_align_mask{~(static_cast<u32>(profile.min_ssbo_alignment) - 1U)}; |
| 608 | const auto aligned_low_addr{fmt::format("{}&{}", addr_xy[0], ssbo_align_mask)}; | ||
| 609 | const auto aligned_addr{fmt::format("uvec2({},{})", aligned_low_addr, addr_xy[1])}; | ||
| 610 | const auto addr_pack{fmt::format("packUint2x32({})", aligned_addr)}; | ||
| 605 | const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)}; | 611 | const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)}; |
| 606 | func += addr_statment; | 612 | func += addr_statment; |
| 607 | 613 | ||
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/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 57df6fc34..3350f1f85 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | |||
| @@ -891,7 +891,9 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { | |||
| 891 | const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32, | 891 | const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32, |
| 892 | zero, ssbo_size_cbuf_offset)}; | 892 | zero, ssbo_size_cbuf_offset)}; |
| 893 | 893 | ||
| 894 | const Id ssbo_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; | 894 | const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)}; |
| 895 | const Id unaligned_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; | ||
| 896 | const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))}; | ||
| 895 | const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))}; | 897 | const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))}; |
| 896 | const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)}; | 898 | const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)}; |
| 897 | const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr), | 899 | const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr), |
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 8fac6bad3..321ea625b 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp | |||
| @@ -298,7 +298,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo | |||
| 298 | 298 | ||
| 299 | Optimization::PositionPass(env, program); | 299 | Optimization::PositionPass(env, program); |
| 300 | 300 | ||
| 301 | Optimization::GlobalMemoryToStorageBufferPass(program); | 301 | Optimization::GlobalMemoryToStorageBufferPass(program, host_info); |
| 302 | Optimization::TexturePass(env, program, host_info); | 302 | Optimization::TexturePass(env, program, host_info); |
| 303 | 303 | ||
| 304 | if (Settings::values.resolution_info.active) { | 304 | if (Settings::values.resolution_info.active) { |
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h index 7d2ded907..1b53404fc 100644 --- a/src/shader_recompiler/host_translate_info.h +++ b/src/shader_recompiler/host_translate_info.h | |||
| @@ -16,6 +16,7 @@ struct HostTranslateInfo { | |||
| 16 | bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered | 16 | bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered |
| 17 | bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers | 17 | bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers |
| 18 | bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS | 18 | bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS |
| 19 | u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs | ||
| 19 | bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry | 20 | bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry |
| 20 | ///< passthrough shaders | 21 | ///< passthrough shaders |
| 21 | bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional | 22 | bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional |
diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp index d1e59f22e..0cea79945 100644 --- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp +++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "shader_recompiler/frontend/ir/breadth_first_search.h" | 11 | #include "shader_recompiler/frontend/ir/breadth_first_search.h" |
| 12 | #include "shader_recompiler/frontend/ir/ir_emitter.h" | 12 | #include "shader_recompiler/frontend/ir/ir_emitter.h" |
| 13 | #include "shader_recompiler/frontend/ir/value.h" | 13 | #include "shader_recompiler/frontend/ir/value.h" |
| 14 | #include "shader_recompiler/host_translate_info.h" | ||
| 14 | #include "shader_recompiler/ir_opt/passes.h" | 15 | #include "shader_recompiler/ir_opt/passes.h" |
| 15 | 16 | ||
| 16 | namespace Shader::Optimization { | 17 | namespace Shader::Optimization { |
| @@ -408,7 +409,7 @@ void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info) | |||
| 408 | } | 409 | } |
| 409 | 410 | ||
| 410 | /// Returns the offset in indices (not bytes) for an equivalent storage instruction | 411 | /// Returns the offset in indices (not bytes) for an equivalent storage instruction |
| 411 | IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer) { | 412 | IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer, u32 alignment) { |
| 412 | IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; | 413 | IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; |
| 413 | IR::U32 offset; | 414 | IR::U32 offset; |
| 414 | if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) { | 415 | if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) { |
| @@ -421,7 +422,10 @@ IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer | |||
| 421 | } | 422 | } |
| 422 | // Subtract the least significant 32 bits from the guest offset. The result is the storage | 423 | // Subtract the least significant 32 bits from the guest offset. The result is the storage |
| 423 | // buffer offset in bytes. | 424 | // buffer offset in bytes. |
| 424 | const IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))}; | 425 | IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))}; |
| 426 | |||
| 427 | // Align the offset base to match the host alignment requirements | ||
| 428 | low_cbuf = ir.BitwiseAnd(low_cbuf, ir.Imm32(~(alignment - 1U))); | ||
| 425 | return ir.ISub(offset, low_cbuf); | 429 | return ir.ISub(offset, low_cbuf); |
| 426 | } | 430 | } |
| 427 | 431 | ||
| @@ -516,7 +520,7 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index, | |||
| 516 | } | 520 | } |
| 517 | } // Anonymous namespace | 521 | } // Anonymous namespace |
| 518 | 522 | ||
| 519 | void GlobalMemoryToStorageBufferPass(IR::Program& program) { | 523 | void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info) { |
| 520 | StorageInfo info; | 524 | StorageInfo info; |
| 521 | for (IR::Block* const block : program.post_order_blocks) { | 525 | for (IR::Block* const block : program.post_order_blocks) { |
| 522 | for (IR::Inst& inst : block->Instructions()) { | 526 | for (IR::Inst& inst : block->Instructions()) { |
| @@ -540,7 +544,8 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program) { | |||
| 540 | const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}}; | 544 | const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}}; |
| 541 | IR::Block* const block{storage_inst.block}; | 545 | IR::Block* const block{storage_inst.block}; |
| 542 | IR::Inst* const inst{storage_inst.inst}; | 546 | IR::Inst* const inst{storage_inst.inst}; |
| 543 | const IR::U32 offset{StorageOffset(*block, *inst, storage_buffer)}; | 547 | const IR::U32 offset{ |
| 548 | StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)}; | ||
| 544 | Replace(*block, *inst, index, offset); | 549 | Replace(*block, *inst, index, offset); |
| 545 | } | 550 | } |
| 546 | } | 551 | } |
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index d4d5285e5..1e637cb23 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h | |||
| @@ -16,7 +16,7 @@ void CollectShaderInfoPass(Environment& env, IR::Program& program); | |||
| 16 | void ConditionalBarrierPass(IR::Program& program); | 16 | void ConditionalBarrierPass(IR::Program& program); |
| 17 | void ConstantPropagationPass(Environment& env, IR::Program& program); | 17 | void ConstantPropagationPass(Environment& env, IR::Program& program); |
| 18 | void DeadCodeEliminationPass(IR::Program& program); | 18 | void DeadCodeEliminationPass(IR::Program& program); |
| 19 | void GlobalMemoryToStorageBufferPass(IR::Program& program); | 19 | void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info); |
| 20 | void IdentityRemovalPass(IR::Program& program); | 20 | void IdentityRemovalPass(IR::Program& program); |
| 21 | void LowerFp64ToFp32(IR::Program& program); | 21 | void LowerFp64ToFp32(IR::Program& program); |
| 22 | void LowerFp16ToFp32(IR::Program& program); | 22 | void LowerFp16ToFp32(IR::Program& program); |
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 38d820db2..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 | ||
| @@ -84,6 +85,8 @@ struct Profile { | |||
| 84 | 85 | ||
| 85 | /// Maxwell and earlier nVidia architectures have broken robust support | 86 | /// Maxwell and earlier nVidia architectures have broken robust support |
| 86 | bool has_broken_robust{}; | 87 | bool has_broken_robust{}; |
| 88 | |||
| 89 | u64 min_ssbo_alignment{}; | ||
| 87 | }; | 90 | }; |
| 88 | 91 | ||
| 89 | } // namespace Shader | 92 | } // namespace Shader |
diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp index 1b014b632..1a28e862b 100644 --- a/src/tests/common/host_memory.cpp +++ b/src/tests/common/host_memory.cpp | |||
| @@ -11,6 +11,7 @@ using namespace Common::Literals; | |||
| 11 | 11 | ||
| 12 | static constexpr size_t VIRTUAL_SIZE = 1ULL << 39; | 12 | static constexpr size_t VIRTUAL_SIZE = 1ULL << 39; |
| 13 | static constexpr size_t BACKING_SIZE = 4_GiB; | 13 | static constexpr size_t BACKING_SIZE = 4_GiB; |
| 14 | static constexpr auto PERMS = Common::MemoryPermission::ReadWrite; | ||
| 14 | 15 | ||
| 15 | TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { | 16 | TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { |
| 16 | { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); } | 17 | { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); } |
| @@ -19,7 +20,7 @@ TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { | |||
| 19 | 20 | ||
| 20 | TEST_CASE("HostMemory: Simple map", "[common]") { | 21 | TEST_CASE("HostMemory: Simple map", "[common]") { |
| 21 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 22 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 22 | mem.Map(0x5000, 0x8000, 0x1000); | 23 | mem.Map(0x5000, 0x8000, 0x1000, PERMS); |
| 23 | 24 | ||
| 24 | volatile u8* const data = mem.VirtualBasePointer() + 0x5000; | 25 | volatile u8* const data = mem.VirtualBasePointer() + 0x5000; |
| 25 | data[0] = 50; | 26 | data[0] = 50; |
| @@ -28,8 +29,8 @@ TEST_CASE("HostMemory: Simple map", "[common]") { | |||
| 28 | 29 | ||
| 29 | TEST_CASE("HostMemory: Simple mirror map", "[common]") { | 30 | TEST_CASE("HostMemory: Simple mirror map", "[common]") { |
| 30 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 31 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 31 | mem.Map(0x5000, 0x3000, 0x2000); | 32 | mem.Map(0x5000, 0x3000, 0x2000, PERMS); |
| 32 | mem.Map(0x8000, 0x4000, 0x1000); | 33 | mem.Map(0x8000, 0x4000, 0x1000, PERMS); |
| 33 | 34 | ||
| 34 | volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000; | 35 | volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000; |
| 35 | volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000; | 36 | volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000; |
| @@ -39,7 +40,7 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") { | |||
| 39 | 40 | ||
| 40 | TEST_CASE("HostMemory: Simple unmap", "[common]") { | 41 | TEST_CASE("HostMemory: Simple unmap", "[common]") { |
| 41 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 42 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 42 | mem.Map(0x5000, 0x3000, 0x2000); | 43 | mem.Map(0x5000, 0x3000, 0x2000, PERMS); |
| 43 | 44 | ||
| 44 | volatile u8* const data = mem.VirtualBasePointer() + 0x5000; | 45 | volatile u8* const data = mem.VirtualBasePointer() + 0x5000; |
| 45 | data[75] = 50; | 46 | data[75] = 50; |
| @@ -50,7 +51,7 @@ TEST_CASE("HostMemory: Simple unmap", "[common]") { | |||
| 50 | 51 | ||
| 51 | TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { | 52 | TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { |
| 52 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 53 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 53 | mem.Map(0x5000, 0x3000, 0x2000); | 54 | mem.Map(0x5000, 0x3000, 0x2000, PERMS); |
| 54 | 55 | ||
| 55 | volatile u8* const data = mem.VirtualBasePointer() + 0x5000; | 56 | volatile u8* const data = mem.VirtualBasePointer() + 0x5000; |
| 56 | data[0] = 50; | 57 | data[0] = 50; |
| @@ -58,79 +59,79 @@ TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { | |||
| 58 | 59 | ||
| 59 | mem.Unmap(0x5000, 0x2000); | 60 | mem.Unmap(0x5000, 0x2000); |
| 60 | 61 | ||
| 61 | mem.Map(0x5000, 0x3000, 0x2000); | 62 | mem.Map(0x5000, 0x3000, 0x2000, PERMS); |
| 62 | REQUIRE(data[0] == 50); | 63 | REQUIRE(data[0] == 50); |
| 63 | 64 | ||
| 64 | mem.Map(0x7000, 0x2000, 0x5000); | 65 | mem.Map(0x7000, 0x2000, 0x5000, PERMS); |
| 65 | REQUIRE(data[0x3000] == 50); | 66 | REQUIRE(data[0x3000] == 50); |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | TEST_CASE("HostMemory: Nieche allocation", "[common]") { | 69 | TEST_CASE("HostMemory: Nieche allocation", "[common]") { |
| 69 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 70 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 70 | mem.Map(0x0000, 0, 0x20000); | 71 | mem.Map(0x0000, 0, 0x20000, PERMS); |
| 71 | mem.Unmap(0x0000, 0x4000); | 72 | mem.Unmap(0x0000, 0x4000); |
| 72 | mem.Map(0x1000, 0, 0x2000); | 73 | mem.Map(0x1000, 0, 0x2000, PERMS); |
| 73 | mem.Map(0x3000, 0, 0x1000); | 74 | mem.Map(0x3000, 0, 0x1000, PERMS); |
| 74 | mem.Map(0, 0, 0x1000); | 75 | mem.Map(0, 0, 0x1000, PERMS); |
| 75 | } | 76 | } |
| 76 | 77 | ||
| 77 | TEST_CASE("HostMemory: Full unmap", "[common]") { | 78 | TEST_CASE("HostMemory: Full unmap", "[common]") { |
| 78 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 79 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 79 | mem.Map(0x8000, 0, 0x4000); | 80 | mem.Map(0x8000, 0, 0x4000, PERMS); |
| 80 | mem.Unmap(0x8000, 0x4000); | 81 | mem.Unmap(0x8000, 0x4000); |
| 81 | mem.Map(0x6000, 0, 0x16000); | 82 | mem.Map(0x6000, 0, 0x16000, PERMS); |
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") { | 85 | TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") { |
| 85 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 86 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 86 | mem.Map(0x0000, 0, 0x4000); | 87 | mem.Map(0x0000, 0, 0x4000, PERMS); |
| 87 | mem.Unmap(0x2000, 0x4000); | 88 | mem.Unmap(0x2000, 0x4000); |
| 88 | mem.Map(0x2000, 0x80000, 0x4000); | 89 | mem.Map(0x2000, 0x80000, 0x4000, PERMS); |
| 89 | } | 90 | } |
| 90 | 91 | ||
| 91 | TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") { | 92 | TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") { |
| 92 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 93 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 93 | mem.Map(0x8000, 0, 0x4000); | 94 | mem.Map(0x8000, 0, 0x4000, PERMS); |
| 94 | mem.Unmap(0x6000, 0x4000); | 95 | mem.Unmap(0x6000, 0x4000); |
| 95 | mem.Map(0x8000, 0, 0x2000); | 96 | mem.Map(0x8000, 0, 0x2000, PERMS); |
| 96 | } | 97 | } |
| 97 | 98 | ||
| 98 | TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") { | 99 | TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") { |
| 99 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 100 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 100 | mem.Map(0x0000, 0, 0x4000); | 101 | mem.Map(0x0000, 0, 0x4000, PERMS); |
| 101 | mem.Map(0x4000, 0, 0x1b000); | 102 | mem.Map(0x4000, 0, 0x1b000, PERMS); |
| 102 | mem.Unmap(0x3000, 0x1c000); | 103 | mem.Unmap(0x3000, 0x1c000); |
| 103 | mem.Map(0x3000, 0, 0x20000); | 104 | mem.Map(0x3000, 0, 0x20000, PERMS); |
| 104 | } | 105 | } |
| 105 | 106 | ||
| 106 | TEST_CASE("HostMemory: Unmap between placeholders", "[common]") { | 107 | TEST_CASE("HostMemory: Unmap between placeholders", "[common]") { |
| 107 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 108 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 108 | mem.Map(0x0000, 0, 0x4000); | 109 | mem.Map(0x0000, 0, 0x4000, PERMS); |
| 109 | mem.Map(0x4000, 0, 0x4000); | 110 | mem.Map(0x4000, 0, 0x4000, PERMS); |
| 110 | mem.Unmap(0x2000, 0x4000); | 111 | mem.Unmap(0x2000, 0x4000); |
| 111 | mem.Map(0x2000, 0, 0x4000); | 112 | mem.Map(0x2000, 0, 0x4000, PERMS); |
| 112 | } | 113 | } |
| 113 | 114 | ||
| 114 | TEST_CASE("HostMemory: Unmap to origin", "[common]") { | 115 | TEST_CASE("HostMemory: Unmap to origin", "[common]") { |
| 115 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 116 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 116 | mem.Map(0x4000, 0, 0x4000); | 117 | mem.Map(0x4000, 0, 0x4000, PERMS); |
| 117 | mem.Map(0x8000, 0, 0x4000); | 118 | mem.Map(0x8000, 0, 0x4000, PERMS); |
| 118 | mem.Unmap(0x4000, 0x4000); | 119 | mem.Unmap(0x4000, 0x4000); |
| 119 | mem.Map(0, 0, 0x4000); | 120 | mem.Map(0, 0, 0x4000, PERMS); |
| 120 | mem.Map(0x4000, 0, 0x4000); | 121 | mem.Map(0x4000, 0, 0x4000, PERMS); |
| 121 | } | 122 | } |
| 122 | 123 | ||
| 123 | TEST_CASE("HostMemory: Unmap to right", "[common]") { | 124 | TEST_CASE("HostMemory: Unmap to right", "[common]") { |
| 124 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 125 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 125 | mem.Map(0x4000, 0, 0x4000); | 126 | mem.Map(0x4000, 0, 0x4000, PERMS); |
| 126 | mem.Map(0x8000, 0, 0x4000); | 127 | mem.Map(0x8000, 0, 0x4000, PERMS); |
| 127 | mem.Unmap(0x8000, 0x4000); | 128 | mem.Unmap(0x8000, 0x4000); |
| 128 | mem.Map(0x8000, 0, 0x4000); | 129 | mem.Map(0x8000, 0, 0x4000, PERMS); |
| 129 | } | 130 | } |
| 130 | 131 | ||
| 131 | TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { | 132 | TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { |
| 132 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 133 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 133 | mem.Map(0x4000, 0x10000, 0x4000); | 134 | mem.Map(0x4000, 0x10000, 0x4000, PERMS); |
| 134 | 135 | ||
| 135 | volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; | 136 | volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; |
| 136 | ptr[0x1000] = 17; | 137 | ptr[0x1000] = 17; |
| @@ -142,7 +143,7 @@ TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { | |||
| 142 | 143 | ||
| 143 | TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { | 144 | TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { |
| 144 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 145 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 145 | mem.Map(0x4000, 0x10000, 0x4000); | 146 | mem.Map(0x4000, 0x10000, 0x4000, PERMS); |
| 146 | 147 | ||
| 147 | volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; | 148 | volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; |
| 148 | ptr[0x3000] = 19; | 149 | ptr[0x3000] = 19; |
| @@ -156,7 +157,7 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { | |||
| 156 | 157 | ||
| 157 | TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { | 158 | TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { |
| 158 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 159 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 159 | mem.Map(0x4000, 0x10000, 0x4000); | 160 | mem.Map(0x4000, 0x10000, 0x4000, PERMS); |
| 160 | 161 | ||
| 161 | volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; | 162 | volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; |
| 162 | ptr[0x0000] = 19; | 163 | ptr[0x0000] = 19; |
| @@ -170,8 +171,8 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { | |||
| 170 | 171 | ||
| 171 | TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") { | 172 | TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") { |
| 172 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); | 173 | HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); |
| 173 | mem.Map(0x4000, 0x10000, 0x2000); | 174 | mem.Map(0x4000, 0x10000, 0x2000, PERMS); |
| 174 | mem.Map(0x6000, 0x20000, 0x2000); | 175 | mem.Map(0x6000, 0x20000, 0x2000, PERMS); |
| 175 | 176 | ||
| 176 | volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; | 177 | volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; |
| 177 | ptr[0x0000] = 19; | 178 | ptr[0x0000] = 19; |
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 f5b10411b..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 | ||
| @@ -1401,7 +1418,8 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, | |||
| 1401 | .dst_offset = dst_base_offset, | 1418 | .dst_offset = dst_base_offset, |
| 1402 | .size = overlap.SizeBytes(), | 1419 | .size = overlap.SizeBytes(), |
| 1403 | }); | 1420 | }); |
| 1404 | 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); | ||
| 1405 | DeleteBuffer(overlap_id, true); | 1423 | DeleteBuffer(overlap_id, true); |
| 1406 | } | 1424 | } |
| 1407 | 1425 | ||
| @@ -1414,7 +1432,9 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) { | |||
| 1414 | const u32 size = static_cast<u32>(overlap.end - overlap.begin); | 1432 | const u32 size = static_cast<u32>(overlap.end - overlap.begin); |
| 1415 | 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); |
| 1416 | auto& new_buffer = slot_buffers[new_buffer_id]; | 1434 | auto& new_buffer = slot_buffers[new_buffer_id]; |
| 1417 | 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); | ||
| 1418 | for (const BufferId overlap_id : overlap.ids) { | 1438 | for (const BufferId overlap_id : overlap.ids) { |
| 1419 | JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); | 1439 | JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); |
| 1420 | } | 1440 | } |
| @@ -1467,11 +1487,6 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept { | |||
| 1467 | 1487 | ||
| 1468 | template <class P> | 1488 | template <class P> |
| 1469 | bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { | 1489 | bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { |
| 1470 | return SynchronizeBufferImpl(buffer, cpu_addr, size); | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | template <class P> | ||
| 1474 | bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) { | ||
| 1475 | boost::container::small_vector<BufferCopy, 4> copies; | 1490 | boost::container::small_vector<BufferCopy, 4> copies; |
| 1476 | u64 total_size_bytes = 0; | 1491 | u64 total_size_bytes = 0; |
| 1477 | u64 largest_copy = 0; | 1492 | u64 largest_copy = 0; |
| @@ -1494,51 +1509,6 @@ bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s | |||
| 1494 | } | 1509 | } |
| 1495 | 1510 | ||
| 1496 | template <class P> | 1511 | template <class P> |
| 1497 | bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size) { | ||
| 1498 | boost::container::small_vector<BufferCopy, 4> copies; | ||
| 1499 | u64 total_size_bytes = 0; | ||
| 1500 | u64 largest_copy = 0; | ||
| 1501 | IntervalSet found_sets{}; | ||
| 1502 | auto make_copies = [&] { | ||
| 1503 | for (auto& interval : found_sets) { | ||
| 1504 | const std::size_t sub_size = interval.upper() - interval.lower(); | ||
| 1505 | const VAddr cpu_addr_ = interval.lower(); | ||
| 1506 | copies.push_back(BufferCopy{ | ||
| 1507 | .src_offset = total_size_bytes, | ||
| 1508 | .dst_offset = cpu_addr_ - buffer.CpuAddr(), | ||
| 1509 | .size = sub_size, | ||
| 1510 | }); | ||
| 1511 | total_size_bytes += sub_size; | ||
| 1512 | largest_copy = std::max<u64>(largest_copy, sub_size); | ||
| 1513 | } | ||
| 1514 | const std::span<BufferCopy> copies_span(copies.data(), copies.size()); | ||
| 1515 | UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); | ||
| 1516 | }; | ||
| 1517 | memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) { | ||
| 1518 | const VAddr base_adr = cpu_addr_out; | ||
| 1519 | const VAddr end_adr = base_adr + range_size; | ||
| 1520 | const IntervalType add_interval{base_adr, end_adr}; | ||
| 1521 | found_sets.add(add_interval); | ||
| 1522 | }); | ||
| 1523 | if (found_sets.empty()) { | ||
| 1524 | return true; | ||
| 1525 | } | ||
| 1526 | const IntervalType search_interval{cpu_addr, cpu_addr + size}; | ||
| 1527 | auto it = common_ranges.lower_bound(search_interval); | ||
| 1528 | auto it_end = common_ranges.upper_bound(search_interval); | ||
| 1529 | if (it == common_ranges.end()) { | ||
| 1530 | make_copies(); | ||
| 1531 | return false; | ||
| 1532 | } | ||
| 1533 | while (it != it_end) { | ||
| 1534 | found_sets.subtract(*it); | ||
| 1535 | it++; | ||
| 1536 | } | ||
| 1537 | make_copies(); | ||
| 1538 | return false; | ||
| 1539 | } | ||
| 1540 | |||
| 1541 | template <class P> | ||
| 1542 | 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, |
| 1543 | std::span<BufferCopy> copies) { | 1513 | std::span<BufferCopy> copies) { |
| 1544 | if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { | 1514 | if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { |
| @@ -1586,7 +1556,8 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer, | |||
| 1586 | // Apply the staging offset | 1556 | // Apply the staging offset |
| 1587 | copy.src_offset += upload_staging.offset; | 1557 | copy.src_offset += upload_staging.offset; |
| 1588 | } | 1558 | } |
| 1589 | 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); | ||
| 1590 | } | 1561 | } |
| 1591 | } | 1562 | } |
| 1592 | 1563 | ||
| @@ -1628,7 +1599,8 @@ void BufferCache<P>::InlineMemoryImplementation(VAddr dest_address, size_t copy_ | |||
| 1628 | }}; | 1599 | }}; |
| 1629 | u8* const src_pointer = upload_staging.mapped_span.data(); | 1600 | u8* const src_pointer = upload_staging.mapped_span.data(); |
| 1630 | std::memcpy(src_pointer, inlined_buffer.data(), copy_size); | 1601 | std::memcpy(src_pointer, inlined_buffer.data(), copy_size); |
| 1631 | 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); | ||
| 1632 | } else { | 1604 | } else { |
| 1633 | buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); | 1605 | buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); |
| 1634 | } | 1606 | } |
| @@ -1681,8 +1653,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si | |||
| 1681 | for (BufferCopy& copy : copies) { | 1653 | for (BufferCopy& copy : copies) { |
| 1682 | // Modify copies to have the staging offset in mind | 1654 | // Modify copies to have the staging offset in mind |
| 1683 | copy.dst_offset += download_staging.offset; | 1655 | copy.dst_offset += download_staging.offset; |
| 1656 | buffer.MarkUsage(copy.src_offset, copy.size); | ||
| 1684 | } | 1657 | } |
| 1685 | runtime.CopyBuffer(download_staging.buffer, buffer, copies_span); | 1658 | runtime.CopyBuffer(download_staging.buffer, buffer, copies_span, true); |
| 1686 | runtime.Finish(); | 1659 | runtime.Finish(); |
| 1687 | for (const BufferCopy& copy : copies) { | 1660 | for (const BufferCopy& copy : copies) { |
| 1688 | const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset; | 1661 | const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset; |
| @@ -1780,15 +1753,25 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index, | |||
| 1780 | const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr)); | 1753 | const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr)); |
| 1781 | return std::min(memory_layout_size, static_cast<u32>(8_MiB)); | 1754 | return std::min(memory_layout_size, static_cast<u32>(8_MiB)); |
| 1782 | }(); | 1755 | }(); |
| 1783 | const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); | 1756 | // Alignment only applies to the offset of the buffer |
| 1784 | if (!cpu_addr || size == 0) { | 1757 | const u32 alignment = runtime.GetStorageBufferAlignment(); |
| 1758 | const GPUVAddr aligned_gpu_addr = Common::AlignDown(gpu_addr, alignment); | ||
| 1759 | const u32 aligned_size = static_cast<u32>(gpu_addr - aligned_gpu_addr) + size; | ||
| 1760 | |||
| 1761 | const std::optional<VAddr> aligned_cpu_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr); | ||
| 1762 | if (!aligned_cpu_addr || size == 0) { | ||
| 1785 | LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index); | 1763 | LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index); |
| 1786 | return NULL_BINDING; | 1764 | return NULL_BINDING; |
| 1787 | } | 1765 | } |
| 1788 | const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, YUZU_PAGESIZE); | 1766 | const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); |
| 1767 | ASSERT_MSG(cpu_addr, "Unaligned storage buffer address not found for cbuf index {}", | ||
| 1768 | cbuf_index); | ||
| 1769 | // The end address used for size calculation does not need to be aligned | ||
| 1770 | const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE); | ||
| 1771 | |||
| 1789 | const Binding binding{ | 1772 | const Binding binding{ |
| 1790 | .cpu_addr = *cpu_addr, | 1773 | .cpu_addr = *aligned_cpu_addr, |
| 1791 | .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr), | 1774 | .size = is_written ? aligned_size : static_cast<u32>(cpu_end - *aligned_cpu_addr), |
| 1792 | .buffer_id = BufferId{}, | 1775 | .buffer_id = BufferId{}, |
| 1793 | }; | 1776 | }; |
| 1794 | return binding; | 1777 | return binding; |
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..5f8688d31 --- /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_t>(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_t>(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/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_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 9d5209e97..e6c70fb34 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -145,8 +145,12 @@ StagingBufferMap BufferCacheRuntime::UploadStagingBuffer(size_t size) { | |||
| 145 | return staging_buffer_pool.RequestUploadBuffer(size); | 145 | return staging_buffer_pool.RequestUploadBuffer(size); |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | StagingBufferMap BufferCacheRuntime::DownloadStagingBuffer(size_t size) { | 148 | StagingBufferMap BufferCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) { |
| 149 | return staging_buffer_pool.RequestDownloadBuffer(size); | 149 | return staging_buffer_pool.RequestDownloadBuffer(size, deferred); |
| 150 | } | ||
| 151 | |||
| 152 | void BufferCacheRuntime::FreeDeferredStagingBuffer(StagingBufferMap& buffer) { | ||
| 153 | staging_buffer_pool.FreeDeferredStagingBuffer(buffer); | ||
| 150 | } | 154 | } |
| 151 | 155 | ||
| 152 | u64 BufferCacheRuntime::GetDeviceMemoryUsage() const { | 156 | u64 BufferCacheRuntime::GetDeviceMemoryUsage() const { |
| @@ -177,13 +181,14 @@ void BufferCacheRuntime::CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, | |||
| 177 | } | 181 | } |
| 178 | 182 | ||
| 179 | void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, | 183 | void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, |
| 180 | std::span<const VideoCommon::BufferCopy> copies, bool barrier) { | 184 | std::span<const VideoCommon::BufferCopy> copies, bool barrier, |
| 185 | bool) { | ||
| 181 | CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier); | 186 | CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier); |
| 182 | } | 187 | } |
| 183 | 188 | ||
| 184 | void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, | 189 | void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, |
| 185 | std::span<const VideoCommon::BufferCopy> copies) { | 190 | std::span<const VideoCommon::BufferCopy> copies, bool) { |
| 186 | CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies); | 191 | CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies, true); |
| 187 | } | 192 | } |
| 188 | 193 | ||
| 189 | void BufferCacheRuntime::PreCopyBarrier() { | 194 | 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 8613037eb..71cd45d35 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 { |
| @@ -64,24 +66,33 @@ public: | |||
| 64 | 66 | ||
| 65 | [[nodiscard]] StagingBufferMap UploadStagingBuffer(size_t size); | 67 | [[nodiscard]] StagingBufferMap UploadStagingBuffer(size_t size); |
| 66 | 68 | ||
| 67 | [[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size); | 69 | [[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false); |
| 70 | |||
| 71 | void FreeDeferredStagingBuffer(StagingBufferMap& buffer); | ||
| 72 | |||
| 73 | bool CanReorderUpload(const Buffer&, std::span<const VideoCommon::BufferCopy>) { | ||
| 74 | return false; | ||
| 75 | } | ||
| 68 | 76 | ||
| 69 | void CopyBuffer(GLuint dst_buffer, GLuint src_buffer, | 77 | void CopyBuffer(GLuint dst_buffer, GLuint src_buffer, |
| 70 | std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); | 78 | std::span<const VideoCommon::BufferCopy> copies, bool barrier); |
| 71 | 79 | ||
| 72 | void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, | 80 | void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, |
| 73 | std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); | 81 | std::span<const VideoCommon::BufferCopy> copies, bool barrier); |
| 74 | 82 | ||
| 75 | void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, | 83 | void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, |
| 76 | std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); | 84 | std::span<const VideoCommon::BufferCopy> copies, bool barrier, |
| 85 | bool can_reorder_upload = false); | ||
| 77 | 86 | ||
| 78 | void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, | 87 | void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, |
| 79 | std::span<const VideoCommon::BufferCopy> copies); | 88 | std::span<const VideoCommon::BufferCopy> copies, bool); |
| 80 | 89 | ||
| 81 | void PreCopyBarrier(); | 90 | void PreCopyBarrier(); |
| 82 | void PostCopyBarrier(); | 91 | void PostCopyBarrier(); |
| 83 | void Finish(); | 92 | void Finish(); |
| 84 | 93 | ||
| 94 | void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {} | ||
| 95 | |||
| 85 | void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); | 96 | void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); |
| 86 | 97 | ||
| 87 | void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); | 98 | void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); |
| @@ -182,6 +193,10 @@ public: | |||
| 182 | return device.CanReportMemoryUsage(); | 193 | return device.CanReportMemoryUsage(); |
| 183 | } | 194 | } |
| 184 | 195 | ||
| 196 | u32 GetStorageBufferAlignment() const { | ||
| 197 | return static_cast<u32>(device.GetShaderStorageBufferAlignment()); | ||
| 198 | } | ||
| 199 | |||
| 185 | private: | 200 | private: |
| 186 | static constexpr std::array PABO_LUT{ | 201 | static constexpr std::array PABO_LUT{ |
| 187 | GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, | 202 | GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, |
| @@ -232,7 +247,7 @@ struct BufferCacheParams { | |||
| 232 | static constexpr bool NEEDS_BIND_STORAGE_INDEX = true; | 247 | static constexpr bool NEEDS_BIND_STORAGE_INDEX = true; |
| 233 | static constexpr bool USE_MEMORY_MAPS = true; | 248 | static constexpr bool USE_MEMORY_MAPS = true; |
| 234 | static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true; | 249 | static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true; |
| 235 | static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = false; | 250 | static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; |
| 236 | 251 | ||
| 237 | // TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads | 252 | // TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads |
| 238 | static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false; | 253 | static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false; |
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 46d88c664..a6c93068f 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -264,33 +264,33 @@ std::string Device::GetVendorName() const { | |||
| 264 | if (vendor_name == "Intel") { | 264 | if (vendor_name == "Intel") { |
| 265 | // For Mesa, `Intel` is an overloaded vendor string that could mean crocus or iris. | 265 | // For Mesa, `Intel` is an overloaded vendor string that could mean crocus or iris. |
| 266 | // Simply return `INTEL` for those as well as the Windows driver. | 266 | // Simply return `INTEL` for those as well as the Windows driver. |
| 267 | return "INTEL"; | 267 | return "Intel"; |
| 268 | } | 268 | } |
| 269 | if (vendor_name == "Intel Open Source Technology Center") { | 269 | if (vendor_name == "Intel Open Source Technology Center") { |
| 270 | return "I965"; | 270 | return "i965"; |
| 271 | } | 271 | } |
| 272 | if (vendor_name == "Mesa Project") { | 272 | if (vendor_name == "Mesa Project") { |
| 273 | return "I915"; | 273 | return "i915"; |
| 274 | } | 274 | } |
| 275 | if (vendor_name == "Mesa/X.org") { | 275 | if (vendor_name == "Mesa/X.org") { |
| 276 | // This vendor string is overloaded between llvmpipe, softpipe, and virgl, so just return | 276 | // This vendor string is overloaded between llvmpipe, softpipe, and virgl, so just return |
| 277 | // MESA instead of one of those driver names. | 277 | // MESA instead of one of those driver names. |
| 278 | return "MESA"; | 278 | return "Mesa"; |
| 279 | } | 279 | } |
| 280 | if (vendor_name == "AMD") { | 280 | if (vendor_name == "AMD") { |
| 281 | return "RADEONSI"; | 281 | return "RadeonSI"; |
| 282 | } | 282 | } |
| 283 | if (vendor_name == "nouveau") { | 283 | if (vendor_name == "nouveau") { |
| 284 | return "NOUVEAU"; | 284 | return "Nouveau"; |
| 285 | } | 285 | } |
| 286 | if (vendor_name == "X.Org") { | 286 | if (vendor_name == "X.Org") { |
| 287 | return "R600"; | 287 | return "R600"; |
| 288 | } | 288 | } |
| 289 | if (vendor_name == "Collabora Ltd") { | 289 | if (vendor_name == "Collabora Ltd") { |
| 290 | return "ZINK"; | 290 | return "Zink"; |
| 291 | } | 291 | } |
| 292 | if (vendor_name == "Intel Corporation") { | 292 | if (vendor_name == "Intel Corporation") { |
| 293 | return "OPENSWR"; | 293 | return "OpenSWR"; |
| 294 | } | 294 | } |
| 295 | if (vendor_name == "Microsoft Corporation") { | 295 | if (vendor_name == "Microsoft Corporation") { |
| 296 | return "D3D12"; | 296 | return "D3D12"; |
| @@ -299,7 +299,7 @@ std::string Device::GetVendorName() const { | |||
| 299 | // Mesa's tegra driver reports `NVIDIA`. Only present in this list because the default | 299 | // Mesa's tegra driver reports `NVIDIA`. Only present in this list because the default |
| 300 | // strategy would have returned `NVIDIA` here for this driver, the same result as the | 300 | // strategy would have returned `NVIDIA` here for this driver, the same result as the |
| 301 | // proprietary driver. | 301 | // proprietary driver. |
| 302 | return "TEGRA"; | 302 | return "Tegra"; |
| 303 | } | 303 | } |
| 304 | return vendor_name; | 304 | return vendor_name; |
| 305 | } | 305 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 2888e0238..26f2d0ea7 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -232,6 +232,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo | |||
| 232 | .has_gl_bool_ref_bug = device.HasBoolRefBug(), | 232 | .has_gl_bool_ref_bug = device.HasBoolRefBug(), |
| 233 | .ignore_nan_fp_comparisons = true, | 233 | .ignore_nan_fp_comparisons = true, |
| 234 | .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), | 234 | .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), |
| 235 | .min_ssbo_alignment = device.GetShaderStorageBufferAlignment(), | ||
| 235 | }, | 236 | }, |
| 236 | host_info{ | 237 | host_info{ |
| 237 | .support_float64 = true, | 238 | .support_float64 = true, |
| @@ -240,6 +241,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo | |||
| 240 | .needs_demote_reorder = device.IsAmd(), | 241 | .needs_demote_reorder = device.IsAmd(), |
| 241 | .support_snorm_render_buffer = false, | 242 | .support_snorm_render_buffer = false, |
| 242 | .support_viewport_index_layer = device.HasVertexViewportLayer(), | 243 | .support_viewport_index_layer = device.HasVertexViewportLayer(), |
| 244 | .min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()), | ||
| 243 | .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), | 245 | .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), |
| 244 | .support_conditional_barrier = device.SupportsConditionalBarriers(), | 246 | .support_conditional_barrier = device.SupportsConditionalBarriers(), |
| 245 | } { | 247 | } { |
diff --git a/src/video_core/renderer_opengl/gl_staging_buffer_pool.cpp b/src/video_core/renderer_opengl/gl_staging_buffer_pool.cpp index bbb06e51f..cadad6507 100644 --- a/src/video_core/renderer_opengl/gl_staging_buffer_pool.cpp +++ b/src/video_core/renderer_opengl/gl_staging_buffer_pool.cpp | |||
| @@ -28,63 +28,69 @@ StagingBuffers::StagingBuffers(GLenum storage_flags_, GLenum map_flags_) | |||
| 28 | 28 | ||
| 29 | StagingBuffers::~StagingBuffers() = default; | 29 | StagingBuffers::~StagingBuffers() = default; |
| 30 | 30 | ||
| 31 | StagingBufferMap StagingBuffers::RequestMap(size_t requested_size, bool insert_fence) { | 31 | StagingBufferMap StagingBuffers::RequestMap(size_t requested_size, bool insert_fence, |
| 32 | bool deferred) { | ||
| 32 | MICROPROFILE_SCOPE(OpenGL_BufferRequest); | 33 | MICROPROFILE_SCOPE(OpenGL_BufferRequest); |
| 33 | 34 | ||
| 34 | const size_t index = RequestBuffer(requested_size); | 35 | const size_t index = RequestBuffer(requested_size); |
| 35 | OGLSync* const sync = insert_fence ? &syncs[index] : nullptr; | 36 | OGLSync* const sync = insert_fence ? &allocs[index].sync : nullptr; |
| 36 | sync_indices[index] = insert_fence ? ++current_sync_index : 0; | 37 | allocs[index].sync_index = insert_fence ? ++current_sync_index : 0; |
| 38 | allocs[index].deferred = deferred; | ||
| 37 | return StagingBufferMap{ | 39 | return StagingBufferMap{ |
| 38 | .mapped_span = std::span(maps[index], requested_size), | 40 | .mapped_span = std::span(allocs[index].map, requested_size), |
| 39 | .sync = sync, | 41 | .sync = sync, |
| 40 | .buffer = buffers[index].handle, | 42 | .buffer = allocs[index].buffer.handle, |
| 43 | .index = index, | ||
| 41 | }; | 44 | }; |
| 42 | } | 45 | } |
| 43 | 46 | ||
| 47 | void StagingBuffers::FreeDeferredStagingBuffer(size_t index) { | ||
| 48 | ASSERT(allocs[index].deferred); | ||
| 49 | allocs[index].deferred = false; | ||
| 50 | } | ||
| 51 | |||
| 44 | size_t StagingBuffers::RequestBuffer(size_t requested_size) { | 52 | size_t StagingBuffers::RequestBuffer(size_t requested_size) { |
| 45 | if (const std::optional<size_t> index = FindBuffer(requested_size); index) { | 53 | if (const std::optional<size_t> index = FindBuffer(requested_size); index) { |
| 46 | return *index; | 54 | return *index; |
| 47 | } | 55 | } |
| 48 | 56 | StagingBufferAlloc alloc; | |
| 49 | OGLBuffer& buffer = buffers.emplace_back(); | 57 | alloc.buffer.Create(); |
| 50 | buffer.Create(); | ||
| 51 | const auto next_pow2_size = Common::NextPow2(requested_size); | 58 | const auto next_pow2_size = Common::NextPow2(requested_size); |
| 52 | glNamedBufferStorage(buffer.handle, next_pow2_size, nullptr, | 59 | glNamedBufferStorage(alloc.buffer.handle, next_pow2_size, nullptr, |
| 53 | storage_flags | GL_MAP_PERSISTENT_BIT); | 60 | storage_flags | GL_MAP_PERSISTENT_BIT); |
| 54 | maps.push_back(static_cast<u8*>(glMapNamedBufferRange(buffer.handle, 0, next_pow2_size, | 61 | alloc.map = static_cast<u8*>(glMapNamedBufferRange(alloc.buffer.handle, 0, next_pow2_size, |
| 55 | map_flags | GL_MAP_PERSISTENT_BIT))); | 62 | map_flags | GL_MAP_PERSISTENT_BIT)); |
| 56 | syncs.emplace_back(); | 63 | alloc.size = next_pow2_size; |
| 57 | sync_indices.emplace_back(); | 64 | allocs.emplace_back(std::move(alloc)); |
| 58 | sizes.push_back(next_pow2_size); | 65 | return allocs.size() - 1; |
| 59 | |||
| 60 | ASSERT(syncs.size() == buffers.size() && buffers.size() == maps.size() && | ||
| 61 | maps.size() == sizes.size()); | ||
| 62 | |||
| 63 | return buffers.size() - 1; | ||
| 64 | } | 66 | } |
| 65 | 67 | ||
| 66 | std::optional<size_t> StagingBuffers::FindBuffer(size_t requested_size) { | 68 | std::optional<size_t> StagingBuffers::FindBuffer(size_t requested_size) { |
| 67 | size_t known_unsignaled_index = current_sync_index + 1; | 69 | size_t known_unsignaled_index = current_sync_index + 1; |
| 68 | size_t smallest_buffer = std::numeric_limits<size_t>::max(); | 70 | size_t smallest_buffer = std::numeric_limits<size_t>::max(); |
| 69 | std::optional<size_t> found; | 71 | std::optional<size_t> found; |
| 70 | const size_t num_buffers = sizes.size(); | 72 | const size_t num_buffers = allocs.size(); |
| 71 | for (size_t index = 0; index < num_buffers; ++index) { | 73 | for (size_t index = 0; index < num_buffers; ++index) { |
| 72 | const size_t buffer_size = sizes[index]; | 74 | StagingBufferAlloc& alloc = allocs[index]; |
| 75 | const size_t buffer_size = alloc.size; | ||
| 73 | if (buffer_size < requested_size || buffer_size >= smallest_buffer) { | 76 | if (buffer_size < requested_size || buffer_size >= smallest_buffer) { |
| 74 | continue; | 77 | continue; |
| 75 | } | 78 | } |
| 76 | if (syncs[index].handle != 0) { | 79 | if (alloc.deferred) { |
| 77 | if (sync_indices[index] >= known_unsignaled_index) { | 80 | continue; |
| 81 | } | ||
| 82 | if (alloc.sync.handle != 0) { | ||
| 83 | if (alloc.sync_index >= known_unsignaled_index) { | ||
| 78 | // This fence is later than a fence that is known to not be signaled | 84 | // This fence is later than a fence that is known to not be signaled |
| 79 | continue; | 85 | continue; |
| 80 | } | 86 | } |
| 81 | if (!syncs[index].IsSignaled()) { | 87 | if (!alloc.sync.IsSignaled()) { |
| 82 | // Since this fence hasn't been signaled, it's safe to assume all later | 88 | // Since this fence hasn't been signaled, it's safe to assume all later |
| 83 | // fences haven't been signaled either | 89 | // fences haven't been signaled either |
| 84 | known_unsignaled_index = std::min(known_unsignaled_index, sync_indices[index]); | 90 | known_unsignaled_index = std::min(known_unsignaled_index, alloc.sync_index); |
| 85 | continue; | 91 | continue; |
| 86 | } | 92 | } |
| 87 | syncs[index].Release(); | 93 | alloc.sync.Release(); |
| 88 | } | 94 | } |
| 89 | smallest_buffer = buffer_size; | 95 | smallest_buffer = buffer_size; |
| 90 | found = index; | 96 | found = index; |
| @@ -143,8 +149,12 @@ StagingBufferMap StagingBufferPool::RequestUploadBuffer(size_t size) { | |||
| 143 | return upload_buffers.RequestMap(size, true); | 149 | return upload_buffers.RequestMap(size, true); |
| 144 | } | 150 | } |
| 145 | 151 | ||
| 146 | StagingBufferMap StagingBufferPool::RequestDownloadBuffer(size_t size) { | 152 | StagingBufferMap StagingBufferPool::RequestDownloadBuffer(size_t size, bool deferred) { |
| 147 | return download_buffers.RequestMap(size, false); | 153 | return download_buffers.RequestMap(size, false, deferred); |
| 154 | } | ||
| 155 | |||
| 156 | void StagingBufferPool::FreeDeferredStagingBuffer(StagingBufferMap& buffer) { | ||
| 157 | download_buffers.FreeDeferredStagingBuffer(buffer.index); | ||
| 148 | } | 158 | } |
| 149 | 159 | ||
| 150 | } // namespace OpenGL | 160 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_staging_buffer_pool.h b/src/video_core/renderer_opengl/gl_staging_buffer_pool.h index 60f72d3a0..07a56b4d2 100644 --- a/src/video_core/renderer_opengl/gl_staging_buffer_pool.h +++ b/src/video_core/renderer_opengl/gl_staging_buffer_pool.h | |||
| @@ -26,23 +26,30 @@ struct StagingBufferMap { | |||
| 26 | size_t offset = 0; | 26 | size_t offset = 0; |
| 27 | OGLSync* sync; | 27 | OGLSync* sync; |
| 28 | GLuint buffer; | 28 | GLuint buffer; |
| 29 | size_t index; | ||
| 29 | }; | 30 | }; |
| 30 | 31 | ||
| 31 | struct StagingBuffers { | 32 | struct StagingBuffers { |
| 32 | explicit StagingBuffers(GLenum storage_flags_, GLenum map_flags_); | 33 | explicit StagingBuffers(GLenum storage_flags_, GLenum map_flags_); |
| 33 | ~StagingBuffers(); | 34 | ~StagingBuffers(); |
| 34 | 35 | ||
| 35 | StagingBufferMap RequestMap(size_t requested_size, bool insert_fence); | 36 | StagingBufferMap RequestMap(size_t requested_size, bool insert_fence, bool deferred = false); |
| 37 | |||
| 38 | void FreeDeferredStagingBuffer(size_t index); | ||
| 36 | 39 | ||
| 37 | size_t RequestBuffer(size_t requested_size); | 40 | size_t RequestBuffer(size_t requested_size); |
| 38 | 41 | ||
| 39 | std::optional<size_t> FindBuffer(size_t requested_size); | 42 | std::optional<size_t> FindBuffer(size_t requested_size); |
| 40 | 43 | ||
| 41 | std::vector<OGLSync> syncs; | 44 | struct StagingBufferAlloc { |
| 42 | std::vector<OGLBuffer> buffers; | 45 | OGLSync sync; |
| 43 | std::vector<u8*> maps; | 46 | OGLBuffer buffer; |
| 44 | std::vector<size_t> sizes; | 47 | u8* map; |
| 45 | std::vector<size_t> sync_indices; | 48 | size_t size; |
| 49 | size_t sync_index; | ||
| 50 | bool deferred; | ||
| 51 | }; | ||
| 52 | std::vector<StagingBufferAlloc> allocs; | ||
| 46 | GLenum storage_flags; | 53 | GLenum storage_flags; |
| 47 | GLenum map_flags; | 54 | GLenum map_flags; |
| 48 | size_t current_sync_index = 0; | 55 | size_t current_sync_index = 0; |
| @@ -85,7 +92,8 @@ public: | |||
| 85 | ~StagingBufferPool() = default; | 92 | ~StagingBufferPool() = default; |
| 86 | 93 | ||
| 87 | StagingBufferMap RequestUploadBuffer(size_t size); | 94 | StagingBufferMap RequestUploadBuffer(size_t size); |
| 88 | StagingBufferMap RequestDownloadBuffer(size_t size); | 95 | StagingBufferMap RequestDownloadBuffer(size_t size, bool deferred = false); |
| 96 | void FreeDeferredStagingBuffer(StagingBufferMap& buffer); | ||
| 89 | 97 | ||
| 90 | private: | 98 | private: |
| 91 | StagingBuffers upload_buffers{GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT}; | 99 | StagingBuffers upload_buffers{GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT}; |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 512eef575..66a5ca03e 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -557,8 +557,12 @@ StagingBufferMap TextureCacheRuntime::UploadStagingBuffer(size_t size) { | |||
| 557 | return staging_buffer_pool.RequestUploadBuffer(size); | 557 | return staging_buffer_pool.RequestUploadBuffer(size); |
| 558 | } | 558 | } |
| 559 | 559 | ||
| 560 | StagingBufferMap TextureCacheRuntime::DownloadStagingBuffer(size_t size) { | 560 | StagingBufferMap TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) { |
| 561 | return staging_buffer_pool.RequestDownloadBuffer(size); | 561 | return staging_buffer_pool.RequestDownloadBuffer(size, deferred); |
| 562 | } | ||
| 563 | |||
| 564 | void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferMap& buffer) { | ||
| 565 | staging_buffer_pool.FreeDeferredStagingBuffer(buffer); | ||
| 562 | } | 566 | } |
| 563 | 567 | ||
| 564 | u64 TextureCacheRuntime::GetDeviceMemoryUsage() const { | 568 | u64 TextureCacheRuntime::GetDeviceMemoryUsage() const { |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index e71b87e99..34870c81f 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h | |||
| @@ -74,7 +74,9 @@ public: | |||
| 74 | 74 | ||
| 75 | StagingBufferMap UploadStagingBuffer(size_t size); | 75 | StagingBufferMap UploadStagingBuffer(size_t size); |
| 76 | 76 | ||
| 77 | StagingBufferMap DownloadStagingBuffer(size_t size); | 77 | StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false); |
| 78 | |||
| 79 | void FreeDeferredStagingBuffer(StagingBufferMap& buffer); | ||
| 78 | 80 | ||
| 79 | u64 GetDeviceLocalMemory() const { | 81 | u64 GetDeviceLocalMemory() const { |
| 80 | return device_access_memory; | 82 | return device_access_memory; |
| @@ -359,7 +361,7 @@ struct TextureCacheParams { | |||
| 359 | static constexpr bool FRAMEBUFFER_BLITS = true; | 361 | static constexpr bool FRAMEBUFFER_BLITS = true; |
| 360 | static constexpr bool HAS_EMULATED_COPIES = true; | 362 | static constexpr bool HAS_EMULATED_COPIES = true; |
| 361 | static constexpr bool HAS_DEVICE_MEMORY_INFO = true; | 363 | static constexpr bool HAS_DEVICE_MEMORY_INFO = true; |
| 362 | static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = false; | 364 | static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; |
| 363 | 365 | ||
| 364 | using Runtime = OpenGL::TextureCacheRuntime; | 366 | using Runtime = OpenGL::TextureCacheRuntime; |
| 365 | using Image = OpenGL::Image; | 367 | using Image = OpenGL::Image; |
| @@ -367,7 +369,7 @@ struct TextureCacheParams { | |||
| 367 | using ImageView = OpenGL::ImageView; | 369 | using ImageView = OpenGL::ImageView; |
| 368 | using Sampler = OpenGL::Sampler; | 370 | using Sampler = OpenGL::Sampler; |
| 369 | using Framebuffer = OpenGL::Framebuffer; | 371 | using Framebuffer = OpenGL::Framebuffer; |
| 370 | using AsyncBuffer = u32; | 372 | using AsyncBuffer = OpenGL::StagingBufferMap; |
| 371 | using BufferType = GLuint; | 373 | using BufferType = GLuint; |
| 372 | }; | 374 | }; |
| 373 | 375 | ||
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 66483a900..5e461fbd0 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -1211,7 +1211,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { | |||
| 1211 | aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); | 1211 | aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); |
| 1212 | return; | 1212 | return; |
| 1213 | } | 1213 | } |
| 1214 | aa_renderpass = CreateRenderPassImpl(GetFormat(framebuffer)); | 1214 | aa_renderpass = CreateRenderPassImpl(VK_FORMAT_R16G16B16A16_SFLOAT); |
| 1215 | aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); | 1215 | aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); |
| 1216 | 1216 | ||
| 1217 | const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{ | 1217 | const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{ |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index d8148e89a..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 | } |
| @@ -355,12 +355,35 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const { | |||
| 355 | return device.CanReportMemoryUsage(); | 355 | return device.CanReportMemoryUsage(); |
| 356 | } | 356 | } |
| 357 | 357 | ||
| 358 | u32 BufferCacheRuntime::GetStorageBufferAlignment() const { | ||
| 359 | return static_cast<u32>(device.GetStorageBufferAlignment()); | ||
| 360 | } | ||
| 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 | |||
| 358 | void BufferCacheRuntime::Finish() { | 368 | void BufferCacheRuntime::Finish() { |
| 359 | scheduler.Finish(); | 369 | scheduler.Finish(); |
| 360 | } | 370 | } |
| 361 | 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 | |||
| 362 | void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, | 384 | void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, |
| 363 | std::span<const VideoCommon::BufferCopy> copies, bool barrier) { | 385 | std::span<const VideoCommon::BufferCopy> copies, bool barrier, |
| 386 | bool can_reorder_upload) { | ||
| 364 | if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { | 387 | if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { |
| 365 | return; | 388 | return; |
| 366 | } | 389 | } |
| @@ -376,9 +399,18 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, | |||
| 376 | .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, | 399 | .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, |
| 377 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | 400 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, |
| 378 | }; | 401 | }; |
| 402 | |||
| 379 | // 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 |
| 380 | boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size()); | 404 | boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size()); |
| 381 | 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 | |||
| 382 | scheduler.RequestOutsideRenderPassOperationContext(); | 414 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 383 | 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) { |
| 384 | 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 95446c732..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; |
| @@ -75,16 +91,21 @@ public: | |||
| 75 | 91 | ||
| 76 | bool CanReportMemoryUsage() const; | 92 | bool CanReportMemoryUsage() const; |
| 77 | 93 | ||
| 94 | u32 GetStorageBufferAlignment() const; | ||
| 95 | |||
| 78 | [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); | 96 | [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); |
| 79 | 97 | ||
| 80 | [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); | 98 | [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); |
| 81 | 99 | ||
| 100 | bool CanReorderUpload(const Buffer& buffer, std::span<const VideoCommon::BufferCopy> copies); | ||
| 101 | |||
| 82 | void FreeDeferredStagingBuffer(StagingBufferRef& ref); | 102 | void FreeDeferredStagingBuffer(StagingBufferRef& ref); |
| 83 | 103 | ||
| 84 | void PreCopyBarrier(); | 104 | void PreCopyBarrier(); |
| 85 | 105 | ||
| 86 | void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, | 106 | void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, |
| 87 | std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); | 107 | std::span<const VideoCommon::BufferCopy> copies, bool barrier, |
| 108 | bool can_reorder_upload = false); | ||
| 88 | 109 | ||
| 89 | void PostCopyBarrier(); | 110 | void PostCopyBarrier(); |
| 90 | 111 | ||
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 22bf8cc77..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 | ||
| @@ -359,6 +373,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device | |||
| 359 | driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, | 373 | driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, |
| 360 | .has_broken_robust = | 374 | .has_broken_robust = |
| 361 | device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, | 375 | device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, |
| 376 | .min_ssbo_alignment = device.GetStorageBufferAlignment(), | ||
| 362 | }; | 377 | }; |
| 363 | 378 | ||
| 364 | host_info = Shader::HostTranslateInfo{ | 379 | host_info = Shader::HostTranslateInfo{ |
| @@ -369,6 +384,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device | |||
| 369 | driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, | 384 | driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, |
| 370 | .support_snorm_render_buffer = true, | 385 | .support_snorm_render_buffer = true, |
| 371 | .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), | 386 | .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), |
| 387 | .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()), | ||
| 372 | .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), | 388 | .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), |
| 373 | .support_conditional_barrier = device.SupportsConditionalBarriers(), | 389 | .support_conditional_barrier = device.SupportsConditionalBarriers(), |
| 374 | }; | 390 | }; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index e0ab1eaac..07222e603 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -892,10 +892,6 @@ void RasterizerVulkan::UpdateDynamicStates() { | |||
| 892 | UpdateFrontFace(regs); | 892 | UpdateFrontFace(regs); |
| 893 | UpdateStencilOp(regs); | 893 | UpdateStencilOp(regs); |
| 894 | 894 | ||
| 895 | if (device.IsExtVertexInputDynamicStateSupported()) { | ||
| 896 | UpdateVertexInput(regs); | ||
| 897 | } | ||
| 898 | |||
| 899 | if (state_tracker.TouchStateEnable()) { | 895 | if (state_tracker.TouchStateEnable()) { |
| 900 | UpdateDepthBoundsTestEnable(regs); | 896 | UpdateDepthBoundsTestEnable(regs); |
| 901 | UpdateDepthTestEnable(regs); | 897 | UpdateDepthTestEnable(regs); |
| @@ -918,6 +914,9 @@ void RasterizerVulkan::UpdateDynamicStates() { | |||
| 918 | UpdateBlending(regs); | 914 | UpdateBlending(regs); |
| 919 | } | 915 | } |
| 920 | } | 916 | } |
| 917 | if (device.IsExtVertexInputDynamicStateSupported()) { | ||
| 918 | UpdateVertexInput(regs); | ||
| 919 | } | ||
| 921 | } | 920 | } |
| 922 | 921 | ||
| 923 | void RasterizerVulkan::HandleTransformFeedback() { | 922 | void RasterizerVulkan::HandleTransformFeedback() { |
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..38b1619df 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -1439,7 +1439,7 @@ void Image::UploadMemory(const StagingBufferRef& map, std::span<const BufferImag | |||
| 1439 | UploadMemory(map.buffer, map.offset, copies); | 1439 | UploadMemory(map.buffer, map.offset, copies); |
| 1440 | } | 1440 | } |
| 1441 | 1441 | ||
| 1442 | void Image::DownloadMemory(VkBuffer buffer, VkDeviceSize offset, | 1442 | void Image::DownloadMemory(VkBuffer buffer, size_t offset, |
| 1443 | std::span<const VideoCommon::BufferImageCopy> copies) { | 1443 | std::span<const VideoCommon::BufferImageCopy> copies) { |
| 1444 | std::array buffer_handles{ | 1444 | std::array buffer_handles{ |
| 1445 | buffer, | 1445 | buffer, |
| @@ -1450,7 +1450,7 @@ void Image::DownloadMemory(VkBuffer buffer, VkDeviceSize offset, | |||
| 1450 | DownloadMemory(buffer_handles, buffer_offsets, copies); | 1450 | DownloadMemory(buffer_handles, buffer_offsets, copies); |
| 1451 | } | 1451 | } |
| 1452 | 1452 | ||
| 1453 | void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceSize> offsets_span, | 1453 | void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<size_t> offsets_span, |
| 1454 | std::span<const VideoCommon::BufferImageCopy> copies) { | 1454 | std::span<const VideoCommon::BufferImageCopy> copies) { |
| 1455 | const bool is_rescaled = True(flags & ImageFlagBits::Rescaled); | 1455 | const bool is_rescaled = True(flags & ImageFlagBits::Rescaled); |
| 1456 | if (is_rescaled) { | 1456 | if (is_rescaled) { |
| @@ -1530,7 +1530,7 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferIm | |||
| 1530 | map.buffer, | 1530 | map.buffer, |
| 1531 | }; | 1531 | }; |
| 1532 | std::array offsets{ | 1532 | std::array offsets{ |
| 1533 | map.offset, | 1533 | static_cast<size_t>(map.offset), |
| 1534 | }; | 1534 | }; |
| 1535 | DownloadMemory(buffers, offsets, copies); | 1535 | DownloadMemory(buffers, offsets, copies); |
| 1536 | } | 1536 | } |
| @@ -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..0dbde65d6 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -147,10 +147,10 @@ public: | |||
| 147 | void UploadMemory(const StagingBufferRef& map, | 147 | void UploadMemory(const StagingBufferRef& map, |
| 148 | std::span<const VideoCommon::BufferImageCopy> copies); | 148 | std::span<const VideoCommon::BufferImageCopy> copies); |
| 149 | 149 | ||
| 150 | void DownloadMemory(VkBuffer buffer, VkDeviceSize offset, | 150 | void DownloadMemory(VkBuffer buffer, size_t offset, |
| 151 | std::span<const VideoCommon::BufferImageCopy> copies); | 151 | std::span<const VideoCommon::BufferImageCopy> copies); |
| 152 | 152 | ||
| 153 | void DownloadMemory(std::span<VkBuffer> buffers, std::span<VkDeviceSize> offsets, | 153 | void DownloadMemory(std::span<VkBuffer> buffers, std::span<size_t> offsets, |
| 154 | std::span<const VideoCommon::BufferImageCopy> copies); | 154 | std::span<const VideoCommon::BufferImageCopy> copies); |
| 155 | 155 | ||
| 156 | void DownloadMemory(const StagingBufferRef& map, | 156 | void DownloadMemory(const StagingBufferRef& map, |
| @@ -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/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index d575c57ca..dade38b18 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -995,7 +995,7 @@ void TextureCache<P>::DownloadImageIntoBuffer(typename TextureCache<P>::Image* i | |||
| 995 | buffer, | 995 | buffer, |
| 996 | download_map.buffer, | 996 | download_map.buffer, |
| 997 | }; | 997 | }; |
| 998 | std::array<u64, 2> buffer_offsets{ | 998 | std::array<size_t, 2> buffer_offsets{ |
| 999 | buffer_offset, | 999 | buffer_offset, |
| 1000 | download_map.offset, | 1000 | download_map.offset, |
| 1001 | }; | 1001 | }; |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index e518756d2..1fda0042d 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -519,10 +519,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 519 | LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state"); | 519 | LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state"); |
| 520 | RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, | 520 | RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, |
| 521 | VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); | 521 | VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); |
| 522 | |||
| 523 | LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2"); | ||
| 524 | RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, | ||
| 525 | VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); | ||
| 526 | } | 522 | } |
| 527 | 523 | ||
| 528 | if (is_nvidia) { | 524 | if (is_nvidia) { |
| @@ -611,17 +607,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 611 | } | 607 | } |
| 612 | } | 608 | } |
| 613 | if (extensions.vertex_input_dynamic_state && is_qualcomm) { | 609 | if (extensions.vertex_input_dynamic_state && is_qualcomm) { |
| 614 | const u32 version = (properties.properties.driverVersion << 3) >> 3; | 610 | // Qualcomm drivers do not properly support vertex_input_dynamic_state. |
| 615 | if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) && | 611 | LOG_WARNING(Render_Vulkan, |
| 616 | version < VK_MAKE_API_VERSION(0, 0, 680, 0)) { | 612 | "Qualcomm drivers have broken VK_EXT_vertex_input_dynamic_state"); |
| 617 | // Qualcomm Adreno 7xx drivers do not properly support vertex_input_dynamic_state. | 613 | RemoveExtensionFeature(extensions.vertex_input_dynamic_state, |
| 618 | LOG_WARNING( | 614 | features.vertex_input_dynamic_state, |
| 619 | Render_Vulkan, | 615 | VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); |
| 620 | "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state"); | ||
| 621 | RemoveExtensionFeature(extensions.vertex_input_dynamic_state, | ||
| 622 | features.vertex_input_dynamic_state, | ||
| 623 | VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); | ||
| 624 | } | ||
| 625 | } | 616 | } |
| 626 | 617 | ||
| 627 | sets_per_pool = 64; | 618 | sets_per_pool = 64; |
| @@ -635,6 +626,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 635 | has_broken_cube_compatibility = true; | 626 | has_broken_cube_compatibility = true; |
| 636 | } | 627 | } |
| 637 | } | 628 | } |
| 629 | if (is_qualcomm) { | ||
| 630 | const u32 version = (properties.properties.driverVersion << 3) >> 3; | ||
| 631 | if (version < VK_MAKE_API_VERSION(0, 255, 615, 512)) { | ||
| 632 | has_broken_parallel_compiling = true; | ||
| 633 | } | ||
| 634 | } | ||
| 638 | if (extensions.sampler_filter_minmax && is_amd) { | 635 | if (extensions.sampler_filter_minmax && is_amd) { |
| 639 | // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. | 636 | // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. |
| 640 | if (!features.shader_float16_int8.shaderFloat16) { | 637 | if (!features.shader_float16_int8.shaderFloat16) { |
| @@ -698,6 +695,22 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 698 | std::min(properties.properties.limits.maxVertexInputBindings, 16U); | 695 | std::min(properties.properties.limits.maxVertexInputBindings, 16U); |
| 699 | } | 696 | } |
| 700 | 697 | ||
| 698 | if (!extensions.extended_dynamic_state && extensions.extended_dynamic_state2) { | ||
| 699 | LOG_INFO(Render_Vulkan, | ||
| 700 | "Removing extendedDynamicState2 due to missing extendedDynamicState"); | ||
| 701 | RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, | ||
| 702 | VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); | ||
| 703 | } | ||
| 704 | |||
| 705 | if (!extensions.extended_dynamic_state2 && extensions.extended_dynamic_state3) { | ||
| 706 | LOG_INFO(Render_Vulkan, | ||
| 707 | "Removing extendedDynamicState3 due to missing extendedDynamicState2"); | ||
| 708 | RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, | ||
| 709 | VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); | ||
| 710 | dynamic_state3_blending = false; | ||
| 711 | dynamic_state3_enables = false; | ||
| 712 | } | ||
| 713 | |||
| 701 | logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), | 714 | logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), |
| 702 | first_next, dld); | 715 | first_next, dld); |
| 703 | 716 | ||
| @@ -841,11 +854,41 @@ std::string Device::GetDriverName() const { | |||
| 841 | case VK_DRIVER_ID_NVIDIA_PROPRIETARY: | 854 | case VK_DRIVER_ID_NVIDIA_PROPRIETARY: |
| 842 | return "NVIDIA"; | 855 | return "NVIDIA"; |
| 843 | case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: | 856 | case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: |
| 844 | return "INTEL"; | 857 | return "Intel"; |
| 845 | case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA: | 858 | case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA: |
| 846 | return "ANV"; | 859 | return "ANV"; |
| 860 | case VK_DRIVER_ID_IMAGINATION_PROPRIETARY: | ||
| 861 | return "PowerVR"; | ||
| 862 | case VK_DRIVER_ID_QUALCOMM_PROPRIETARY: | ||
| 863 | return "Qualcomm"; | ||
| 864 | case VK_DRIVER_ID_ARM_PROPRIETARY: | ||
| 865 | return "Mali"; | ||
| 866 | case VK_DRIVER_ID_GOOGLE_SWIFTSHADER: | ||
| 867 | return "SwiftShader"; | ||
| 868 | case VK_DRIVER_ID_BROADCOM_PROPRIETARY: | ||
| 869 | return "Broadcom"; | ||
| 847 | case VK_DRIVER_ID_MESA_LLVMPIPE: | 870 | case VK_DRIVER_ID_MESA_LLVMPIPE: |
| 848 | return "LAVAPIPE"; | 871 | return "Lavapipe"; |
| 872 | case VK_DRIVER_ID_MOLTENVK: | ||
| 873 | return "MoltenVK"; | ||
| 874 | case VK_DRIVER_ID_VERISILICON_PROPRIETARY: | ||
| 875 | return "Vivante"; | ||
| 876 | case VK_DRIVER_ID_MESA_TURNIP: | ||
| 877 | return "Turnip"; | ||
| 878 | case VK_DRIVER_ID_MESA_V3DV: | ||
| 879 | return "V3DV"; | ||
| 880 | case VK_DRIVER_ID_MESA_PANVK: | ||
| 881 | return "PanVK"; | ||
| 882 | case VK_DRIVER_ID_MESA_VENUS: | ||
| 883 | return "Venus"; | ||
| 884 | case VK_DRIVER_ID_MESA_DOZEN: | ||
| 885 | return "Dozen"; | ||
| 886 | case VK_DRIVER_ID_MESA_NVK: | ||
| 887 | return "NVK"; | ||
| 888 | case VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA: | ||
| 889 | return "PVR"; | ||
| 890 | // case VK_DRIVER_ID_MESA_AGXV: | ||
| 891 | // return "Asahi"; | ||
| 849 | default: | 892 | default: |
| 850 | return properties.driver.driverName; | 893 | return properties.driver.driverName; |
| 851 | } | 894 | } |
| @@ -863,7 +906,8 @@ bool Device::ShouldBoostClocks() const { | |||
| 863 | driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA || | 906 | driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA || |
| 864 | driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP; | 907 | driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP; |
| 865 | 908 | ||
| 866 | const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; | 909 | const bool is_steam_deck = (vendor_id == 0x1002 && device_id == 0x163F) || |
| 910 | (vendor_id == 0x1002 && device_id == 0x1435); | ||
| 867 | 911 | ||
| 868 | const bool is_debugging = this->HasDebuggingToolAttached(); | 912 | const bool is_debugging = this->HasDebuggingToolAttached(); |
| 869 | 913 | ||
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/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 2afa72140..ed5750155 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -30,7 +30,6 @@ | |||
| 30 | #include <QSize> | 30 | #include <QSize> |
| 31 | #include <QStringLiteral> | 31 | #include <QStringLiteral> |
| 32 | #include <QSurfaceFormat> | 32 | #include <QSurfaceFormat> |
| 33 | #include <QTimer> | ||
| 34 | #include <QWindow> | 33 | #include <QWindow> |
| 35 | #include <QtCore/qobjectdefs.h> | 34 | #include <QtCore/qobjectdefs.h> |
| 36 | 35 | ||
| @@ -66,6 +65,8 @@ class QObject; | |||
| 66 | class QPaintEngine; | 65 | class QPaintEngine; |
| 67 | class QSurface; | 66 | class QSurface; |
| 68 | 67 | ||
| 68 | constexpr int default_mouse_constrain_timeout = 10; | ||
| 69 | |||
| 69 | EmuThread::EmuThread(Core::System& system) : m_system{system} {} | 70 | EmuThread::EmuThread(Core::System& system) : m_system{system} {} |
| 70 | 71 | ||
| 71 | EmuThread::~EmuThread() = default; | 72 | EmuThread::~EmuThread() = default; |
| @@ -304,6 +305,9 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, | |||
| 304 | Qt::QueuedConnection); | 305 | Qt::QueuedConnection); |
| 305 | connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection); | 306 | connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection); |
| 306 | connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged); | 307 | connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged); |
| 308 | |||
| 309 | mouse_constrain_timer.setInterval(default_mouse_constrain_timeout); | ||
| 310 | connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse); | ||
| 307 | } | 311 | } |
| 308 | 312 | ||
| 309 | void GRenderWindow::ExecuteProgram(std::size_t program_index) { | 313 | void GRenderWindow::ExecuteProgram(std::size_t program_index) { |
| @@ -393,6 +397,22 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { | |||
| 393 | QWidget::closeEvent(event); | 397 | QWidget::closeEvent(event); |
| 394 | } | 398 | } |
| 395 | 399 | ||
| 400 | void GRenderWindow::leaveEvent(QEvent* event) { | ||
| 401 | if (Settings::values.mouse_panning) { | ||
| 402 | const QRect& rect = QWidget::geometry(); | ||
| 403 | QPoint position = QCursor::pos(); | ||
| 404 | |||
| 405 | qint32 x = qBound(rect.left(), position.x(), rect.right()); | ||
| 406 | qint32 y = qBound(rect.top(), position.y(), rect.bottom()); | ||
| 407 | // Only start the timer if the mouse has left the window bound. | ||
| 408 | // The leave event is also triggered when the window looses focus. | ||
| 409 | if (x != position.x() || y != position.y()) { | ||
| 410 | mouse_constrain_timer.start(); | ||
| 411 | } | ||
| 412 | event->accept(); | ||
| 413 | } | ||
| 414 | } | ||
| 415 | |||
| 396 | int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { | 416 | int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { |
| 397 | static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = { | 417 | static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = { |
| 398 | std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A}, | 418 | std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A}, |
| @@ -658,10 +678,19 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | |||
| 658 | input_subsystem->GetMouse()->TouchMove(touch_x, touch_y); | 678 | input_subsystem->GetMouse()->TouchMove(touch_x, touch_y); |
| 659 | input_subsystem->GetMouse()->Move(pos.x(), pos.y(), center_x, center_y); | 679 | input_subsystem->GetMouse()->Move(pos.x(), pos.y(), center_x, center_y); |
| 660 | 680 | ||
| 681 | // Center mouse for mouse panning | ||
| 661 | if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { | 682 | if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { |
| 662 | QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); | 683 | QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); |
| 663 | } | 684 | } |
| 664 | 685 | ||
| 686 | // Constrain mouse for mouse emulation with mouse panning | ||
| 687 | if (Settings::values.mouse_panning && Settings::values.mouse_enabled) { | ||
| 688 | const auto [clamped_mouse_x, clamped_mouse_y] = ClipToTouchScreen(x, y); | ||
| 689 | QCursor::setPos(mapToGlobal( | ||
| 690 | QPoint{static_cast<int>(clamped_mouse_x), static_cast<int>(clamped_mouse_y)})); | ||
| 691 | } | ||
| 692 | |||
| 693 | mouse_constrain_timer.stop(); | ||
| 665 | emit MouseActivity(); | 694 | emit MouseActivity(); |
| 666 | } | 695 | } |
| 667 | 696 | ||
| @@ -675,6 +704,31 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | |||
| 675 | input_subsystem->GetMouse()->ReleaseButton(button); | 704 | input_subsystem->GetMouse()->ReleaseButton(button); |
| 676 | } | 705 | } |
| 677 | 706 | ||
| 707 | void GRenderWindow::ConstrainMouse() { | ||
| 708 | if (emu_thread == nullptr || !Settings::values.mouse_panning) { | ||
| 709 | mouse_constrain_timer.stop(); | ||
| 710 | return; | ||
| 711 | } | ||
| 712 | if (!this->isActiveWindow()) { | ||
| 713 | mouse_constrain_timer.stop(); | ||
| 714 | return; | ||
| 715 | } | ||
| 716 | |||
| 717 | if (Settings::values.mouse_enabled) { | ||
| 718 | const auto pos = mapFromGlobal(QCursor::pos()); | ||
| 719 | const int new_pos_x = std::clamp(pos.x(), 0, width()); | ||
| 720 | const int new_pos_y = std::clamp(pos.y(), 0, height()); | ||
| 721 | |||
| 722 | QCursor::setPos(mapToGlobal(QPoint{new_pos_x, new_pos_y})); | ||
| 723 | return; | ||
| 724 | } | ||
| 725 | |||
| 726 | const int center_x = width() / 2; | ||
| 727 | const int center_y = height() / 2; | ||
| 728 | |||
| 729 | QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); | ||
| 730 | } | ||
| 731 | |||
| 678 | void GRenderWindow::wheelEvent(QWheelEvent* event) { | 732 | void GRenderWindow::wheelEvent(QWheelEvent* event) { |
| 679 | const int x = event->angleDelta().x(); | 733 | const int x = event->angleDelta().x(); |
| 680 | const int y = event->angleDelta().y(); | 734 | const int y = event->angleDelta().y(); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 87b23df12..60edd464c 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <QString> | 17 | #include <QString> |
| 18 | #include <QStringList> | 18 | #include <QStringList> |
| 19 | #include <QThread> | 19 | #include <QThread> |
| 20 | #include <QTimer> | ||
| 20 | #include <QWidget> | 21 | #include <QWidget> |
| 21 | #include <qglobal.h> | 22 | #include <qglobal.h> |
| 22 | #include <qnamespace.h> | 23 | #include <qnamespace.h> |
| @@ -38,7 +39,6 @@ class QMouseEvent; | |||
| 38 | class QObject; | 39 | class QObject; |
| 39 | class QResizeEvent; | 40 | class QResizeEvent; |
| 40 | class QShowEvent; | 41 | class QShowEvent; |
| 41 | class QTimer; | ||
| 42 | class QTouchEvent; | 42 | class QTouchEvent; |
| 43 | class QWheelEvent; | 43 | class QWheelEvent; |
| 44 | 44 | ||
| @@ -166,6 +166,7 @@ public: | |||
| 166 | std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; | 166 | std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; |
| 167 | 167 | ||
| 168 | void closeEvent(QCloseEvent* event) override; | 168 | void closeEvent(QCloseEvent* event) override; |
| 169 | void leaveEvent(QEvent* event) override; | ||
| 169 | 170 | ||
| 170 | void resizeEvent(QResizeEvent* event) override; | 171 | void resizeEvent(QResizeEvent* event) override; |
| 171 | 172 | ||
| @@ -229,6 +230,7 @@ private: | |||
| 229 | void TouchBeginEvent(const QTouchEvent* event); | 230 | void TouchBeginEvent(const QTouchEvent* event); |
| 230 | void TouchUpdateEvent(const QTouchEvent* event); | 231 | void TouchUpdateEvent(const QTouchEvent* event); |
| 231 | void TouchEndEvent(); | 232 | void TouchEndEvent(); |
| 233 | void ConstrainMouse(); | ||
| 232 | 234 | ||
| 233 | void RequestCameraCapture(); | 235 | void RequestCameraCapture(); |
| 234 | void OnCameraCapture(int requestId, const QImage& img); | 236 | void OnCameraCapture(int requestId, const QImage& img); |
| @@ -268,6 +270,8 @@ private: | |||
| 268 | std::unique_ptr<QTimer> camera_timer; | 270 | std::unique_ptr<QTimer> camera_timer; |
| 269 | #endif | 271 | #endif |
| 270 | 272 | ||
| 273 | QTimer mouse_constrain_timer; | ||
| 274 | |||
| 271 | Core::System& system; | 275 | Core::System& system; |
| 272 | 276 | ||
| 273 | protected: | 277 | protected: |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp deleted file mode 100644 index c0ae6468b..000000000 --- a/src/yuzu/configuration/config.cpp +++ /dev/null | |||
| @@ -1,1309 +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 | Qt::Key_Q, Qt::Key_E, | ||
| 39 | }; | ||
| 40 | |||
| 41 | const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = { | ||
| 42 | Qt::Key_7, | ||
| 43 | Qt::Key_8, | ||
| 44 | }; | ||
| 45 | |||
| 46 | const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ | ||
| 47 | { | ||
| 48 | Qt::Key_W, | ||
| 49 | Qt::Key_S, | ||
| 50 | Qt::Key_A, | ||
| 51 | Qt::Key_D, | ||
| 52 | }, | ||
| 53 | { | ||
| 54 | Qt::Key_I, | ||
| 55 | Qt::Key_K, | ||
| 56 | Qt::Key_J, | ||
| 57 | Qt::Key_L, | ||
| 58 | }, | ||
| 59 | }}; | ||
| 60 | |||
| 61 | const std::array<int, 2> Config::default_stick_mod = { | ||
| 62 | Qt::Key_Shift, | ||
| 63 | 0, | ||
| 64 | }; | ||
| 65 | |||
| 66 | const std::array<int, 2> Config::default_ringcon_analogs{{ | ||
| 67 | Qt::Key_A, | ||
| 68 | Qt::Key_D, | ||
| 69 | }}; | ||
| 70 | |||
| 71 | const std::map<Settings::AntiAliasing, QString> Config::anti_aliasing_texts_map = { | ||
| 72 | {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))}, | ||
| 73 | {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))}, | ||
| 74 | {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))}, | ||
| 75 | }; | ||
| 76 | |||
| 77 | const std::map<Settings::ScalingFilter, QString> Config::scaling_filter_texts_map = { | ||
| 78 | {Settings::ScalingFilter::NearestNeighbor, | ||
| 79 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))}, | ||
| 80 | {Settings::ScalingFilter::Bilinear, | ||
| 81 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))}, | ||
| 82 | {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, | ||
| 83 | {Settings::ScalingFilter::Gaussian, | ||
| 84 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, | ||
| 85 | {Settings::ScalingFilter::ScaleForce, | ||
| 86 | QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, | ||
| 87 | {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, | ||
| 88 | }; | ||
| 89 | |||
| 90 | const std::map<Settings::ConsoleMode, QString> Config::use_docked_mode_texts_map = { | ||
| 91 | {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, | ||
| 92 | {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, | ||
| 93 | }; | ||
| 94 | |||
| 95 | const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = { | ||
| 96 | {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))}, | ||
| 97 | {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))}, | ||
| 98 | {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))}, | ||
| 99 | }; | ||
| 100 | |||
| 101 | const std::map<Settings::RendererBackend, QString> Config::renderer_backend_texts_map = { | ||
| 102 | {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))}, | ||
| 103 | {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))}, | ||
| 104 | {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, | ||
| 105 | }; | ||
| 106 | |||
| 107 | const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = { | ||
| 108 | {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, | ||
| 109 | {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, | ||
| 110 | {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, | ||
| 111 | }; | ||
| 112 | |||
| 113 | // This shouldn't have anything except static initializers (no functions). So | ||
| 114 | // QKeySequence(...).toString() is NOT ALLOWED HERE. | ||
| 115 | // This must be in alphabetical order according to action name as it must have the same order as | ||
| 116 | // UISetting::values.shortcuts, which is alphabetically ordered. | ||
| 117 | // clang-format off | ||
| 118 | const std::array<UISettings::Shortcut, 23> Config::default_hotkeys{{ | ||
| 119 | {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}}, | ||
| 120 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut, true}}, | ||
| 121 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut, true}}, | ||
| 122 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut, false}}, | ||
| 123 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut, false}}, | ||
| 124 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut, false}}, | ||
| 125 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut, false}}, | ||
| 126 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut, false}}, | ||
| 127 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut, false}}, | ||
| 128 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut, false}}, | ||
| 129 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}}, | ||
| 130 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}}, | ||
| 131 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, | ||
| 132 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}}, | ||
| 133 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}}, | ||
| 134 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | ||
| 135 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | ||
| 136 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | ||
| 137 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut, false}}, | ||
| 138 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut, false}}, | ||
| 139 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | ||
| 140 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | ||
| 141 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut, false}}, | ||
| 142 | }}; | ||
| 143 | // clang-format on | ||
| 144 | |||
| 145 | void Config::Initialize(const std::string& config_name) { | ||
| 146 | const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); | ||
| 147 | const auto config_file = fmt::format("{}.ini", config_name); | ||
| 148 | |||
| 149 | switch (type) { | ||
| 150 | case ConfigType::GlobalConfig: | ||
| 151 | qt_config_loc = FS::PathToUTF8String(fs_config_loc / config_file); | ||
| 152 | void(FS::CreateParentDir(qt_config_loc)); | ||
| 153 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), | ||
| 154 | QSettings::IniFormat); | ||
| 155 | Reload(); | ||
| 156 | break; | ||
| 157 | case ConfigType::PerGameConfig: | ||
| 158 | qt_config_loc = | ||
| 159 | FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); | ||
| 160 | void(FS::CreateParentDir(qt_config_loc)); | ||
| 161 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), | ||
| 162 | QSettings::IniFormat); | ||
| 163 | Reload(); | ||
| 164 | break; | ||
| 165 | case ConfigType::InputProfile: | ||
| 166 | qt_config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); | ||
| 167 | void(FS::CreateParentDir(qt_config_loc)); | ||
| 168 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), | ||
| 169 | QSettings::IniFormat); | ||
| 170 | break; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | bool Config::IsCustomConfig() { | ||
| 175 | return type == ConfigType::PerGameConfig; | ||
| 176 | } | ||
| 177 | |||
| 178 | void Config::ReadPlayerValue(std::size_t player_index) { | ||
| 179 | const QString player_prefix = [this, player_index] { | ||
| 180 | if (type == ConfigType::InputProfile) { | ||
| 181 | return QString{}; | ||
| 182 | } else { | ||
| 183 | return QStringLiteral("player_%1_").arg(player_index); | ||
| 184 | } | ||
| 185 | }(); | ||
| 186 | |||
| 187 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 188 | if (IsCustomConfig()) { | ||
| 189 | const auto profile_name = | ||
| 190 | qt_config->value(QStringLiteral("%1profile_name").arg(player_prefix), QString{}) | ||
| 191 | .toString() | ||
| 192 | .toStdString(); | ||
| 193 | if (profile_name.empty()) { | ||
| 194 | // Use the global input config | ||
| 195 | player = Settings::values.players.GetValue(true)[player_index]; | ||
| 196 | return; | ||
| 197 | } | ||
| 198 | player.profile_name = profile_name; | ||
| 199 | } | ||
| 200 | |||
| 201 | if (player_prefix.isEmpty() && Settings::IsConfiguringGlobal()) { | ||
| 202 | const auto controller = static_cast<Settings::ControllerType>( | ||
| 203 | qt_config | ||
| 204 | ->value(QStringLiteral("%1type").arg(player_prefix), | ||
| 205 | static_cast<u8>(Settings::ControllerType::ProController)) | ||
| 206 | .toUInt()); | ||
| 207 | |||
| 208 | if (controller == Settings::ControllerType::LeftJoycon || | ||
| 209 | controller == Settings::ControllerType::RightJoycon) { | ||
| 210 | player.controller_type = controller; | ||
| 211 | } | ||
| 212 | } else { | ||
| 213 | player.connected = | ||
| 214 | ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0) | ||
| 215 | .toBool(); | ||
| 216 | |||
| 217 | player.controller_type = static_cast<Settings::ControllerType>( | ||
| 218 | qt_config | ||
| 219 | ->value(QStringLiteral("%1type").arg(player_prefix), | ||
| 220 | static_cast<u8>(Settings::ControllerType::ProController)) | ||
| 221 | .toUInt()); | ||
| 222 | |||
| 223 | player.vibration_enabled = | ||
| 224 | qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true) | ||
| 225 | .toBool(); | ||
| 226 | |||
| 227 | player.vibration_strength = | ||
| 228 | qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100) | ||
| 229 | .toInt(); | ||
| 230 | |||
| 231 | player.body_color_left = qt_config | ||
| 232 | ->value(QStringLiteral("%1body_color_left").arg(player_prefix), | ||
| 233 | Settings::JOYCON_BODY_NEON_BLUE) | ||
| 234 | .toUInt(); | ||
| 235 | player.body_color_right = | ||
| 236 | qt_config | ||
| 237 | ->value(QStringLiteral("%1body_color_right").arg(player_prefix), | ||
| 238 | Settings::JOYCON_BODY_NEON_RED) | ||
| 239 | .toUInt(); | ||
| 240 | player.button_color_left = | ||
| 241 | qt_config | ||
| 242 | ->value(QStringLiteral("%1button_color_left").arg(player_prefix), | ||
| 243 | Settings::JOYCON_BUTTONS_NEON_BLUE) | ||
| 244 | .toUInt(); | ||
| 245 | player.button_color_right = | ||
| 246 | qt_config | ||
| 247 | ->value(QStringLiteral("%1button_color_right").arg(player_prefix), | ||
| 248 | Settings::JOYCON_BUTTONS_NEON_RED) | ||
| 249 | .toUInt(); | ||
| 250 | } | ||
| 251 | |||
| 252 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 253 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 254 | auto& player_buttons = player.buttons[i]; | ||
| 255 | |||
| 256 | player_buttons = qt_config | ||
| 257 | ->value(QStringLiteral("%1").arg(player_prefix) + | ||
| 258 | QString::fromUtf8(Settings::NativeButton::mapping[i]), | ||
| 259 | QString::fromStdString(default_param)) | ||
| 260 | .toString() | ||
| 261 | .toStdString(); | ||
| 262 | if (player_buttons.empty()) { | ||
| 263 | player_buttons = default_param; | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 268 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 269 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 270 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 271 | auto& player_analogs = player.analogs[i]; | ||
| 272 | |||
| 273 | player_analogs = qt_config | ||
| 274 | ->value(QStringLiteral("%1").arg(player_prefix) + | ||
| 275 | QString::fromUtf8(Settings::NativeAnalog::mapping[i]), | ||
| 276 | QString::fromStdString(default_param)) | ||
| 277 | .toString() | ||
| 278 | .toStdString(); | ||
| 279 | if (player_analogs.empty()) { | ||
| 280 | player_analogs = default_param; | ||
| 281 | } | ||
| 282 | } | ||
| 283 | |||
| 284 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | ||
| 285 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); | ||
| 286 | auto& player_motions = player.motions[i]; | ||
| 287 | |||
| 288 | player_motions = qt_config | ||
| 289 | ->value(QStringLiteral("%1").arg(player_prefix) + | ||
| 290 | QString::fromUtf8(Settings::NativeMotion::mapping[i]), | ||
| 291 | QString::fromStdString(default_param)) | ||
| 292 | .toString() | ||
| 293 | .toStdString(); | ||
| 294 | if (player_motions.empty()) { | ||
| 295 | player_motions = default_param; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | void Config::ReadDebugValues() { | ||
| 301 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 302 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 303 | auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; | ||
| 304 | |||
| 305 | debug_pad_buttons = qt_config | ||
| 306 | ->value(QStringLiteral("debug_pad_") + | ||
| 307 | QString::fromUtf8(Settings::NativeButton::mapping[i]), | ||
| 308 | QString::fromStdString(default_param)) | ||
| 309 | .toString() | ||
| 310 | .toStdString(); | ||
| 311 | if (debug_pad_buttons.empty()) { | ||
| 312 | debug_pad_buttons = default_param; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 317 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 318 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 319 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 320 | auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; | ||
| 321 | |||
| 322 | debug_pad_analogs = qt_config | ||
| 323 | ->value(QStringLiteral("debug_pad_") + | ||
| 324 | QString::fromUtf8(Settings::NativeAnalog::mapping[i]), | ||
| 325 | QString::fromStdString(default_param)) | ||
| 326 | .toString() | ||
| 327 | .toStdString(); | ||
| 328 | if (debug_pad_analogs.empty()) { | ||
| 329 | debug_pad_analogs = default_param; | ||
| 330 | } | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | void Config::ReadTouchscreenValues() { | ||
| 335 | Settings::values.touchscreen.enabled = | ||
| 336 | ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool(); | ||
| 337 | |||
| 338 | Settings::values.touchscreen.rotation_angle = | ||
| 339 | ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt(); | ||
| 340 | Settings::values.touchscreen.diameter_x = | ||
| 341 | ReadSetting(QStringLiteral("touchscreen_diameter_x"), 15).toUInt(); | ||
| 342 | Settings::values.touchscreen.diameter_y = | ||
| 343 | ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); | ||
| 344 | } | ||
| 345 | |||
| 346 | void Config::ReadHidbusValues() { | ||
| 347 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 348 | 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); | ||
| 349 | auto& ringcon_analogs = Settings::values.ringcon_analogs; | ||
| 350 | |||
| 351 | ringcon_analogs = | ||
| 352 | qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param)) | ||
| 353 | .toString() | ||
| 354 | .toStdString(); | ||
| 355 | if (ringcon_analogs.empty()) { | ||
| 356 | ringcon_analogs = default_param; | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | void Config::ReadAudioValues() { | ||
| 361 | qt_config->beginGroup(QStringLiteral("Audio")); | ||
| 362 | |||
| 363 | ReadCategory(Settings::Category::Audio); | ||
| 364 | ReadCategory(Settings::Category::UiAudio); | ||
| 365 | |||
| 366 | qt_config->endGroup(); | ||
| 367 | } | ||
| 368 | |||
| 369 | void Config::ReadControlValues() { | ||
| 370 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 371 | |||
| 372 | ReadCategory(Settings::Category::Controls); | ||
| 373 | |||
| 374 | Settings::values.players.SetGlobal(!IsCustomConfig()); | ||
| 375 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 376 | ReadPlayerValue(p); | ||
| 377 | } | ||
| 378 | |||
| 379 | // Disable docked mode if handheld is selected | ||
| 380 | const auto controller_type = Settings::values.players.GetValue()[0].controller_type; | ||
| 381 | if (controller_type == Settings::ControllerType::Handheld) { | ||
| 382 | Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); | ||
| 383 | Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld); | ||
| 384 | } | ||
| 385 | |||
| 386 | if (IsCustomConfig()) { | ||
| 387 | qt_config->endGroup(); | ||
| 388 | return; | ||
| 389 | } | ||
| 390 | ReadDebugValues(); | ||
| 391 | ReadTouchscreenValues(); | ||
| 392 | ReadMotionTouchValues(); | ||
| 393 | ReadHidbusValues(); | ||
| 394 | |||
| 395 | qt_config->endGroup(); | ||
| 396 | } | ||
| 397 | |||
| 398 | void Config::ReadMotionTouchValues() { | ||
| 399 | int num_touch_from_button_maps = | ||
| 400 | qt_config->beginReadArray(QStringLiteral("touch_from_button_maps")); | ||
| 401 | |||
| 402 | if (num_touch_from_button_maps > 0) { | ||
| 403 | const auto append_touch_from_button_map = [this] { | ||
| 404 | Settings::TouchFromButtonMap map; | ||
| 405 | map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default")) | ||
| 406 | .toString() | ||
| 407 | .toStdString(); | ||
| 408 | const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries")); | ||
| 409 | map.buttons.reserve(num_touch_maps); | ||
| 410 | for (int i = 0; i < num_touch_maps; i++) { | ||
| 411 | qt_config->setArrayIndex(i); | ||
| 412 | std::string touch_mapping = | ||
| 413 | ReadSetting(QStringLiteral("bind")).toString().toStdString(); | ||
| 414 | map.buttons.emplace_back(std::move(touch_mapping)); | ||
| 415 | } | ||
| 416 | qt_config->endArray(); // entries | ||
| 417 | Settings::values.touch_from_button_maps.emplace_back(std::move(map)); | ||
| 418 | }; | ||
| 419 | |||
| 420 | for (int i = 0; i < num_touch_from_button_maps; ++i) { | ||
| 421 | qt_config->setArrayIndex(i); | ||
| 422 | append_touch_from_button_map(); | ||
| 423 | } | ||
| 424 | } else { | ||
| 425 | Settings::values.touch_from_button_maps.emplace_back( | ||
| 426 | Settings::TouchFromButtonMap{"default", {}}); | ||
| 427 | num_touch_from_button_maps = 1; | ||
| 428 | } | ||
| 429 | qt_config->endArray(); | ||
| 430 | |||
| 431 | Settings::values.touch_from_button_map_index = std::clamp( | ||
| 432 | Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); | ||
| 433 | } | ||
| 434 | |||
| 435 | void Config::ReadCoreValues() { | ||
| 436 | qt_config->beginGroup(QStringLiteral("Core")); | ||
| 437 | |||
| 438 | ReadCategory(Settings::Category::Core); | ||
| 439 | |||
| 440 | qt_config->endGroup(); | ||
| 441 | } | ||
| 442 | |||
| 443 | void Config::ReadDataStorageValues() { | ||
| 444 | qt_config->beginGroup(QStringLiteral("Data Storage")); | ||
| 445 | |||
| 446 | FS::SetYuzuPath( | ||
| 447 | FS::YuzuPath::NANDDir, | ||
| 448 | qt_config | ||
| 449 | ->value(QStringLiteral("nand_directory"), | ||
| 450 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))) | ||
| 451 | .toString() | ||
| 452 | .toStdString()); | ||
| 453 | FS::SetYuzuPath( | ||
| 454 | FS::YuzuPath::SDMCDir, | ||
| 455 | qt_config | ||
| 456 | ->value(QStringLiteral("sdmc_directory"), | ||
| 457 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))) | ||
| 458 | .toString() | ||
| 459 | .toStdString()); | ||
| 460 | FS::SetYuzuPath( | ||
| 461 | FS::YuzuPath::LoadDir, | ||
| 462 | qt_config | ||
| 463 | ->value(QStringLiteral("load_directory"), | ||
| 464 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))) | ||
| 465 | .toString() | ||
| 466 | .toStdString()); | ||
| 467 | FS::SetYuzuPath( | ||
| 468 | FS::YuzuPath::DumpDir, | ||
| 469 | qt_config | ||
| 470 | ->value(QStringLiteral("dump_directory"), | ||
| 471 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))) | ||
| 472 | .toString() | ||
| 473 | .toStdString()); | ||
| 474 | FS::SetYuzuPath(FS::YuzuPath::TASDir, | ||
| 475 | qt_config | ||
| 476 | ->value(QStringLiteral("tas_directory"), | ||
| 477 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))) | ||
| 478 | .toString() | ||
| 479 | .toStdString()); | ||
| 480 | |||
| 481 | ReadCategory(Settings::Category::DataStorage); | ||
| 482 | |||
| 483 | qt_config->endGroup(); | ||
| 484 | } | ||
| 485 | |||
| 486 | void Config::ReadDebuggingValues() { | ||
| 487 | qt_config->beginGroup(QStringLiteral("Debugging")); | ||
| 488 | |||
| 489 | // Intentionally not using the QT default setting as this is intended to be changed in the ini | ||
| 490 | Settings::values.record_frame_times = | ||
| 491 | qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); | ||
| 492 | |||
| 493 | ReadCategory(Settings::Category::Debugging); | ||
| 494 | ReadCategory(Settings::Category::DebuggingGraphics); | ||
| 495 | |||
| 496 | qt_config->endGroup(); | ||
| 497 | } | ||
| 498 | |||
| 499 | void Config::ReadServiceValues() { | ||
| 500 | qt_config->beginGroup(QStringLiteral("Services")); | ||
| 501 | |||
| 502 | ReadCategory(Settings::Category::Services); | ||
| 503 | |||
| 504 | qt_config->endGroup(); | ||
| 505 | } | ||
| 506 | |||
| 507 | void Config::ReadDisabledAddOnValues() { | ||
| 508 | const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns")); | ||
| 509 | |||
| 510 | for (int i = 0; i < size; ++i) { | ||
| 511 | qt_config->setArrayIndex(i); | ||
| 512 | const auto title_id = ReadSetting(QStringLiteral("title_id"), 0).toULongLong(); | ||
| 513 | std::vector<std::string> out; | ||
| 514 | const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled")); | ||
| 515 | for (int j = 0; j < d_size; ++j) { | ||
| 516 | qt_config->setArrayIndex(j); | ||
| 517 | out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString()); | ||
| 518 | } | ||
| 519 | qt_config->endArray(); | ||
| 520 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | ||
| 521 | } | ||
| 522 | |||
| 523 | qt_config->endArray(); | ||
| 524 | } | ||
| 525 | |||
| 526 | void Config::ReadMiscellaneousValues() { | ||
| 527 | qt_config->beginGroup(QStringLiteral("Miscellaneous")); | ||
| 528 | |||
| 529 | ReadCategory(Settings::Category::Miscellaneous); | ||
| 530 | |||
| 531 | qt_config->endGroup(); | ||
| 532 | } | ||
| 533 | |||
| 534 | void Config::ReadPathValues() { | ||
| 535 | qt_config->beginGroup(QStringLiteral("Paths")); | ||
| 536 | |||
| 537 | UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); | ||
| 538 | UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); | ||
| 539 | UISettings::values.game_dir_deprecated = | ||
| 540 | ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); | ||
| 541 | UISettings::values.game_dir_deprecated_deepscan = | ||
| 542 | ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool(); | ||
| 543 | const int gamedirs_size = qt_config->beginReadArray(QStringLiteral("gamedirs")); | ||
| 544 | for (int i = 0; i < gamedirs_size; ++i) { | ||
| 545 | qt_config->setArrayIndex(i); | ||
| 546 | UISettings::GameDir game_dir; | ||
| 547 | game_dir.path = ReadSetting(QStringLiteral("path")).toString(); | ||
| 548 | game_dir.deep_scan = ReadSetting(QStringLiteral("deep_scan"), false).toBool(); | ||
| 549 | game_dir.expanded = ReadSetting(QStringLiteral("expanded"), true).toBool(); | ||
| 550 | UISettings::values.game_dirs.append(game_dir); | ||
| 551 | } | ||
| 552 | qt_config->endArray(); | ||
| 553 | // create NAND and SD card directories if empty, these are not removable through the UI, | ||
| 554 | // also carries over old game list settings if present | ||
| 555 | if (UISettings::values.game_dirs.isEmpty()) { | ||
| 556 | UISettings::GameDir game_dir; | ||
| 557 | game_dir.path = QStringLiteral("SDMC"); | ||
| 558 | game_dir.expanded = true; | ||
| 559 | UISettings::values.game_dirs.append(game_dir); | ||
| 560 | game_dir.path = QStringLiteral("UserNAND"); | ||
| 561 | UISettings::values.game_dirs.append(game_dir); | ||
| 562 | game_dir.path = QStringLiteral("SysNAND"); | ||
| 563 | UISettings::values.game_dirs.append(game_dir); | ||
| 564 | if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) { | ||
| 565 | game_dir.path = UISettings::values.game_dir_deprecated; | ||
| 566 | game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan; | ||
| 567 | UISettings::values.game_dirs.append(game_dir); | ||
| 568 | } | ||
| 569 | } | ||
| 570 | UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); | ||
| 571 | UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString(); | ||
| 572 | |||
| 573 | qt_config->endGroup(); | ||
| 574 | } | ||
| 575 | |||
| 576 | void Config::ReadCpuValues() { | ||
| 577 | qt_config->beginGroup(QStringLiteral("Cpu")); | ||
| 578 | |||
| 579 | ReadCategory(Settings::Category::Cpu); | ||
| 580 | ReadCategory(Settings::Category::CpuDebug); | ||
| 581 | ReadCategory(Settings::Category::CpuUnsafe); | ||
| 582 | |||
| 583 | qt_config->endGroup(); | ||
| 584 | } | ||
| 585 | |||
| 586 | void Config::ReadRendererValues() { | ||
| 587 | qt_config->beginGroup(QStringLiteral("Renderer")); | ||
| 588 | |||
| 589 | ReadCategory(Settings::Category::Renderer); | ||
| 590 | ReadCategory(Settings::Category::RendererAdvanced); | ||
| 591 | ReadCategory(Settings::Category::RendererDebug); | ||
| 592 | |||
| 593 | qt_config->endGroup(); | ||
| 594 | } | ||
| 595 | |||
| 596 | void Config::ReadScreenshotValues() { | ||
| 597 | qt_config->beginGroup(QStringLiteral("Screenshots")); | ||
| 598 | |||
| 599 | ReadCategory(Settings::Category::Screenshots); | ||
| 600 | FS::SetYuzuPath( | ||
| 601 | FS::YuzuPath::ScreenshotsDir, | ||
| 602 | qt_config | ||
| 603 | ->value(QStringLiteral("screenshot_path"), | ||
| 604 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))) | ||
| 605 | .toString() | ||
| 606 | .toStdString()); | ||
| 607 | |||
| 608 | qt_config->endGroup(); | ||
| 609 | } | ||
| 610 | |||
| 611 | void Config::ReadShortcutValues() { | ||
| 612 | qt_config->beginGroup(QStringLiteral("Shortcuts")); | ||
| 613 | |||
| 614 | for (const auto& [name, group, shortcut] : default_hotkeys) { | ||
| 615 | qt_config->beginGroup(group); | ||
| 616 | qt_config->beginGroup(name); | ||
| 617 | // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1 | ||
| 618 | // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open | ||
| 619 | // a file dialog in windowed mode | ||
| 620 | UISettings::values.shortcuts.push_back( | ||
| 621 | {name, | ||
| 622 | group, | ||
| 623 | {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(), | ||
| 624 | ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq) | ||
| 625 | .toString(), | ||
| 626 | shortcut.context, ReadSetting(QStringLiteral("Repeat"), shortcut.repeat).toBool()}}); | ||
| 627 | qt_config->endGroup(); | ||
| 628 | qt_config->endGroup(); | ||
| 629 | } | ||
| 630 | |||
| 631 | qt_config->endGroup(); | ||
| 632 | } | ||
| 633 | |||
| 634 | void Config::ReadSystemValues() { | ||
| 635 | qt_config->beginGroup(QStringLiteral("System")); | ||
| 636 | |||
| 637 | ReadCategory(Settings::Category::System); | ||
| 638 | ReadCategory(Settings::Category::SystemAudio); | ||
| 639 | |||
| 640 | qt_config->endGroup(); | ||
| 641 | } | ||
| 642 | |||
| 643 | void Config::ReadUIValues() { | ||
| 644 | qt_config->beginGroup(QStringLiteral("UI")); | ||
| 645 | |||
| 646 | UISettings::values.theme = | ||
| 647 | ReadSetting( | ||
| 648 | QStringLiteral("theme"), | ||
| 649 | QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second)) | ||
| 650 | .toString(); | ||
| 651 | |||
| 652 | ReadUIGamelistValues(); | ||
| 653 | ReadUILayoutValues(); | ||
| 654 | ReadPathValues(); | ||
| 655 | ReadScreenshotValues(); | ||
| 656 | ReadShortcutValues(); | ||
| 657 | ReadMultiplayerValues(); | ||
| 658 | |||
| 659 | ReadCategory(Settings::Category::Ui); | ||
| 660 | ReadCategory(Settings::Category::UiGeneral); | ||
| 661 | |||
| 662 | qt_config->endGroup(); | ||
| 663 | } | ||
| 664 | |||
| 665 | void Config::ReadUIGamelistValues() { | ||
| 666 | qt_config->beginGroup(QStringLiteral("UIGameList")); | ||
| 667 | |||
| 668 | ReadCategory(Settings::Category::UiGameList); | ||
| 669 | |||
| 670 | const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); | ||
| 671 | for (int i = 0; i < favorites_size; i++) { | ||
| 672 | qt_config->setArrayIndex(i); | ||
| 673 | UISettings::values.favorited_ids.append( | ||
| 674 | ReadSetting(QStringLiteral("program_id")).toULongLong()); | ||
| 675 | } | ||
| 676 | qt_config->endArray(); | ||
| 677 | |||
| 678 | qt_config->endGroup(); | ||
| 679 | } | ||
| 680 | |||
| 681 | void Config::ReadUILayoutValues() { | ||
| 682 | qt_config->beginGroup(QStringLiteral("UILayout")); | ||
| 683 | |||
| 684 | UISettings::values.geometry = ReadSetting(QStringLiteral("geometry")).toByteArray(); | ||
| 685 | UISettings::values.state = ReadSetting(QStringLiteral("state")).toByteArray(); | ||
| 686 | UISettings::values.renderwindow_geometry = | ||
| 687 | ReadSetting(QStringLiteral("geometryRenderWindow")).toByteArray(); | ||
| 688 | UISettings::values.gamelist_header_state = | ||
| 689 | ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray(); | ||
| 690 | UISettings::values.microprofile_geometry = | ||
| 691 | ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray(); | ||
| 692 | |||
| 693 | ReadCategory(Settings::Category::UiLayout); | ||
| 694 | |||
| 695 | qt_config->endGroup(); | ||
| 696 | } | ||
| 697 | |||
| 698 | void Config::ReadWebServiceValues() { | ||
| 699 | qt_config->beginGroup(QStringLiteral("WebService")); | ||
| 700 | |||
| 701 | ReadCategory(Settings::Category::WebService); | ||
| 702 | |||
| 703 | qt_config->endGroup(); | ||
| 704 | } | ||
| 705 | |||
| 706 | void Config::ReadMultiplayerValues() { | ||
| 707 | qt_config->beginGroup(QStringLiteral("Multiplayer")); | ||
| 708 | |||
| 709 | ReadCategory(Settings::Category::Multiplayer); | ||
| 710 | |||
| 711 | // Read ban list back | ||
| 712 | int size = qt_config->beginReadArray(QStringLiteral("username_ban_list")); | ||
| 713 | UISettings::values.multiplayer_ban_list.first.resize(size); | ||
| 714 | for (int i = 0; i < size; ++i) { | ||
| 715 | qt_config->setArrayIndex(i); | ||
| 716 | UISettings::values.multiplayer_ban_list.first[i] = | ||
| 717 | ReadSetting(QStringLiteral("username")).toString().toStdString(); | ||
| 718 | } | ||
| 719 | qt_config->endArray(); | ||
| 720 | size = qt_config->beginReadArray(QStringLiteral("ip_ban_list")); | ||
| 721 | UISettings::values.multiplayer_ban_list.second.resize(size); | ||
| 722 | for (int i = 0; i < size; ++i) { | ||
| 723 | qt_config->setArrayIndex(i); | ||
| 724 | UISettings::values.multiplayer_ban_list.second[i] = | ||
| 725 | ReadSetting(QStringLiteral("ip")).toString().toStdString(); | ||
| 726 | } | ||
| 727 | qt_config->endArray(); | ||
| 728 | |||
| 729 | qt_config->endGroup(); | ||
| 730 | } | ||
| 731 | |||
| 732 | void Config::ReadNetworkValues() { | ||
| 733 | qt_config->beginGroup(QString::fromStdString("Services")); | ||
| 734 | |||
| 735 | ReadCategory(Settings::Category::Network); | ||
| 736 | |||
| 737 | qt_config->endGroup(); | ||
| 738 | } | ||
| 739 | |||
| 740 | void Config::ReadValues() { | ||
| 741 | if (global) { | ||
| 742 | ReadDataStorageValues(); | ||
| 743 | ReadDebuggingValues(); | ||
| 744 | ReadDisabledAddOnValues(); | ||
| 745 | ReadNetworkValues(); | ||
| 746 | ReadServiceValues(); | ||
| 747 | ReadUIValues(); | ||
| 748 | ReadWebServiceValues(); | ||
| 749 | ReadMiscellaneousValues(); | ||
| 750 | } | ||
| 751 | ReadControlValues(); | ||
| 752 | ReadCoreValues(); | ||
| 753 | ReadCpuValues(); | ||
| 754 | ReadRendererValues(); | ||
| 755 | ReadAudioValues(); | ||
| 756 | ReadSystemValues(); | ||
| 757 | } | ||
| 758 | |||
| 759 | void Config::SavePlayerValue(std::size_t player_index) { | ||
| 760 | const QString player_prefix = [this, player_index] { | ||
| 761 | if (type == ConfigType::InputProfile) { | ||
| 762 | return QString{}; | ||
| 763 | } else { | ||
| 764 | return QStringLiteral("player_%1_").arg(player_index); | ||
| 765 | } | ||
| 766 | }(); | ||
| 767 | |||
| 768 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 769 | if (IsCustomConfig()) { | ||
| 770 | if (player.profile_name.empty()) { | ||
| 771 | // No custom profile selected | ||
| 772 | return; | ||
| 773 | } | ||
| 774 | WriteSetting(QStringLiteral("%1profile_name").arg(player_prefix), | ||
| 775 | QString::fromStdString(player.profile_name), QString{}); | ||
| 776 | } | ||
| 777 | |||
| 778 | WriteSetting(QStringLiteral("%1type").arg(player_prefix), | ||
| 779 | static_cast<u8>(player.controller_type), | ||
| 780 | static_cast<u8>(Settings::ControllerType::ProController)); | ||
| 781 | |||
| 782 | if (!player_prefix.isEmpty() || !Settings::IsConfiguringGlobal()) { | ||
| 783 | WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, | ||
| 784 | player_index == 0); | ||
| 785 | WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix), | ||
| 786 | player.vibration_enabled, true); | ||
| 787 | WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix), | ||
| 788 | player.vibration_strength, 100); | ||
| 789 | WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left, | ||
| 790 | Settings::JOYCON_BODY_NEON_BLUE); | ||
| 791 | WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix), | ||
| 792 | player.body_color_right, Settings::JOYCON_BODY_NEON_RED); | ||
| 793 | WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix), | ||
| 794 | player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE); | ||
| 795 | WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix), | ||
| 796 | player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED); | ||
| 797 | } | ||
| 798 | |||
| 799 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 800 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 801 | WriteSetting(QStringLiteral("%1").arg(player_prefix) + | ||
| 802 | QString::fromStdString(Settings::NativeButton::mapping[i]), | ||
| 803 | QString::fromStdString(player.buttons[i]), | ||
| 804 | QString::fromStdString(default_param)); | ||
| 805 | } | ||
| 806 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 807 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 808 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 809 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 810 | WriteSetting(QStringLiteral("%1").arg(player_prefix) + | ||
| 811 | QString::fromStdString(Settings::NativeAnalog::mapping[i]), | ||
| 812 | QString::fromStdString(player.analogs[i]), | ||
| 813 | QString::fromStdString(default_param)); | ||
| 814 | } | ||
| 815 | for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { | ||
| 816 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); | ||
| 817 | WriteSetting(QStringLiteral("%1").arg(player_prefix) + | ||
| 818 | QString::fromStdString(Settings::NativeMotion::mapping[i]), | ||
| 819 | QString::fromStdString(player.motions[i]), | ||
| 820 | QString::fromStdString(default_param)); | ||
| 821 | } | ||
| 822 | } | ||
| 823 | |||
| 824 | void Config::SaveDebugValues() { | ||
| 825 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 826 | const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 827 | WriteSetting(QStringLiteral("debug_pad_") + | ||
| 828 | QString::fromStdString(Settings::NativeButton::mapping[i]), | ||
| 829 | QString::fromStdString(Settings::values.debug_pad_buttons[i]), | ||
| 830 | QString::fromStdString(default_param)); | ||
| 831 | } | ||
| 832 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 833 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 834 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 835 | default_analogs[i][3], default_stick_mod[i], 0.5f); | ||
| 836 | WriteSetting(QStringLiteral("debug_pad_") + | ||
| 837 | QString::fromStdString(Settings::NativeAnalog::mapping[i]), | ||
| 838 | QString::fromStdString(Settings::values.debug_pad_analogs[i]), | ||
| 839 | QString::fromStdString(default_param)); | ||
| 840 | } | ||
| 841 | } | ||
| 842 | |||
| 843 | void Config::SaveTouchscreenValues() { | ||
| 844 | const auto& touchscreen = Settings::values.touchscreen; | ||
| 845 | |||
| 846 | WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true); | ||
| 847 | |||
| 848 | WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0); | ||
| 849 | WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15); | ||
| 850 | WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); | ||
| 851 | } | ||
| 852 | |||
| 853 | void Config::SaveMotionTouchValues() { | ||
| 854 | qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); | ||
| 855 | for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { | ||
| 856 | qt_config->setArrayIndex(static_cast<int>(p)); | ||
| 857 | WriteSetting(QStringLiteral("name"), | ||
| 858 | QString::fromStdString(Settings::values.touch_from_button_maps[p].name), | ||
| 859 | QStringLiteral("default")); | ||
| 860 | qt_config->beginWriteArray(QStringLiteral("entries")); | ||
| 861 | for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); | ||
| 862 | ++q) { | ||
| 863 | qt_config->setArrayIndex(static_cast<int>(q)); | ||
| 864 | WriteSetting( | ||
| 865 | QStringLiteral("bind"), | ||
| 866 | QString::fromStdString(Settings::values.touch_from_button_maps[p].buttons[q])); | ||
| 867 | } | ||
| 868 | qt_config->endArray(); | ||
| 869 | } | ||
| 870 | qt_config->endArray(); | ||
| 871 | } | ||
| 872 | |||
| 873 | void Config::SaveHidbusValues() { | ||
| 874 | const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 875 | 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); | ||
| 876 | WriteSetting(QStringLiteral("ring_controller"), | ||
| 877 | QString::fromStdString(Settings::values.ringcon_analogs), | ||
| 878 | QString::fromStdString(default_param)); | ||
| 879 | } | ||
| 880 | |||
| 881 | void Config::SaveValues() { | ||
| 882 | if (global) { | ||
| 883 | SaveDataStorageValues(); | ||
| 884 | SaveDebuggingValues(); | ||
| 885 | SaveDisabledAddOnValues(); | ||
| 886 | SaveNetworkValues(); | ||
| 887 | SaveUIValues(); | ||
| 888 | SaveWebServiceValues(); | ||
| 889 | SaveMiscellaneousValues(); | ||
| 890 | } | ||
| 891 | SaveControlValues(); | ||
| 892 | SaveCoreValues(); | ||
| 893 | SaveCpuValues(); | ||
| 894 | SaveRendererValues(); | ||
| 895 | SaveAudioValues(); | ||
| 896 | SaveSystemValues(); | ||
| 897 | |||
| 898 | qt_config->sync(); | ||
| 899 | } | ||
| 900 | |||
| 901 | void Config::SaveAudioValues() { | ||
| 902 | qt_config->beginGroup(QStringLiteral("Audio")); | ||
| 903 | |||
| 904 | WriteCategory(Settings::Category::Audio); | ||
| 905 | WriteCategory(Settings::Category::UiAudio); | ||
| 906 | |||
| 907 | qt_config->endGroup(); | ||
| 908 | } | ||
| 909 | |||
| 910 | void Config::SaveControlValues() { | ||
| 911 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 912 | |||
| 913 | WriteCategory(Settings::Category::Controls); | ||
| 914 | |||
| 915 | Settings::values.players.SetGlobal(!IsCustomConfig()); | ||
| 916 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 917 | SavePlayerValue(p); | ||
| 918 | } | ||
| 919 | if (IsCustomConfig()) { | ||
| 920 | qt_config->endGroup(); | ||
| 921 | return; | ||
| 922 | } | ||
| 923 | SaveDebugValues(); | ||
| 924 | SaveTouchscreenValues(); | ||
| 925 | SaveMotionTouchValues(); | ||
| 926 | SaveHidbusValues(); | ||
| 927 | |||
| 928 | qt_config->endGroup(); | ||
| 929 | } | ||
| 930 | |||
| 931 | void Config::SaveCoreValues() { | ||
| 932 | qt_config->beginGroup(QStringLiteral("Core")); | ||
| 933 | |||
| 934 | WriteCategory(Settings::Category::Core); | ||
| 935 | |||
| 936 | qt_config->endGroup(); | ||
| 937 | } | ||
| 938 | |||
| 939 | void Config::SaveDataStorageValues() { | ||
| 940 | qt_config->beginGroup(QStringLiteral("Data Storage")); | ||
| 941 | |||
| 942 | WriteSetting(QStringLiteral("nand_directory"), | ||
| 943 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)), | ||
| 944 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); | ||
| 945 | WriteSetting(QStringLiteral("sdmc_directory"), | ||
| 946 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)), | ||
| 947 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); | ||
| 948 | WriteSetting(QStringLiteral("load_directory"), | ||
| 949 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)), | ||
| 950 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); | ||
| 951 | WriteSetting(QStringLiteral("dump_directory"), | ||
| 952 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)), | ||
| 953 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); | ||
| 954 | WriteSetting(QStringLiteral("tas_directory"), | ||
| 955 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)), | ||
| 956 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); | ||
| 957 | |||
| 958 | WriteCategory(Settings::Category::DataStorage); | ||
| 959 | |||
| 960 | qt_config->endGroup(); | ||
| 961 | } | ||
| 962 | |||
| 963 | void Config::SaveDebuggingValues() { | ||
| 964 | qt_config->beginGroup(QStringLiteral("Debugging")); | ||
| 965 | |||
| 966 | // Intentionally not using the QT default setting as this is intended to be changed in the ini | ||
| 967 | qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); | ||
| 968 | |||
| 969 | WriteCategory(Settings::Category::Debugging); | ||
| 970 | WriteCategory(Settings::Category::DebuggingGraphics); | ||
| 971 | |||
| 972 | qt_config->endGroup(); | ||
| 973 | } | ||
| 974 | |||
| 975 | void Config::SaveNetworkValues() { | ||
| 976 | qt_config->beginGroup(QStringLiteral("Services")); | ||
| 977 | |||
| 978 | WriteCategory(Settings::Category::Network); | ||
| 979 | |||
| 980 | qt_config->endGroup(); | ||
| 981 | } | ||
| 982 | |||
| 983 | void Config::SaveDisabledAddOnValues() { | ||
| 984 | qt_config->beginWriteArray(QStringLiteral("DisabledAddOns")); | ||
| 985 | |||
| 986 | int i = 0; | ||
| 987 | for (const auto& elem : Settings::values.disabled_addons) { | ||
| 988 | qt_config->setArrayIndex(i); | ||
| 989 | WriteSetting(QStringLiteral("title_id"), QVariant::fromValue<u64>(elem.first), 0); | ||
| 990 | qt_config->beginWriteArray(QStringLiteral("disabled")); | ||
| 991 | for (std::size_t j = 0; j < elem.second.size(); ++j) { | ||
| 992 | qt_config->setArrayIndex(static_cast<int>(j)); | ||
| 993 | WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{}); | ||
| 994 | } | ||
| 995 | qt_config->endArray(); | ||
| 996 | ++i; | ||
| 997 | } | ||
| 998 | |||
| 999 | qt_config->endArray(); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | void Config::SaveMiscellaneousValues() { | ||
| 1003 | qt_config->beginGroup(QStringLiteral("Miscellaneous")); | ||
| 1004 | |||
| 1005 | WriteCategory(Settings::Category::Miscellaneous); | ||
| 1006 | |||
| 1007 | qt_config->endGroup(); | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | void Config::SavePathValues() { | ||
| 1011 | qt_config->beginGroup(QStringLiteral("Paths")); | ||
| 1012 | |||
| 1013 | WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); | ||
| 1014 | WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); | ||
| 1015 | qt_config->beginWriteArray(QStringLiteral("gamedirs")); | ||
| 1016 | for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { | ||
| 1017 | qt_config->setArrayIndex(i); | ||
| 1018 | const auto& game_dir = UISettings::values.game_dirs[i]; | ||
| 1019 | WriteSetting(QStringLiteral("path"), game_dir.path); | ||
| 1020 | WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false); | ||
| 1021 | WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true); | ||
| 1022 | } | ||
| 1023 | qt_config->endArray(); | ||
| 1024 | WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); | ||
| 1025 | WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{}); | ||
| 1026 | |||
| 1027 | qt_config->endGroup(); | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | void Config::SaveCpuValues() { | ||
| 1031 | qt_config->beginGroup(QStringLiteral("Cpu")); | ||
| 1032 | |||
| 1033 | WriteCategory(Settings::Category::Cpu); | ||
| 1034 | WriteCategory(Settings::Category::CpuDebug); | ||
| 1035 | WriteCategory(Settings::Category::CpuUnsafe); | ||
| 1036 | |||
| 1037 | qt_config->endGroup(); | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | void Config::SaveRendererValues() { | ||
| 1041 | qt_config->beginGroup(QStringLiteral("Renderer")); | ||
| 1042 | |||
| 1043 | WriteCategory(Settings::Category::Renderer); | ||
| 1044 | WriteCategory(Settings::Category::RendererAdvanced); | ||
| 1045 | WriteCategory(Settings::Category::RendererDebug); | ||
| 1046 | |||
| 1047 | qt_config->endGroup(); | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | void Config::SaveScreenshotValues() { | ||
| 1051 | qt_config->beginGroup(QStringLiteral("Screenshots")); | ||
| 1052 | |||
| 1053 | WriteSetting(QStringLiteral("screenshot_path"), | ||
| 1054 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))); | ||
| 1055 | WriteCategory(Settings::Category::Screenshots); | ||
| 1056 | |||
| 1057 | qt_config->endGroup(); | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | void Config::SaveShortcutValues() { | ||
| 1061 | qt_config->beginGroup(QStringLiteral("Shortcuts")); | ||
| 1062 | |||
| 1063 | // Lengths of UISettings::values.shortcuts & default_hotkeys are same. | ||
| 1064 | // However, their ordering must also be the same. | ||
| 1065 | for (std::size_t i = 0; i < default_hotkeys.size(); i++) { | ||
| 1066 | const auto& [name, group, shortcut] = UISettings::values.shortcuts[i]; | ||
| 1067 | const auto& default_hotkey = default_hotkeys[i].shortcut; | ||
| 1068 | |||
| 1069 | qt_config->beginGroup(group); | ||
| 1070 | qt_config->beginGroup(name); | ||
| 1071 | WriteSetting(QStringLiteral("KeySeq"), shortcut.keyseq, default_hotkey.keyseq); | ||
| 1072 | WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq, | ||
| 1073 | default_hotkey.controller_keyseq); | ||
| 1074 | WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context); | ||
| 1075 | WriteSetting(QStringLiteral("Repeat"), shortcut.repeat, default_hotkey.repeat); | ||
| 1076 | qt_config->endGroup(); | ||
| 1077 | qt_config->endGroup(); | ||
| 1078 | } | ||
| 1079 | |||
| 1080 | qt_config->endGroup(); | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | void Config::SaveSystemValues() { | ||
| 1084 | qt_config->beginGroup(QStringLiteral("System")); | ||
| 1085 | |||
| 1086 | WriteCategory(Settings::Category::System); | ||
| 1087 | WriteCategory(Settings::Category::SystemAudio); | ||
| 1088 | |||
| 1089 | qt_config->endGroup(); | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | void Config::SaveUIValues() { | ||
| 1093 | qt_config->beginGroup(QStringLiteral("UI")); | ||
| 1094 | |||
| 1095 | WriteCategory(Settings::Category::Ui); | ||
| 1096 | WriteCategory(Settings::Category::UiGeneral); | ||
| 1097 | |||
| 1098 | WriteSetting(QStringLiteral("theme"), UISettings::values.theme, | ||
| 1099 | QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second)); | ||
| 1100 | |||
| 1101 | SaveUIGamelistValues(); | ||
| 1102 | SaveUILayoutValues(); | ||
| 1103 | SavePathValues(); | ||
| 1104 | SaveScreenshotValues(); | ||
| 1105 | SaveShortcutValues(); | ||
| 1106 | SaveMultiplayerValues(); | ||
| 1107 | |||
| 1108 | qt_config->endGroup(); | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | void Config::SaveUIGamelistValues() { | ||
| 1112 | qt_config->beginGroup(QStringLiteral("UIGameList")); | ||
| 1113 | |||
| 1114 | WriteCategory(Settings::Category::UiGameList); | ||
| 1115 | |||
| 1116 | qt_config->beginWriteArray(QStringLiteral("favorites")); | ||
| 1117 | for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { | ||
| 1118 | qt_config->setArrayIndex(i); | ||
| 1119 | WriteSetting(QStringLiteral("program_id"), | ||
| 1120 | QVariant::fromValue(UISettings::values.favorited_ids[i])); | ||
| 1121 | } | ||
| 1122 | qt_config->endArray(); | ||
| 1123 | |||
| 1124 | qt_config->endGroup(); | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | void Config::SaveUILayoutValues() { | ||
| 1128 | qt_config->beginGroup(QStringLiteral("UILayout")); | ||
| 1129 | |||
| 1130 | WriteSetting(QStringLiteral("geometry"), UISettings::values.geometry); | ||
| 1131 | WriteSetting(QStringLiteral("state"), UISettings::values.state); | ||
| 1132 | WriteSetting(QStringLiteral("geometryRenderWindow"), UISettings::values.renderwindow_geometry); | ||
| 1133 | WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state); | ||
| 1134 | WriteSetting(QStringLiteral("microProfileDialogGeometry"), | ||
| 1135 | UISettings::values.microprofile_geometry); | ||
| 1136 | |||
| 1137 | WriteCategory(Settings::Category::UiLayout); | ||
| 1138 | |||
| 1139 | qt_config->endGroup(); | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | void Config::SaveWebServiceValues() { | ||
| 1143 | qt_config->beginGroup(QStringLiteral("WebService")); | ||
| 1144 | |||
| 1145 | WriteCategory(Settings::Category::WebService); | ||
| 1146 | |||
| 1147 | qt_config->endGroup(); | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | void Config::SaveMultiplayerValues() { | ||
| 1151 | qt_config->beginGroup(QStringLiteral("Multiplayer")); | ||
| 1152 | |||
| 1153 | WriteCategory(Settings::Category::Multiplayer); | ||
| 1154 | |||
| 1155 | // Write ban list | ||
| 1156 | qt_config->beginWriteArray(QStringLiteral("username_ban_list")); | ||
| 1157 | for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) { | ||
| 1158 | qt_config->setArrayIndex(static_cast<int>(i)); | ||
| 1159 | WriteSetting(QStringLiteral("username"), | ||
| 1160 | QString::fromStdString(UISettings::values.multiplayer_ban_list.first[i])); | ||
| 1161 | } | ||
| 1162 | qt_config->endArray(); | ||
| 1163 | qt_config->beginWriteArray(QStringLiteral("ip_ban_list")); | ||
| 1164 | for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) { | ||
| 1165 | qt_config->setArrayIndex(static_cast<int>(i)); | ||
| 1166 | WriteSetting(QStringLiteral("ip"), | ||
| 1167 | QString::fromStdString(UISettings::values.multiplayer_ban_list.second[i])); | ||
| 1168 | } | ||
| 1169 | qt_config->endArray(); | ||
| 1170 | |||
| 1171 | qt_config->endGroup(); | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | QVariant Config::ReadSetting(const QString& name) const { | ||
| 1175 | return qt_config->value(name); | ||
| 1176 | } | ||
| 1177 | |||
| 1178 | QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const { | ||
| 1179 | QVariant result; | ||
| 1180 | if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { | ||
| 1181 | result = default_value; | ||
| 1182 | } else { | ||
| 1183 | result = qt_config->value(name, default_value); | ||
| 1184 | } | ||
| 1185 | return result; | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | void Config::WriteSetting(const QString& name, const QVariant& value) { | ||
| 1189 | qt_config->setValue(name, value); | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | void Config::WriteSetting(const QString& name, const QVariant& value, | ||
| 1193 | const QVariant& default_value) { | ||
| 1194 | qt_config->setValue(name + QStringLiteral("/default"), value == default_value); | ||
| 1195 | qt_config->setValue(name, value); | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, | ||
| 1199 | bool use_global) { | ||
| 1200 | if (!global) { | ||
| 1201 | qt_config->setValue(name + QStringLiteral("/use_global"), use_global); | ||
| 1202 | } | ||
| 1203 | if (global || !use_global) { | ||
| 1204 | qt_config->setValue(name + QStringLiteral("/default"), value == default_value); | ||
| 1205 | qt_config->setValue(name, value); | ||
| 1206 | } | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | void Config::Reload() { | ||
| 1210 | ReadValues(); | ||
| 1211 | // To apply default value changes | ||
| 1212 | SaveValues(); | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | void Config::Save() { | ||
| 1216 | SaveValues(); | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | void Config::ReadControlPlayerValue(std::size_t player_index) { | ||
| 1220 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 1221 | ReadPlayerValue(player_index); | ||
| 1222 | qt_config->endGroup(); | ||
| 1223 | } | ||
| 1224 | |||
| 1225 | void Config::SaveControlPlayerValue(std::size_t player_index) { | ||
| 1226 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 1227 | SavePlayerValue(player_index); | ||
| 1228 | qt_config->endGroup(); | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | void Config::ClearControlPlayerValues() { | ||
| 1232 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 1233 | // If key is an empty string, all keys in the current group() are removed. | ||
| 1234 | qt_config->remove(QString{}); | ||
| 1235 | qt_config->endGroup(); | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | const std::string& Config::GetConfigFilePath() const { | ||
| 1239 | return qt_config_loc; | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | static auto FindRelevantList(Settings::Category category) { | ||
| 1243 | auto& map = Settings::values.linkage.by_category; | ||
| 1244 | if (map.contains(category)) { | ||
| 1245 | return Settings::values.linkage.by_category[category]; | ||
| 1246 | } | ||
| 1247 | return UISettings::values.linkage.by_category[category]; | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | void Config::ReadCategory(Settings::Category category) { | ||
| 1251 | const auto& settings = FindRelevantList(category); | ||
| 1252 | std::for_each(settings.begin(), settings.end(), | ||
| 1253 | [&](const auto& setting) { ReadSettingGeneric(setting); }); | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | void Config::WriteCategory(Settings::Category category) { | ||
| 1257 | const auto& settings = FindRelevantList(category); | ||
| 1258 | std::for_each(settings.begin(), settings.end(), | ||
| 1259 | [&](const auto& setting) { WriteSettingGeneric(setting); }); | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) { | ||
| 1263 | if (!setting->Save() || (!setting->Switchable() && !global)) { | ||
| 1264 | return; | ||
| 1265 | } | ||
| 1266 | const QString name = QString::fromStdString(setting->GetLabel()); | ||
| 1267 | const auto default_value = | ||
| 1268 | QVariant::fromValue<QString>(QString::fromStdString(setting->DefaultToString())); | ||
| 1269 | |||
| 1270 | bool use_global = true; | ||
| 1271 | if (setting->Switchable() && !global) { | ||
| 1272 | use_global = qt_config->value(name + QStringLiteral("/use_global"), true).value<bool>(); | ||
| 1273 | setting->SetGlobal(use_global); | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | if (global || !use_global) { | ||
| 1277 | const bool is_default = | ||
| 1278 | qt_config->value(name + QStringLiteral("/default"), true).value<bool>(); | ||
| 1279 | if (!is_default) { | ||
| 1280 | setting->LoadString( | ||
| 1281 | qt_config->value(name, default_value).value<QString>().toStdString()); | ||
| 1282 | } else { | ||
| 1283 | // Empty string resets the Setting to default | ||
| 1284 | setting->LoadString(""); | ||
| 1285 | } | ||
| 1286 | } | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | void Config::WriteSettingGeneric(Settings::BasicSetting* const setting) const { | ||
| 1290 | if (!setting->Save()) { | ||
| 1291 | return; | ||
| 1292 | } | ||
| 1293 | const QVariant value = QVariant::fromValue(QString::fromStdString(setting->ToString())); | ||
| 1294 | const QVariant default_value = | ||
| 1295 | QVariant::fromValue(QString::fromStdString(setting->DefaultToString())); | ||
| 1296 | const QString label = QString::fromStdString(setting->GetLabel()); | ||
| 1297 | if (setting->Switchable()) { | ||
| 1298 | if (!global) { | ||
| 1299 | qt_config->setValue(label + QStringLiteral("/use_global"), setting->UsingGlobal()); | ||
| 1300 | } | ||
| 1301 | if (global || !setting->UsingGlobal()) { | ||
| 1302 | qt_config->setValue(label + QStringLiteral("/default"), value == default_value); | ||
| 1303 | qt_config->setValue(label, value); | ||
| 1304 | } | ||
| 1305 | } else if (global) { | ||
| 1306 | qt_config->setValue(label + QStringLiteral("/default"), value == default_value); | ||
| 1307 | qt_config->setValue(label, value); | ||
| 1308 | } | ||
| 1309 | } | ||
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h deleted file mode 100644 index 1589ba057..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_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_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp index a51359903..7e16cf17d 100644 --- a/src/yuzu/configuration/configure_cpu.cpp +++ b/src/yuzu/configuration/configure_cpu.cpp | |||
| @@ -27,6 +27,13 @@ ConfigureCpu::ConfigureCpu(const Core::System& system_, | |||
| 27 | 27 | ||
| 28 | connect(accuracy_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this, | 28 | connect(accuracy_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this, |
| 29 | &ConfigureCpu::UpdateGroup); | 29 | &ConfigureCpu::UpdateGroup); |
| 30 | |||
| 31 | connect(backend_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||
| 32 | &ConfigureCpu::UpdateGroup); | ||
| 33 | |||
| 34 | #ifdef HAS_NCE | ||
| 35 | ui->backend_group->setVisible(true); | ||
| 36 | #endif | ||
| 30 | } | 37 | } |
| 31 | 38 | ||
| 32 | ConfigureCpu::~ConfigureCpu() = default; | 39 | ConfigureCpu::~ConfigureCpu() = default; |
| @@ -34,6 +41,7 @@ ConfigureCpu::~ConfigureCpu() = default; | |||
| 34 | void ConfigureCpu::SetConfiguration() {} | 41 | void ConfigureCpu::SetConfiguration() {} |
| 35 | void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) { | 42 | void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) { |
| 36 | auto* accuracy_layout = ui->widget_accuracy->layout(); | 43 | auto* accuracy_layout = ui->widget_accuracy->layout(); |
| 44 | auto* backend_layout = ui->widget_backend->layout(); | ||
| 37 | auto* unsafe_layout = ui->unsafe_widget->layout(); | 45 | auto* unsafe_layout = ui->unsafe_widget->layout(); |
| 38 | std::map<u32, QWidget*> unsafe_hold{}; | 46 | std::map<u32, QWidget*> unsafe_hold{}; |
| 39 | 47 | ||
| @@ -62,6 +70,9 @@ void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) { | |||
| 62 | // Keep track of cpu_accuracy combobox to display/hide the unsafe settings | 70 | // Keep track of cpu_accuracy combobox to display/hide the unsafe settings |
| 63 | accuracy_layout->addWidget(widget); | 71 | accuracy_layout->addWidget(widget); |
| 64 | accuracy_combobox = widget->combobox; | 72 | accuracy_combobox = widget->combobox; |
| 73 | } else if (setting->Id() == Settings::values.cpu_backend.Id()) { | ||
| 74 | backend_layout->addWidget(widget); | ||
| 75 | backend_combobox = widget->combobox; | ||
| 65 | } else { | 76 | } else { |
| 66 | // Presently, all other settings here are unsafe checkboxes | 77 | // Presently, all other settings here are unsafe checkboxes |
| 67 | unsafe_hold.insert({setting->Id(), widget}); | 78 | unsafe_hold.insert({setting->Id(), widget}); |
| @@ -73,6 +84,7 @@ void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) { | |||
| 73 | } | 84 | } |
| 74 | 85 | ||
| 75 | UpdateGroup(accuracy_combobox->currentIndex()); | 86 | UpdateGroup(accuracy_combobox->currentIndex()); |
| 87 | UpdateGroup(backend_combobox->currentIndex()); | ||
| 76 | } | 88 | } |
| 77 | 89 | ||
| 78 | void ConfigureCpu::UpdateGroup(int index) { | 90 | void ConfigureCpu::UpdateGroup(int index) { |
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h index 61a6de7aa..a102b4c1f 100644 --- a/src/yuzu/configuration/configure_cpu.h +++ b/src/yuzu/configuration/configure_cpu.h | |||
| @@ -49,4 +49,5 @@ private: | |||
| 49 | std::vector<std::function<void(bool)>> apply_funcs{}; | 49 | std::vector<std::function<void(bool)>> apply_funcs{}; |
| 50 | 50 | ||
| 51 | QComboBox* accuracy_combobox; | 51 | QComboBox* accuracy_combobox; |
| 52 | QComboBox* backend_combobox; | ||
| 52 | }; | 53 | }; |
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui index f734e842e..13fd43605 100644 --- a/src/yuzu/configuration/configure_cpu.ui +++ b/src/yuzu/configuration/configure_cpu.ui | |||
| @@ -60,6 +60,36 @@ | |||
| 60 | </widget> | 60 | </widget> |
| 61 | </item> | 61 | </item> |
| 62 | <item> | 62 | <item> |
| 63 | <widget class="QGroupBox" name="backend_group"> | ||
| 64 | <property name="title"> | ||
| 65 | <string>CPU Backend</string> | ||
| 66 | </property> | ||
| 67 | <layout class="QVBoxLayout"> | ||
| 68 | <item> | ||
| 69 | <widget class="QWidget" name="widget_backend" native="true"> | ||
| 70 | <layout class="QVBoxLayout" name="verticalLayout1"> | ||
| 71 | <property name="leftMargin"> | ||
| 72 | <number>0</number> | ||
| 73 | </property> | ||
| 74 | <property name="topMargin"> | ||
| 75 | <number>0</number> | ||
| 76 | </property> | ||
| 77 | <property name="rightMargin"> | ||
| 78 | <number>0</number> | ||
| 79 | </property> | ||
| 80 | <property name="bottomMargin"> | ||
| 81 | <number>0</number> | ||
| 82 | </property> | ||
| 83 | </layout> | ||
| 84 | </widget> | ||
| 85 | </item> | ||
| 86 | </layout> | ||
| 87 | <property name="visible"> | ||
| 88 | <bool>false</bool> | ||
| 89 | </property> | ||
| 90 | </widget> | ||
| 91 | </item> | ||
| 92 | <item> | ||
| 63 | <widget class="QGroupBox" name="unsafe_group"> | 93 | <widget class="QGroupBox" name="unsafe_group"> |
| 64 | <property name="title"> | 94 | <property name="title"> |
| 65 | <string>Unsafe CPU Optimization Settings</string> | 95 | <string>Unsafe CPU Optimization Settings</string> |
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_general.cpp b/src/yuzu/configuration/configure_general.cpp index c727fadd1..701b895e7 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -36,12 +36,29 @@ ConfigureGeneral::~ConfigureGeneral() = default; | |||
| 36 | void ConfigureGeneral::SetConfiguration() {} | 36 | void ConfigureGeneral::SetConfiguration() {} |
| 37 | 37 | ||
| 38 | void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { | 38 | void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { |
| 39 | QLayout& layout = *ui->general_widget->layout(); | 39 | QLayout& general_layout = *ui->general_widget->layout(); |
| 40 | QLayout& linux_layout = *ui->linux_widget->layout(); | ||
| 40 | 41 | ||
| 41 | std::map<u32, QWidget*> hold{}; | 42 | std::map<u32, QWidget*> general_hold{}; |
| 43 | std::map<u32, QWidget*> linux_hold{}; | ||
| 42 | 44 | ||
| 43 | for (const auto setting : | 45 | std::vector<Settings::BasicSetting*> settings; |
| 44 | UISettings::values.linkage.by_category[Settings::Category::UiGeneral]) { | 46 | |
| 47 | auto push = [&settings](auto& list) { | ||
| 48 | for (auto setting : list) { | ||
| 49 | settings.push_back(setting); | ||
| 50 | } | ||
| 51 | }; | ||
| 52 | |||
| 53 | push(UISettings::values.linkage.by_category[Settings::Category::UiGeneral]); | ||
| 54 | push(Settings::values.linkage.by_category[Settings::Category::Linux]); | ||
| 55 | |||
| 56 | // Only show Linux group on Unix | ||
| 57 | #ifndef __unix__ | ||
| 58 | ui->LinuxGroupBox->setVisible(false); | ||
| 59 | #endif | ||
| 60 | |||
| 61 | for (const auto setting : settings) { | ||
| 45 | auto* widget = builder.BuildWidget(setting, apply_funcs); | 62 | auto* widget = builder.BuildWidget(setting, apply_funcs); |
| 46 | 63 | ||
| 47 | if (widget == nullptr) { | 64 | if (widget == nullptr) { |
| @@ -52,11 +69,23 @@ void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { | |||
| 52 | continue; | 69 | continue; |
| 53 | } | 70 | } |
| 54 | 71 | ||
| 55 | hold.emplace(setting->Id(), widget); | 72 | switch (setting->GetCategory()) { |
| 73 | case Settings::Category::UiGeneral: | ||
| 74 | general_hold.emplace(setting->Id(), widget); | ||
| 75 | break; | ||
| 76 | case Settings::Category::Linux: | ||
| 77 | linux_hold.emplace(setting->Id(), widget); | ||
| 78 | break; | ||
| 79 | default: | ||
| 80 | widget->deleteLater(); | ||
| 81 | } | ||
| 56 | } | 82 | } |
| 57 | 83 | ||
| 58 | for (const auto& [id, widget] : hold) { | 84 | for (const auto& [id, widget] : general_hold) { |
| 59 | layout.addWidget(widget); | 85 | general_layout.addWidget(widget); |
| 86 | } | ||
| 87 | for (const auto& [id, widget] : linux_hold) { | ||
| 88 | linux_layout.addWidget(widget); | ||
| 60 | } | 89 | } |
| 61 | } | 90 | } |
| 62 | 91 | ||
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index a10e7d3a5..ef20891a3 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -47,6 +47,33 @@ | |||
| 47 | </widget> | 47 | </widget> |
| 48 | </item> | 48 | </item> |
| 49 | <item> | 49 | <item> |
| 50 | <widget class="QGroupBox" name="LinuxGroupBox"> | ||
| 51 | <property name="title"> | ||
| 52 | <string>Linux</string> | ||
| 53 | </property> | ||
| 54 | <layout class="QVBoxLayout" name="LinuxVerticalLayout_1"> | ||
| 55 | <item> | ||
| 56 | <widget class="QWidget" name="linux_widget" native="true"> | ||
| 57 | <layout class="QVBoxLayout" name="LinuxVerticalLayout_2"> | ||
| 58 | <property name="leftMargin"> | ||
| 59 | <number>0</number> | ||
| 60 | </property> | ||
| 61 | <property name="topMargin"> | ||
| 62 | <number>0</number> | ||
| 63 | </property> | ||
| 64 | <property name="rightMargin"> | ||
| 65 | <number>0</number> | ||
| 66 | </property> | ||
| 67 | <property name="bottomMargin"> | ||
| 68 | <number>0</number> | ||
| 69 | </property> | ||
| 70 | </layout> | ||
| 71 | </widget> | ||
| 72 | </item> | ||
| 73 | </layout> | ||
| 74 | </widget> | ||
| 75 | </item> | ||
| 76 | <item> | ||
| 50 | <spacer name="verticalSpacer"> | 77 | <spacer name="verticalSpacer"> |
| 51 | <property name="orientation"> | 78 | <property name="orientation"> |
| 52 | <enum>Qt::Vertical</enum> | 79 | <enum>Qt::Vertical</enum> |
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_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 9259e2a5d..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" |
| @@ -1397,25 +1398,25 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() { | |||
| 1397 | 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) { |
| 1398 | emulated_controller->SetButtonParam( | 1399 | emulated_controller->SetButtonParam( |
| 1399 | button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( | 1400 | button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( |
| 1400 | Config::default_buttons[button_id])}); | 1401 | QtConfig::default_buttons[button_id])}); |
| 1401 | } | 1402 | } |
| 1402 | 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) { |
| 1403 | Common::ParamPackage analog_param{}; | 1404 | Common::ParamPackage analog_param{}; |
| 1404 | 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) { |
| 1405 | Common::ParamPackage params{InputCommon::GenerateKeyboardParam( | 1406 | Common::ParamPackage params{InputCommon::GenerateKeyboardParam( |
| 1406 | Config::default_analogs[analog_id][sub_button_id])}; | 1407 | QtConfig::default_analogs[analog_id][sub_button_id])}; |
| 1407 | SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]); | 1408 | SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]); |
| 1408 | } | 1409 | } |
| 1409 | 1410 | ||
| 1410 | analog_param.Set("modifier", InputCommon::GenerateKeyboardParam( | 1411 | analog_param.Set("modifier", InputCommon::GenerateKeyboardParam( |
| 1411 | Config::default_stick_mod[analog_id])); | 1412 | QtConfig::default_stick_mod[analog_id])); |
| 1412 | emulated_controller->SetStickParam(analog_id, analog_param); | 1413 | emulated_controller->SetStickParam(analog_id, analog_param); |
| 1413 | } | 1414 | } |
| 1414 | 1415 | ||
| 1415 | 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) { |
| 1416 | emulated_controller->SetMotionParam( | 1417 | emulated_controller->SetMotionParam( |
| 1417 | motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( | 1418 | motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( |
| 1418 | Config::default_motions[motion_id])}); | 1419 | QtConfig::default_motions[motion_id])}); |
| 1419 | } | 1420 | } |
| 1420 | 1421 | ||
| 1421 | // 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_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 cc2513001..c8ee46c04 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h | |||
| @@ -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_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_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_system.ui b/src/yuzu/configuration/configure_system.ui index 2a735836e..04b771129 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui | |||
| @@ -57,7 +57,7 @@ | |||
| 57 | </widget> | 57 | </widget> |
| 58 | </item> | 58 | </item> |
| 59 | <item> | 59 | <item> |
| 60 | <widget class="QGroupBox" name="groupBox"> | 60 | <widget class="QGroupBox" name="coreGroup"> |
| 61 | <property name="title"> | 61 | <property name="title"> |
| 62 | <string>Core</string> | 62 | <string>Core</string> |
| 63 | </property> | 63 | </property> |
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_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 1434b1a56..7e908924c 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,136 @@ 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()); |
| 47 | INSERT(Settings, cpu_backend, tr("Backend:"), QStringLiteral()); | ||
| 45 | 48 | ||
| 46 | // Cpu Debug | 49 | // Cpu Debug |
| 47 | 50 | ||
| 48 | // Cpu Unsafe | 51 | // 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( | 52 | INSERT( |
| 63 | Settings, cpuopt_unsafe_fastmem_check, "Disable address space checks", | 53 | Settings, cpuopt_unsafe_unfuse_fma, |
| 64 | "This option improves speed by eliminating a safety check before every memory read/write " | 54 | 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."); | 55 | 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", | 56 | "CPUs without native FMA support.")); |
| 67 | "This option improves speed by relying only on the semantics of cmpxchg to ensure " | 57 | INSERT( |
| 58 | Settings, cpuopt_unsafe_reduce_fp_error, tr("Faster FRSQRTE and FRECPE"), | ||
| 59 | tr("This option improves the speed of some approximate floating-point functions by using " | ||
| 60 | "less accurate native approximations.")); | ||
| 61 | INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, | ||
| 62 | tr("Faster ASIMD instructions (32 bits only)"), | ||
| 63 | tr("This option improves the speed of 32 bits ASIMD floating-point functions by running " | ||
| 64 | "with incorrect rounding modes.")); | ||
| 65 | INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"), | ||
| 66 | tr("This option improves speed by removing NaN checking. Please note this also reduces " | ||
| 67 | "accuracy of certain floating-point instructions.")); | ||
| 68 | INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"), | ||
| 69 | tr("This option improves speed by eliminating a safety check before every memory " | ||
| 70 | "read/write " | ||
| 71 | "in guest. Disabling it may allow a game to read/write the emulator's memory.")); | ||
| 72 | INSERT( | ||
| 73 | Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"), | ||
| 74 | 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 " | 75 | "safety of exclusive access instructions. Please note this may result in deadlocks and " |
| 69 | "other race conditions."); | 76 | "other race conditions.")); |
| 70 | 77 | ||
| 71 | // Renderer | 78 | // Renderer |
| 72 | INSERT(Settings, renderer_backend, "API:", ""); | 79 | INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral()); |
| 73 | INSERT(Settings, vulkan_device, "Device:", ""); | 80 | INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral()); |
| 74 | INSERT(Settings, shader_backend, "Shader Backend:", ""); | 81 | INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral()); |
| 75 | INSERT(Settings, resolution_setup, "Resolution:", ""); | 82 | INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral()); |
| 76 | INSERT(Settings, scaling_filter, "Window Adapting Filter:", ""); | 83 | INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral()); |
| 77 | INSERT(Settings, fsr_sharpening_slider, "FSR Sharpness:", ""); | 84 | INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral()); |
| 78 | INSERT(Settings, anti_aliasing, "Anti-Aliasing Method:", ""); | 85 | INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral()); |
| 79 | INSERT(Settings, fullscreen_mode, "Fullscreen Mode:", ""); | 86 | INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral()); |
| 80 | INSERT(Settings, aspect_ratio, "Aspect Ratio:", ""); | 87 | INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral()); |
| 81 | INSERT(Settings, use_disk_shader_cache, "Use disk pipeline cache", ""); | 88 | INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral()); |
| 82 | INSERT(Settings, use_asynchronous_gpu_emulation, "Use asynchronous GPU emulation", ""); | 89 | INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"), |
| 83 | INSERT(Settings, nvdec_emulation, "NVDEC emulation:", ""); | 90 | QStringLiteral()); |
| 84 | INSERT(Settings, accelerate_astc, "ASTC Decoding Method:", ""); | 91 | INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral()); |
| 85 | INSERT(Settings, astc_recompression, "ASTC Recompression Method:", ""); | 92 | INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral()); |
| 86 | INSERT(Settings, vsync_mode, "VSync Mode:", | 93 | 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 " | 94 | INSERT( |
| 95 | Settings, vsync_mode, tr("VSync Mode:"), | ||
| 96 | 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 " | 97 | "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 " | 98 | "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 " | 99 | "frames.\nImmediate (no synchronization) just presents whatever is available and can " |
| 91 | "exhibit tearing."); | 100 | "exhibit tearing.")); |
| 92 | INSERT(Settings, bg_red, "", ""); | 101 | INSERT(Settings, bg_red, QStringLiteral(), QStringLiteral()); |
| 93 | INSERT(Settings, bg_green, "", ""); | 102 | INSERT(Settings, bg_green, QStringLiteral(), QStringLiteral()); |
| 94 | INSERT(Settings, bg_blue, "", ""); | 103 | INSERT(Settings, bg_blue, QStringLiteral(), QStringLiteral()); |
| 95 | 104 | ||
| 96 | // Renderer (Advanced Graphics) | 105 | // Renderer (Advanced Graphics) |
| 97 | INSERT(Settings, async_presentation, "Enable asynchronous presentation (Vulkan only)", ""); | 106 | INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"), |
| 98 | INSERT(Settings, renderer_force_max_clock, "Force maximum clocks (Vulkan only)", | 107 | QStringLiteral()); |
| 99 | "Runs work in the background while waiting for graphics commands to keep the GPU from " | 108 | INSERT( |
| 100 | "lowering its clock speed."); | 109 | Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"), |
| 101 | INSERT(Settings, max_anisotropy, "Anisotropic Filtering:", ""); | 110 | tr("Runs work in the background while waiting for graphics commands to keep the GPU from " |
| 102 | INSERT(Settings, gpu_accuracy, "Accuracy Level:", ""); | 111 | "lowering its clock speed.")); |
| 103 | INSERT(Settings, use_asynchronous_shaders, "Use asynchronous shader building (Hack)", | 112 | INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral()); |
| 104 | "Enables asynchronous shader compilation, which may reduce shader stutter. This feature " | 113 | INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral()); |
| 105 | "is experimental."); | 114 | INSERT( |
| 106 | INSERT(Settings, use_fast_gpu_time, "Use Fast GPU Time (Hack)", | 115 | 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 " | 116 | tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature " |
| 108 | "native resolution."); | 117 | "is experimental.")); |
| 109 | INSERT(Settings, use_vulkan_driver_pipeline_cache, "Use Vulkan pipeline cache", | 118 | 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 " | 119 | 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 " | 120 | "native resolution.")); |
| 112 | "files internally."); | 121 | INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"), |
| 113 | INSERT(Settings, enable_compute_pipelines, "Enable Compute Pipelines (Intel Vulkan Only)", | 122 | 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 " | 123 | "time significantly in cases where the Vulkan driver does not store pipeline cache " |
| 124 | "files internally.")); | ||
| 125 | INSERT( | ||
| 126 | Settings, enable_compute_pipelines, tr("Enable Compute Pipelines (Intel Vulkan Only)"), | ||
| 127 | 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 " | 128 | "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled " |
| 116 | "on all other drivers."); | 129 | "on all other drivers.")); |
| 117 | INSERT(Settings, use_reactive_flushing, "Enable Reactive Flushing", | 130 | INSERT( |
| 118 | "Uses reactive flushing instead of predictive flushing, allowing more accurate memory " | 131 | Settings, use_reactive_flushing, tr("Enable Reactive Flushing"), |
| 119 | "syncing."); | 132 | tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory " |
| 120 | INSERT(Settings, use_video_framerate, "Sync to framerate of video playback", | 133 | "syncing.")); |
| 121 | "Run the game at normal speed during video playback, even when the framerate is " | 134 | INSERT(Settings, use_video_framerate, tr("Sync to framerate of video playback"), |
| 122 | "unlocked."); | 135 | tr("Run the game at normal speed during video playback, even when the framerate is " |
| 123 | INSERT(Settings, barrier_feedback_loops, "Barrier feedback loops", | 136 | "unlocked.")); |
| 124 | "Improves rendering of transparency effects in specific games."); | 137 | INSERT(Settings, barrier_feedback_loops, tr("Barrier feedback loops"), |
| 138 | tr("Improves rendering of transparency effects in specific games.")); | ||
| 125 | 139 | ||
| 126 | // Renderer (Debug) | 140 | // Renderer (Debug) |
| 127 | 141 | ||
| 128 | // System | 142 | // System |
| 129 | INSERT(Settings, rng_seed, "RNG Seed", ""); | 143 | INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral()); |
| 130 | INSERT(Settings, rng_seed_enabled, "", ""); | 144 | INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral()); |
| 131 | INSERT(Settings, device_name, "Device Name", ""); | 145 | INSERT(Settings, device_name, tr("Device Name"), QStringLiteral()); |
| 132 | INSERT(Settings, custom_rtc, "Custom RTC", ""); | 146 | INSERT(Settings, custom_rtc, tr("Custom RTC"), QStringLiteral()); |
| 133 | INSERT(Settings, custom_rtc_enabled, "", ""); | 147 | INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral()); |
| 134 | INSERT(Settings, language_index, | 148 | INSERT(Settings, language_index, tr("Language:"), |
| 135 | "Language:", "Note: this can be overridden when region setting is auto-select"); | 149 | tr("Note: this can be overridden when region setting is auto-select")); |
| 136 | INSERT(Settings, region_index, "Region:", ""); | 150 | INSERT(Settings, region_index, tr("Region:"), QStringLiteral()); |
| 137 | INSERT(Settings, time_zone_index, "Time Zone:", ""); | 151 | INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral()); |
| 138 | INSERT(Settings, sound_index, "Sound Output Mode:", ""); | 152 | INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral()); |
| 139 | INSERT(Settings, use_docked_mode, "Console Mode:", ""); | 153 | INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral()); |
| 140 | INSERT(Settings, current_user, "", ""); | 154 | INSERT(Settings, current_user, QStringLiteral(), QStringLiteral()); |
| 141 | 155 | ||
| 142 | // Controls | 156 | // Controls |
| 143 | 157 | ||
| @@ -154,11 +168,17 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { | |||
| 154 | // Ui | 168 | // Ui |
| 155 | 169 | ||
| 156 | // Ui General | 170 | // Ui General |
| 157 | INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); | 171 | 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", ""); | 172 | INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"), |
| 159 | INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); | 173 | QStringLiteral()); |
| 160 | INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); | 174 | INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"), |
| 161 | INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); | 175 | QStringLiteral()); |
| 176 | INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral()); | ||
| 177 | INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), | ||
| 178 | QStringLiteral()); | ||
| 179 | |||
| 180 | // Linux | ||
| 181 | INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral()); | ||
| 162 | 182 | ||
| 163 | // Ui Debugging | 183 | // Ui Debugging |
| 164 | 184 | ||
| @@ -178,140 +198,146 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) { | |||
| 178 | return parent->tr(text, context); | 198 | return parent->tr(text, context); |
| 179 | }; | 199 | }; |
| 180 | 200 | ||
| 181 | #define PAIR(ENUM, VALUE, TRANSLATION) \ | 201 | #define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)} |
| 182 | { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION) } | ||
| 183 | #define CTX_PAIR(ENUM, VALUE, TRANSLATION, CONTEXT) \ | ||
| 184 | { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION, CONTEXT) } | ||
| 185 | 202 | ||
| 186 | // Intentionally skipping VSyncMode to let the UI fill that one out | 203 | // Intentionally skipping VSyncMode to let the UI fill that one out |
| 187 | 204 | ||
| 188 | translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), | 205 | translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), |
| 189 | { | 206 | { |
| 190 | PAIR(AstcDecodeMode, Cpu, "CPU"), | 207 | PAIR(AstcDecodeMode, Cpu, tr("CPU")), |
| 191 | PAIR(AstcDecodeMode, Gpu, "GPU"), | 208 | PAIR(AstcDecodeMode, Gpu, tr("GPU")), |
| 192 | PAIR(AstcDecodeMode, CpuAsynchronous, "CPU Asynchronous"), | 209 | PAIR(AstcDecodeMode, CpuAsynchronous, tr("CPU Asynchronous")), |
| 193 | }}); | ||
| 194 | translations->insert({Settings::EnumMetadata<Settings::AstcRecompression>::Index(), | ||
| 195 | { | ||
| 196 | PAIR(AstcRecompression, Uncompressed, "Uncompressed (Best quality)"), | ||
| 197 | PAIR(AstcRecompression, Bc1, "BC1 (Low quality)"), | ||
| 198 | PAIR(AstcRecompression, Bc3, "BC3 (Medium quality)"), | ||
| 199 | }}); | 210 | }}); |
| 211 | translations->insert( | ||
| 212 | {Settings::EnumMetadata<Settings::AstcRecompression>::Index(), | ||
| 213 | { | ||
| 214 | PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")), | ||
| 215 | PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")), | ||
| 216 | PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")), | ||
| 217 | }}); | ||
| 200 | translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), | 218 | translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), |
| 201 | { | 219 | { |
| 202 | #ifdef HAS_OPENGL | 220 | #ifdef HAS_OPENGL |
| 203 | PAIR(RendererBackend, OpenGL, "OpenGL"), | 221 | PAIR(RendererBackend, OpenGL, tr("OpenGL")), |
| 204 | #endif | 222 | #endif |
| 205 | PAIR(RendererBackend, Vulkan, "Vulkan"), | 223 | PAIR(RendererBackend, Vulkan, tr("Vulkan")), |
| 206 | PAIR(RendererBackend, Null, "Null"), | 224 | PAIR(RendererBackend, Null, tr("Null")), |
| 207 | }}); | ||
| 208 | translations->insert({Settings::EnumMetadata<Settings::ShaderBackend>::Index(), | ||
| 209 | { | ||
| 210 | PAIR(ShaderBackend, Glsl, "GLSL"), | ||
| 211 | PAIR(ShaderBackend, Glasm, "GLASM (Assembly Shaders, NVIDIA Only)"), | ||
| 212 | PAIR(ShaderBackend, SpirV, "SPIR-V (Experimental, Mesa Only)"), | ||
| 213 | }}); | 225 | }}); |
| 226 | translations->insert( | ||
| 227 | {Settings::EnumMetadata<Settings::ShaderBackend>::Index(), | ||
| 228 | { | ||
| 229 | PAIR(ShaderBackend, Glsl, tr("GLSL")), | ||
| 230 | PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")), | ||
| 231 | PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, Mesa Only)")), | ||
| 232 | }}); | ||
| 214 | translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(), | 233 | translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(), |
| 215 | { | 234 | { |
| 216 | PAIR(GpuAccuracy, Normal, "Normal"), | 235 | PAIR(GpuAccuracy, Normal, tr("Normal")), |
| 217 | PAIR(GpuAccuracy, High, "High"), | 236 | PAIR(GpuAccuracy, High, tr("High")), |
| 218 | PAIR(GpuAccuracy, Extreme, "Extreme"), | 237 | PAIR(GpuAccuracy, Extreme, tr("Extreme")), |
| 219 | }}); | 238 | }}); |
| 220 | translations->insert({Settings::EnumMetadata<Settings::CpuAccuracy>::Index(), | 239 | translations->insert( |
| 240 | {Settings::EnumMetadata<Settings::CpuAccuracy>::Index(), | ||
| 241 | { | ||
| 242 | PAIR(CpuAccuracy, Auto, tr("Auto")), | ||
| 243 | PAIR(CpuAccuracy, Accurate, tr("Accurate")), | ||
| 244 | PAIR(CpuAccuracy, Unsafe, tr("Unsafe")), | ||
| 245 | PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")), | ||
| 246 | }}); | ||
| 247 | translations->insert({Settings::EnumMetadata<Settings::CpuBackend>::Index(), | ||
| 221 | { | 248 | { |
| 222 | PAIR(CpuAccuracy, Auto, "Auto"), | 249 | PAIR(CpuBackend, Dynarmic, tr("Dynarmic")), |
| 223 | PAIR(CpuAccuracy, Accurate, "Accurate"), | 250 | PAIR(CpuBackend, Nce, tr("NCE")), |
| 224 | PAIR(CpuAccuracy, Unsafe, "Unsafe"), | ||
| 225 | PAIR(CpuAccuracy, Paranoid, "Paranoid (disables most optimizations)"), | ||
| 226 | }}); | 251 | }}); |
| 227 | translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(), | 252 | translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(), |
| 228 | { | 253 | { |
| 229 | PAIR(FullscreenMode, Borderless, "Borderless Windowed"), | 254 | PAIR(FullscreenMode, Borderless, tr("Borderless Windowed")), |
| 230 | PAIR(FullscreenMode, Exclusive, "Exclusive Fullscreen"), | 255 | PAIR(FullscreenMode, Exclusive, tr("Exclusive Fullscreen")), |
| 231 | }}); | 256 | }}); |
| 232 | translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(), | 257 | translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(), |
| 233 | { | 258 | { |
| 234 | PAIR(NvdecEmulation, Off, "No Video Output"), | 259 | PAIR(NvdecEmulation, Off, tr("No Video Output")), |
| 235 | PAIR(NvdecEmulation, Cpu, "CPU Video Decoding"), | 260 | PAIR(NvdecEmulation, Cpu, tr("CPU Video Decoding")), |
| 236 | PAIR(NvdecEmulation, Gpu, "GPU Video Decoding (Default)"), | 261 | PAIR(NvdecEmulation, Gpu, tr("GPU Video Decoding (Default)")), |
| 237 | }}); | ||
| 238 | translations->insert({Settings::EnumMetadata<Settings::ResolutionSetup>::Index(), | ||
| 239 | { | ||
| 240 | PAIR(ResolutionSetup, Res1_2X, "0.5X (360p/540p) [EXPERIMENTAL]"), | ||
| 241 | PAIR(ResolutionSetup, Res3_4X, "0.75X (540p/810p) [EXPERIMENTAL]"), | ||
| 242 | PAIR(ResolutionSetup, Res1X, "1X (720p/1080p)"), | ||
| 243 | PAIR(ResolutionSetup, Res3_2X, "1.5X (1080p/1620p) [EXPERIMENTAL]"), | ||
| 244 | PAIR(ResolutionSetup, Res2X, "2X (1440p/2160p)"), | ||
| 245 | PAIR(ResolutionSetup, Res3X, "3X (2160p/3240p)"), | ||
| 246 | PAIR(ResolutionSetup, Res4X, "4X (2880p/4320p)"), | ||
| 247 | PAIR(ResolutionSetup, Res5X, "5X (3600p/5400p)"), | ||
| 248 | PAIR(ResolutionSetup, Res6X, "6X (4320p/6480p)"), | ||
| 249 | PAIR(ResolutionSetup, Res7X, "7X (5040p/7560p)"), | ||
| 250 | PAIR(ResolutionSetup, Res8X, "8X (5760p/8640p)"), | ||
| 251 | }}); | 262 | }}); |
| 263 | translations->insert( | ||
| 264 | {Settings::EnumMetadata<Settings::ResolutionSetup>::Index(), | ||
| 265 | { | ||
| 266 | PAIR(ResolutionSetup, Res1_2X, tr("0.5X (360p/540p) [EXPERIMENTAL]")), | ||
| 267 | PAIR(ResolutionSetup, Res3_4X, tr("0.75X (540p/810p) [EXPERIMENTAL]")), | ||
| 268 | PAIR(ResolutionSetup, Res1X, tr("1X (720p/1080p)")), | ||
| 269 | PAIR(ResolutionSetup, Res3_2X, tr("1.5X (1080p/1620p) [EXPERIMENTAL]")), | ||
| 270 | PAIR(ResolutionSetup, Res2X, tr("2X (1440p/2160p)")), | ||
| 271 | PAIR(ResolutionSetup, Res3X, tr("3X (2160p/3240p)")), | ||
| 272 | PAIR(ResolutionSetup, Res4X, tr("4X (2880p/4320p)")), | ||
| 273 | PAIR(ResolutionSetup, Res5X, tr("5X (3600p/5400p)")), | ||
| 274 | PAIR(ResolutionSetup, Res6X, tr("6X (4320p/6480p)")), | ||
| 275 | PAIR(ResolutionSetup, Res7X, tr("7X (5040p/7560p)")), | ||
| 276 | PAIR(ResolutionSetup, Res8X, tr("8X (5760p/8640p)")), | ||
| 277 | }}); | ||
| 252 | translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(), | 278 | translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(), |
| 253 | { | 279 | { |
| 254 | PAIR(ScalingFilter, NearestNeighbor, "Nearest Neighbor"), | 280 | PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), |
| 255 | PAIR(ScalingFilter, Bilinear, "Bilinear"), | 281 | PAIR(ScalingFilter, Bilinear, tr("Bilinear")), |
| 256 | PAIR(ScalingFilter, Bicubic, "Bicubic"), | 282 | PAIR(ScalingFilter, Bicubic, tr("Bicubic")), |
| 257 | PAIR(ScalingFilter, Gaussian, "Gaussian"), | 283 | PAIR(ScalingFilter, Gaussian, tr("Gaussian")), |
| 258 | PAIR(ScalingFilter, ScaleForce, "ScaleForce"), | 284 | PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), |
| 259 | PAIR(ScalingFilter, Fsr, "AMD FidelityFX™️ Super Resolution"), | 285 | PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")), |
| 260 | }}); | 286 | }}); |
| 261 | translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(), | 287 | translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(), |
| 262 | { | 288 | { |
| 263 | PAIR(AntiAliasing, None, "None"), | 289 | PAIR(AntiAliasing, None, tr("None")), |
| 264 | PAIR(AntiAliasing, Fxaa, "FXAA"), | 290 | PAIR(AntiAliasing, Fxaa, tr("FXAA")), |
| 265 | PAIR(AntiAliasing, Smaa, "SMAA"), | 291 | PAIR(AntiAliasing, Smaa, tr("SMAA")), |
| 266 | }}); | 292 | }}); |
| 267 | translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(), | 293 | translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(), |
| 268 | { | 294 | { |
| 269 | PAIR(AspectRatio, R16_9, "Default (16:9)"), | 295 | PAIR(AspectRatio, R16_9, tr("Default (16:9)")), |
| 270 | PAIR(AspectRatio, R4_3, "Force 4:3"), | 296 | PAIR(AspectRatio, R4_3, tr("Force 4:3")), |
| 271 | PAIR(AspectRatio, R21_9, "Force 21:9"), | 297 | PAIR(AspectRatio, R21_9, tr("Force 21:9")), |
| 272 | PAIR(AspectRatio, R16_10, "Force 16:10"), | 298 | PAIR(AspectRatio, R16_10, tr("Force 16:10")), |
| 273 | PAIR(AspectRatio, Stretch, "Stretch to Window"), | 299 | PAIR(AspectRatio, Stretch, tr("Stretch to Window")), |
| 274 | }}); | 300 | }}); |
| 275 | translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(), | 301 | translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(), |
| 276 | { | 302 | { |
| 277 | PAIR(AnisotropyMode, Automatic, "Automatic"), | 303 | PAIR(AnisotropyMode, Automatic, tr("Automatic")), |
| 278 | PAIR(AnisotropyMode, Default, "Default"), | 304 | PAIR(AnisotropyMode, Default, tr("Default")), |
| 279 | PAIR(AnisotropyMode, X2, "2x"), | 305 | PAIR(AnisotropyMode, X2, tr("2x")), |
| 280 | PAIR(AnisotropyMode, X4, "4x"), | 306 | PAIR(AnisotropyMode, X4, tr("4x")), |
| 281 | PAIR(AnisotropyMode, X8, "8x"), | 307 | PAIR(AnisotropyMode, X8, tr("8x")), |
| 282 | PAIR(AnisotropyMode, X16, "16x"), | 308 | PAIR(AnisotropyMode, X16, tr("16x")), |
| 283 | }}); | 309 | }}); |
| 284 | translations->insert( | 310 | translations->insert( |
| 285 | {Settings::EnumMetadata<Settings::Language>::Index(), | 311 | {Settings::EnumMetadata<Settings::Language>::Index(), |
| 286 | { | 312 | { |
| 287 | PAIR(Language, Japanese, "Japanese (日本語)"), | 313 | PAIR(Language, Japanese, tr("Japanese (日本語)")), |
| 288 | PAIR(Language, EnglishAmerican, "American English"), | 314 | PAIR(Language, EnglishAmerican, tr("American English")), |
| 289 | PAIR(Language, French, "French (français)"), | 315 | PAIR(Language, French, tr("French (français)")), |
| 290 | PAIR(Language, German, "German (Deutsch)"), | 316 | PAIR(Language, German, tr("German (Deutsch)")), |
| 291 | PAIR(Language, Italian, "Italian (italiano)"), | 317 | PAIR(Language, Italian, tr("Italian (italiano)")), |
| 292 | PAIR(Language, Spanish, "Spanish (español)"), | 318 | PAIR(Language, Spanish, tr("Spanish (español)")), |
| 293 | PAIR(Language, Chinese, "Chinese"), | 319 | PAIR(Language, Chinese, tr("Chinese")), |
| 294 | PAIR(Language, Korean, "Korean (한국어)"), | 320 | PAIR(Language, Korean, tr("Korean (한국어)")), |
| 295 | PAIR(Language, Dutch, "Dutch (Nederlands)"), | 321 | PAIR(Language, Dutch, tr("Dutch (Nederlands)")), |
| 296 | PAIR(Language, Portuguese, "Portuguese (português)"), | 322 | PAIR(Language, Portuguese, tr("Portuguese (português)")), |
| 297 | PAIR(Language, Russian, "Russian (Русский)"), | 323 | PAIR(Language, Russian, tr("Russian (Русский)")), |
| 298 | PAIR(Language, Taiwanese, "Taiwanese"), | 324 | PAIR(Language, Taiwanese, tr("Taiwanese")), |
| 299 | PAIR(Language, EnglishBritish, "British English"), | 325 | PAIR(Language, EnglishBritish, tr("British English")), |
| 300 | PAIR(Language, FrenchCanadian, "Canadian French"), | 326 | PAIR(Language, FrenchCanadian, tr("Canadian French")), |
| 301 | PAIR(Language, SpanishLatin, "Latin American Spanish"), | 327 | PAIR(Language, SpanishLatin, tr("Latin American Spanish")), |
| 302 | PAIR(Language, ChineseSimplified, "Simplified Chinese"), | 328 | PAIR(Language, ChineseSimplified, tr("Simplified Chinese")), |
| 303 | PAIR(Language, ChineseTraditional, "Traditional Chinese (正體中文)"), | 329 | PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")), |
| 304 | PAIR(Language, PortugueseBrazilian, "Brazilian Portuguese (português do Brasil)"), | 330 | PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")), |
| 305 | }}); | 331 | }}); |
| 306 | translations->insert({Settings::EnumMetadata<Settings::Region>::Index(), | 332 | translations->insert({Settings::EnumMetadata<Settings::Region>::Index(), |
| 307 | { | 333 | { |
| 308 | PAIR(Region, Japan, "Japan"), | 334 | PAIR(Region, Japan, tr("Japan")), |
| 309 | PAIR(Region, Usa, "USA"), | 335 | PAIR(Region, Usa, tr("USA")), |
| 310 | PAIR(Region, Europe, "Europe"), | 336 | PAIR(Region, Europe, tr("Europe")), |
| 311 | PAIR(Region, Australia, "Australia"), | 337 | PAIR(Region, Australia, tr("Australia")), |
| 312 | PAIR(Region, China, "China"), | 338 | PAIR(Region, China, tr("China")), |
| 313 | PAIR(Region, Korea, "Korea"), | 339 | PAIR(Region, Korea, tr("Korea")), |
| 314 | PAIR(Region, Taiwan, "Taiwan"), | 340 | PAIR(Region, Taiwan, tr("Taiwan")), |
| 315 | }}); | 341 | }}); |
| 316 | translations->insert( | 342 | translations->insert( |
| 317 | {Settings::EnumMetadata<Settings::TimeZone>::Index(), | 343 | {Settings::EnumMetadata<Settings::TimeZone>::Index(), |
| @@ -323,72 +349,74 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) { | |||
| 323 | {static_cast<u32>(Settings::TimeZone::Default), | 349 | {static_cast<u32>(Settings::TimeZone::Default), |
| 324 | tr("Default (%1)", "Default time zone") | 350 | tr("Default (%1)", "Default time zone") |
| 325 | .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, | 351 | .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, |
| 326 | PAIR(TimeZone, Cet, "CET"), | 352 | PAIR(TimeZone, Cet, tr("CET")), |
| 327 | PAIR(TimeZone, Cst6Cdt, "CST6CDT"), | 353 | PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")), |
| 328 | PAIR(TimeZone, Cuba, "Cuba"), | 354 | PAIR(TimeZone, Cuba, tr("Cuba")), |
| 329 | PAIR(TimeZone, Eet, "EET"), | 355 | PAIR(TimeZone, Eet, tr("EET")), |
| 330 | PAIR(TimeZone, Egypt, "Egypt"), | 356 | PAIR(TimeZone, Egypt, tr("Egypt")), |
| 331 | PAIR(TimeZone, Eire, "Eire"), | 357 | PAIR(TimeZone, Eire, tr("Eire")), |
| 332 | PAIR(TimeZone, Est, "EST"), | 358 | PAIR(TimeZone, Est, tr("EST")), |
| 333 | PAIR(TimeZone, Est5Edt, "EST5EDT"), | 359 | PAIR(TimeZone, Est5Edt, tr("EST5EDT")), |
| 334 | PAIR(TimeZone, Gb, "GB"), | 360 | PAIR(TimeZone, Gb, tr("GB")), |
| 335 | PAIR(TimeZone, GbEire, "GB-Eire"), | 361 | PAIR(TimeZone, GbEire, tr("GB-Eire")), |
| 336 | PAIR(TimeZone, Gmt, "GMT"), | 362 | PAIR(TimeZone, Gmt, tr("GMT")), |
| 337 | PAIR(TimeZone, GmtPlusZero, "GMT+0"), | 363 | PAIR(TimeZone, GmtPlusZero, tr("GMT+0")), |
| 338 | PAIR(TimeZone, GmtMinusZero, "GMT-0"), | 364 | PAIR(TimeZone, GmtMinusZero, tr("GMT-0")), |
| 339 | PAIR(TimeZone, GmtZero, "GMT0"), | 365 | PAIR(TimeZone, GmtZero, tr("GMT0")), |
| 340 | PAIR(TimeZone, Greenwich, "Greenwich"), | 366 | PAIR(TimeZone, Greenwich, tr("Greenwich")), |
| 341 | PAIR(TimeZone, Hongkong, "Hongkong"), | 367 | PAIR(TimeZone, Hongkong, tr("Hongkong")), |
| 342 | PAIR(TimeZone, Hst, "HST"), | 368 | PAIR(TimeZone, Hst, tr("HST")), |
| 343 | PAIR(TimeZone, Iceland, "Iceland"), | 369 | PAIR(TimeZone, Iceland, tr("Iceland")), |
| 344 | PAIR(TimeZone, Iran, "Iran"), | 370 | PAIR(TimeZone, Iran, tr("Iran")), |
| 345 | PAIR(TimeZone, Israel, "Israel"), | 371 | PAIR(TimeZone, Israel, tr("Israel")), |
| 346 | PAIR(TimeZone, Jamaica, "Jamaica"), | 372 | PAIR(TimeZone, Jamaica, tr("Jamaica")), |
| 347 | PAIR(TimeZone, Japan, "Japan"), | 373 | PAIR(TimeZone, Japan, tr("Japan")), |
| 348 | PAIR(TimeZone, Kwajalein, "Kwajalein"), | 374 | PAIR(TimeZone, Kwajalein, tr("Kwajalein")), |
| 349 | PAIR(TimeZone, Libya, "Libya"), | 375 | PAIR(TimeZone, Libya, tr("Libya")), |
| 350 | PAIR(TimeZone, Met, "MET"), | 376 | PAIR(TimeZone, Met, tr("MET")), |
| 351 | PAIR(TimeZone, Mst, "MST"), | 377 | PAIR(TimeZone, Mst, tr("MST")), |
| 352 | PAIR(TimeZone, Mst7Mdt, "MST7MDT"), | 378 | PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")), |
| 353 | PAIR(TimeZone, Navajo, "Navajo"), | 379 | PAIR(TimeZone, Navajo, tr("Navajo")), |
| 354 | PAIR(TimeZone, Nz, "NZ"), | 380 | PAIR(TimeZone, Nz, tr("NZ")), |
| 355 | PAIR(TimeZone, NzChat, "NZ-CHAT"), | 381 | PAIR(TimeZone, NzChat, tr("NZ-CHAT")), |
| 356 | PAIR(TimeZone, Poland, "Poland"), | 382 | PAIR(TimeZone, Poland, tr("Poland")), |
| 357 | PAIR(TimeZone, Portugal, "Portugal"), | 383 | PAIR(TimeZone, Portugal, tr("Portugal")), |
| 358 | PAIR(TimeZone, Prc, "PRC"), | 384 | PAIR(TimeZone, Prc, tr("PRC")), |
| 359 | PAIR(TimeZone, Pst8Pdt, "PST8PDT"), | 385 | PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")), |
| 360 | PAIR(TimeZone, Roc, "ROC"), | 386 | PAIR(TimeZone, Roc, tr("ROC")), |
| 361 | PAIR(TimeZone, Rok, "ROK"), | 387 | PAIR(TimeZone, Rok, tr("ROK")), |
| 362 | PAIR(TimeZone, Singapore, "Singapore"), | 388 | PAIR(TimeZone, Singapore, tr("Singapore")), |
| 363 | PAIR(TimeZone, Turkey, "Turkey"), | 389 | PAIR(TimeZone, Turkey, tr("Turkey")), |
| 364 | PAIR(TimeZone, Uct, "UCT"), | 390 | PAIR(TimeZone, Uct, tr("UCT")), |
| 365 | PAIR(TimeZone, Universal, "Universal"), | 391 | PAIR(TimeZone, Universal, tr("Universal")), |
| 366 | PAIR(TimeZone, Utc, "UTC"), | 392 | PAIR(TimeZone, Utc, tr("UTC")), |
| 367 | PAIR(TimeZone, WSu, "W-SU"), | 393 | PAIR(TimeZone, WSu, tr("W-SU")), |
| 368 | PAIR(TimeZone, Wet, "WET"), | 394 | PAIR(TimeZone, Wet, tr("WET")), |
| 369 | PAIR(TimeZone, Zulu, "Zulu"), | 395 | PAIR(TimeZone, Zulu, tr("Zulu")), |
| 370 | }}); | 396 | }}); |
| 371 | translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(), | 397 | translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(), |
| 372 | { | 398 | { |
| 373 | PAIR(AudioMode, Mono, "Mono"), | 399 | PAIR(AudioMode, Mono, tr("Mono")), |
| 374 | PAIR(AudioMode, Stereo, "Stereo"), | 400 | PAIR(AudioMode, Stereo, tr("Stereo")), |
| 375 | PAIR(AudioMode, Surround, "Surround"), | 401 | PAIR(AudioMode, Surround, tr("Surround")), |
| 376 | }}); | 402 | }}); |
| 377 | translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(), | 403 | translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(), |
| 378 | { | 404 | { |
| 379 | PAIR(MemoryLayout, Memory_4Gb, "4GB DRAM (Default)"), | 405 | PAIR(MemoryLayout, Memory_4Gb, tr("4GB DRAM (Default)")), |
| 380 | PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), | 406 | PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM (Unsafe)")), |
| 381 | PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), | 407 | PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM (Unsafe)")), |
| 408 | }}); | ||
| 409 | translations->insert({Settings::EnumMetadata<Settings::ConsoleMode>::Index(), | ||
| 410 | { | ||
| 411 | PAIR(ConsoleMode, Docked, tr("Docked")), | ||
| 412 | PAIR(ConsoleMode, Handheld, tr("Handheld")), | ||
| 382 | }}); | 413 | }}); |
| 383 | translations->insert( | ||
| 384 | {Settings::EnumMetadata<Settings::ConsoleMode>::Index(), | ||
| 385 | {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}}); | ||
| 386 | translations->insert( | 414 | translations->insert( |
| 387 | {Settings::EnumMetadata<Settings::ConfirmStop>::Index(), | 415 | {Settings::EnumMetadata<Settings::ConfirmStop>::Index(), |
| 388 | { | 416 | { |
| 389 | PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"), | 417 | PAIR(ConfirmStop, Ask_Always, tr("Always ask (Default)")), |
| 390 | PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"), | 418 | PAIR(ConfirmStop, Ask_Based_On_Game, tr("Only if game specifies not to stop")), |
| 391 | PAIR(ConfirmStop, Ask_Never, "Never ask"), | 419 | PAIR(ConfirmStop, Ask_Never, tr("Never ask")), |
| 392 | }}); | 420 | }}); |
| 393 | 421 | ||
| 394 | #undef PAIR | 422 | #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 f294dc23d..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( |
| @@ -727,7 +727,8 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { | |||
| 727 | }); | 727 | }); |
| 728 | 728 | ||
| 729 | connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { | 729 | connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { |
| 730 | emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path); | 730 | emit OpenDirectory( |
| 731 | QString::fromStdString(UISettings::values.game_dirs[game_dir_index].path)); | ||
| 731 | }); | 732 | }); |
| 732 | } | 733 | } |
| 733 | 734 | ||
| @@ -869,7 +870,7 @@ const QStringList GameList::supported_file_extensions = { | |||
| 869 | QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; | 870 | QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; |
| 870 | 871 | ||
| 871 | void GameList::RefreshGameDirectory() { | 872 | void GameList::RefreshGameDirectory() { |
| 872 | if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { | 873 | if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { |
| 873 | 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."); |
| 874 | PopulateAsync(UISettings::values.game_dirs); | 875 | PopulateAsync(UISettings::values.game_dirs); |
| 875 | } | 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 f6b548fd3..b056c3717 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #ifdef __unix__ | 17 | #ifdef __unix__ |
| 18 | #include <csignal> | 18 | #include <csignal> |
| 19 | #include <sys/socket.h> | 19 | #include <sys/socket.h> |
| 20 | #include "common/linux/gamemode.h" | ||
| 20 | #endif | 21 | #endif |
| 21 | 22 | ||
| 22 | #include <boost/container/flat_set.hpp> | 23 | #include <boost/container/flat_set.hpp> |
| @@ -47,6 +48,7 @@ | |||
| 47 | #include "core/hle/service/am/applet_ae.h" | 48 | #include "core/hle/service/am/applet_ae.h" |
| 48 | #include "core/hle/service/am/applet_oe.h" | 49 | #include "core/hle/service/am/applet_oe.h" |
| 49 | #include "core/hle/service/am/applets/applets.h" | 50 | #include "core/hle/service/am/applets/applets.h" |
| 51 | #include "core/hle/service/set/set_sys.h" | ||
| 50 | #include "yuzu/multiplayer/state.h" | 52 | #include "yuzu/multiplayer/state.h" |
| 51 | #include "yuzu/util/controller_navigation.h" | 53 | #include "yuzu/util/controller_navigation.h" |
| 52 | 54 | ||
| @@ -128,6 +130,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 128 | #include "core/loader/loader.h" | 130 | #include "core/loader/loader.h" |
| 129 | #include "core/perf_stats.h" | 131 | #include "core/perf_stats.h" |
| 130 | #include "core/telemetry_session.h" | 132 | #include "core/telemetry_session.h" |
| 133 | #include "frontend_common/config.h" | ||
| 131 | #include "input_common/drivers/tas_input.h" | 134 | #include "input_common/drivers/tas_input.h" |
| 132 | #include "input_common/drivers/virtual_amiibo.h" | 135 | #include "input_common/drivers/virtual_amiibo.h" |
| 133 | #include "input_common/main.h" | 136 | #include "input_common/main.h" |
| @@ -140,9 +143,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 140 | #include "yuzu/bootmanager.h" | 143 | #include "yuzu/bootmanager.h" |
| 141 | #include "yuzu/compatdb.h" | 144 | #include "yuzu/compatdb.h" |
| 142 | #include "yuzu/compatibility_list.h" | 145 | #include "yuzu/compatibility_list.h" |
| 143 | #include "yuzu/configuration/config.h" | ||
| 144 | #include "yuzu/configuration/configure_dialog.h" | 146 | #include "yuzu/configuration/configure_dialog.h" |
| 145 | #include "yuzu/configuration/configure_input_per_game.h" | 147 | #include "yuzu/configuration/configure_input_per_game.h" |
| 148 | #include "yuzu/configuration/qt_config.h" | ||
| 146 | #include "yuzu/debugger/console.h" | 149 | #include "yuzu/debugger/console.h" |
| 147 | #include "yuzu/debugger/controller.h" | 150 | #include "yuzu/debugger/controller.h" |
| 148 | #include "yuzu/debugger/profiler.h" | 151 | #include "yuzu/debugger/profiler.h" |
| @@ -185,7 +188,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; | |||
| 185 | #endif | 188 | #endif |
| 186 | 189 | ||
| 187 | constexpr int default_mouse_hide_timeout = 2500; | 190 | constexpr int default_mouse_hide_timeout = 2500; |
| 188 | constexpr int default_mouse_center_timeout = 10; | ||
| 189 | constexpr int default_input_update_timeout = 1; | 191 | constexpr int default_input_update_timeout = 1; |
| 190 | 192 | ||
| 191 | constexpr size_t CopyBufferSize = 1_MiB; | 193 | constexpr size_t CopyBufferSize = 1_MiB; |
| @@ -311,13 +313,14 @@ bool GMainWindow::CheckDarkMode() { | |||
| 311 | #endif // __unix__ | 313 | #endif // __unix__ |
| 312 | } | 314 | } |
| 313 | 315 | ||
| 314 | GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan) | 316 | GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan) |
| 315 | : 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>()}, |
| 316 | input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, | 318 | input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, |
| 317 | vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, | 319 | vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, |
| 318 | provider{std::make_unique<FileSys::ManualContentProvider>()} { | 320 | provider{std::make_unique<FileSys::ManualContentProvider>()} { |
| 319 | #ifdef __unix__ | 321 | #ifdef __unix__ |
| 320 | SetupSigInterrupts(); | 322 | SetupSigInterrupts(); |
| 323 | SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); | ||
| 321 | #endif | 324 | #endif |
| 322 | system->Initialize(); | 325 | system->Initialize(); |
| 323 | 326 | ||
| @@ -435,9 +438,6 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan | |||
| 435 | connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); | 438 | connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); |
| 436 | connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor); | 439 | connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor); |
| 437 | 440 | ||
| 438 | mouse_center_timer.setInterval(default_mouse_center_timeout); | ||
| 439 | connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor); | ||
| 440 | |||
| 441 | update_input_timer.setInterval(default_input_update_timeout); | 441 | update_input_timer.setInterval(default_input_update_timeout); |
| 442 | connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers); | 442 | connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers); |
| 443 | update_input_timer.start(); | 443 | update_input_timer.start(); |
| @@ -676,7 +676,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers( | |||
| 676 | // Don't forget to apply settings. | 676 | // Don't forget to apply settings. |
| 677 | system->HIDCore().DisableAllControllerConfiguration(); | 677 | system->HIDCore().DisableAllControllerConfiguration(); |
| 678 | system->ApplySettings(); | 678 | system->ApplySettings(); |
| 679 | config->Save(); | 679 | config->SaveAllValues(); |
| 680 | 680 | ||
| 681 | UpdateStatusButtons(); | 681 | UpdateStatusButtons(); |
| 682 | 682 | ||
| @@ -1047,7 +1047,12 @@ void GMainWindow::InitializeWidgets() { | |||
| 1047 | statusBar()->addPermanentWidget(label); | 1047 | statusBar()->addPermanentWidget(label); |
| 1048 | } | 1048 | } |
| 1049 | 1049 | ||
| 1050 | // TODO (flTobi): Add the widget when multiplayer is fully implemented | 1050 | firmware_label = new QLabel(); |
| 1051 | firmware_label->setObjectName(QStringLiteral("FirmwareLabel")); | ||
| 1052 | firmware_label->setVisible(false); | ||
| 1053 | firmware_label->setFocusPolicy(Qt::NoFocus); | ||
| 1054 | statusBar()->addPermanentWidget(firmware_label); | ||
| 1055 | |||
| 1051 | statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); | 1056 | statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); |
| 1052 | statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); | 1057 | statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); |
| 1053 | 1058 | ||
| @@ -1129,7 +1134,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 1129 | connect(aa_status_button, &QPushButton::customContextMenuRequested, | 1134 | connect(aa_status_button, &QPushButton::customContextMenuRequested, |
| 1130 | [this](const QPoint& menu_location) { | 1135 | [this](const QPoint& menu_location) { |
| 1131 | QMenu context_menu; | 1136 | QMenu context_menu; |
| 1132 | for (auto const& aa_text_pair : Config::anti_aliasing_texts_map) { | 1137 | for (auto const& aa_text_pair : ConfigurationShared::anti_aliasing_texts_map) { |
| 1133 | context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] { | 1138 | context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] { |
| 1134 | Settings::values.anti_aliasing.SetValue(aa_text_pair.first); | 1139 | Settings::values.anti_aliasing.SetValue(aa_text_pair.first); |
| 1135 | UpdateAAText(); | 1140 | UpdateAAText(); |
| @@ -1153,7 +1158,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 1153 | connect(filter_status_button, &QPushButton::customContextMenuRequested, | 1158 | connect(filter_status_button, &QPushButton::customContextMenuRequested, |
| 1154 | [this](const QPoint& menu_location) { | 1159 | [this](const QPoint& menu_location) { |
| 1155 | QMenu context_menu; | 1160 | QMenu context_menu; |
| 1156 | for (auto const& filter_text_pair : Config::scaling_filter_texts_map) { | 1161 | for (auto const& filter_text_pair : ConfigurationShared::scaling_filter_texts_map) { |
| 1157 | context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] { | 1162 | context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] { |
| 1158 | Settings::values.scaling_filter.SetValue(filter_text_pair.first); | 1163 | Settings::values.scaling_filter.SetValue(filter_text_pair.first); |
| 1159 | UpdateFilterText(); | 1164 | UpdateFilterText(); |
| @@ -1176,7 +1181,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 1176 | [this](const QPoint& menu_location) { | 1181 | [this](const QPoint& menu_location) { |
| 1177 | QMenu context_menu; | 1182 | QMenu context_menu; |
| 1178 | 1183 | ||
| 1179 | for (auto const& pair : Config::use_docked_mode_texts_map) { | 1184 | for (auto const& pair : ConfigurationShared::use_docked_mode_texts_map) { |
| 1180 | context_menu.addAction(pair.second, [this, &pair] { | 1185 | context_menu.addAction(pair.second, [this, &pair] { |
| 1181 | if (pair.first != Settings::values.use_docked_mode.GetValue()) { | 1186 | if (pair.first != Settings::values.use_docked_mode.GetValue()) { |
| 1182 | OnToggleDockedMode(); | 1187 | OnToggleDockedMode(); |
| @@ -1200,7 +1205,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 1200 | [this](const QPoint& menu_location) { | 1205 | [this](const QPoint& menu_location) { |
| 1201 | QMenu context_menu; | 1206 | QMenu context_menu; |
| 1202 | 1207 | ||
| 1203 | for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) { | 1208 | for (auto const& gpu_accuracy_pair : ConfigurationShared::gpu_accuracy_texts_map) { |
| 1204 | if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) { | 1209 | if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) { |
| 1205 | continue; | 1210 | continue; |
| 1206 | } | 1211 | } |
| @@ -1229,7 +1234,8 @@ void GMainWindow::InitializeWidgets() { | |||
| 1229 | [this](const QPoint& menu_location) { | 1234 | [this](const QPoint& menu_location) { |
| 1230 | QMenu context_menu; | 1235 | QMenu context_menu; |
| 1231 | 1236 | ||
| 1232 | for (auto const& renderer_backend_pair : Config::renderer_backend_texts_map) { | 1237 | for (auto const& renderer_backend_pair : |
| 1238 | ConfigurationShared::renderer_backend_texts_map) { | ||
| 1233 | if (renderer_backend_pair.first == Settings::RendererBackend::Null) { | 1239 | if (renderer_backend_pair.first == Settings::RendererBackend::Null) { |
| 1234 | continue; | 1240 | continue; |
| 1235 | } | 1241 | } |
| @@ -1294,16 +1300,17 @@ void GMainWindow::InitializeRecentFileMenuActions() { | |||
| 1294 | 1300 | ||
| 1295 | void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name, | 1301 | void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name, |
| 1296 | const bool tas_allowed) { | 1302 | const bool tas_allowed) { |
| 1297 | static const QString main_window = QStringLiteral("Main Window"); | 1303 | static const auto main_window = std::string("Main Window"); |
| 1298 | action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); | 1304 | action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name.toStdString())); |
| 1299 | action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); | 1305 | action->setShortcutContext( |
| 1306 | hotkey_registry.GetShortcutContext(main_window, action_name.toStdString())); | ||
| 1300 | action->setAutoRepeat(false); | 1307 | action->setAutoRepeat(false); |
| 1301 | 1308 | ||
| 1302 | this->addAction(action); | 1309 | this->addAction(action); |
| 1303 | 1310 | ||
| 1304 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | 1311 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 1305 | const auto* controller_hotkey = | 1312 | const auto* controller_hotkey = |
| 1306 | hotkey_registry.GetControllerHotkey(main_window, action_name, controller); | 1313 | hotkey_registry.GetControllerHotkey(main_window, action_name.toStdString(), controller); |
| 1307 | connect( | 1314 | connect( |
| 1308 | controller_hotkey, &ControllerShortcut::Activated, this, | 1315 | controller_hotkey, &ControllerShortcut::Activated, this, |
| 1309 | [action, tas_allowed, this] { | 1316 | [action, tas_allowed, this] { |
| @@ -1335,10 +1342,11 @@ void GMainWindow::InitializeHotkeys() { | |||
| 1335 | 1342 | ||
| 1336 | static const QString main_window = QStringLiteral("Main Window"); | 1343 | static const QString main_window = QStringLiteral("Main Window"); |
| 1337 | const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { | 1344 | const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { |
| 1338 | const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this); | 1345 | const auto* hotkey = |
| 1346 | hotkey_registry.GetHotkey(main_window.toStdString(), action_name.toStdString(), this); | ||
| 1339 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | 1347 | auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); |
| 1340 | const auto* controller_hotkey = | 1348 | const auto* controller_hotkey = hotkey_registry.GetControllerHotkey( |
| 1341 | hotkey_registry.GetControllerHotkey(main_window, action_name, controller); | 1349 | main_window.toStdString(), action_name.toStdString(), controller); |
| 1342 | connect(hotkey, &QShortcut::activated, this, function); | 1350 | connect(hotkey, &QShortcut::activated, this, function); |
| 1343 | connect(controller_hotkey, &ControllerShortcut::Activated, this, function, | 1351 | connect(controller_hotkey, &ControllerShortcut::Activated, this, function, |
| 1344 | Qt::QueuedConnection); | 1352 | Qt::QueuedConnection); |
| @@ -1366,14 +1374,6 @@ void GMainWindow::InitializeHotkeys() { | |||
| 1366 | } | 1374 | } |
| 1367 | }); | 1375 | }); |
| 1368 | connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] { | 1376 | connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] { |
| 1369 | if (Settings::values.mouse_enabled) { | ||
| 1370 | Settings::values.mouse_panning = false; | ||
| 1371 | QMessageBox::warning( | ||
| 1372 | this, tr("Emulated mouse is enabled"), | ||
| 1373 | tr("Real mouse input and mouse panning are incompatible. Please disable the " | ||
| 1374 | "emulated mouse in input advanced settings to allow mouse panning.")); | ||
| 1375 | return; | ||
| 1376 | } | ||
| 1377 | Settings::values.mouse_panning = !Settings::values.mouse_panning; | 1377 | Settings::values.mouse_panning = !Settings::values.mouse_panning; |
| 1378 | if (Settings::values.mouse_panning) { | 1378 | if (Settings::values.mouse_panning) { |
| 1379 | render_window->installEventFilter(render_window); | 1379 | render_window->installEventFilter(render_window); |
| @@ -1575,6 +1575,7 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 1575 | connect_menu(ui->action_Load_Cabinet_Formatter, | 1575 | connect_menu(ui->action_Load_Cabinet_Formatter, |
| 1576 | [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); }); | 1576 | [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); }); |
| 1577 | connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit); | 1577 | connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit); |
| 1578 | connect_menu(ui->action_Open_Controller_Menu, &GMainWindow::OnOpenControllerMenu); | ||
| 1578 | connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); | 1579 | connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); |
| 1579 | 1580 | ||
| 1580 | // TAS | 1581 | // TAS |
| @@ -1602,14 +1603,13 @@ void GMainWindow::UpdateMenuState() { | |||
| 1602 | ui->action_Pause, | 1603 | ui->action_Pause, |
| 1603 | }; | 1604 | }; |
| 1604 | 1605 | ||
| 1605 | const std::array applet_actions{ | 1606 | const std::array applet_actions{ui->action_Load_Album, |
| 1606 | ui->action_Load_Album, | 1607 | ui->action_Load_Cabinet_Nickname_Owner, |
| 1607 | ui->action_Load_Cabinet_Nickname_Owner, | 1608 | ui->action_Load_Cabinet_Eraser, |
| 1608 | ui->action_Load_Cabinet_Eraser, | 1609 | ui->action_Load_Cabinet_Restorer, |
| 1609 | ui->action_Load_Cabinet_Restorer, | 1610 | ui->action_Load_Cabinet_Formatter, |
| 1610 | ui->action_Load_Cabinet_Formatter, | 1611 | ui->action_Load_Mii_Edit, |
| 1611 | ui->action_Load_Mii_Edit, | 1612 | ui->action_Open_Controller_Menu}; |
| 1612 | }; | ||
| 1613 | 1613 | ||
| 1614 | for (QAction* action : running_actions) { | 1614 | for (QAction* action : running_actions) { |
| 1615 | action->setEnabled(emulation_running); | 1615 | action->setEnabled(emulation_running); |
| @@ -1918,7 +1918,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t | |||
| 1918 | // Save configurations | 1918 | // Save configurations |
| 1919 | UpdateUISettings(); | 1919 | UpdateUISettings(); |
| 1920 | game_list->SaveInterfaceLayout(); | 1920 | game_list->SaveInterfaceLayout(); |
| 1921 | config->Save(); | 1921 | config->SaveAllValues(); |
| 1922 | 1922 | ||
| 1923 | u64 title_id{0}; | 1923 | u64 title_id{0}; |
| 1924 | 1924 | ||
| @@ -1936,7 +1936,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t | |||
| 1936 | const auto config_file_name = title_id == 0 | 1936 | const auto config_file_name = title_id == 0 |
| 1937 | ? Common::FS::PathToUTF8String(file_path.filename()) | 1937 | ? Common::FS::PathToUTF8String(file_path.filename()) |
| 1938 | : fmt::format("{:016X}", title_id); | 1938 | : fmt::format("{:016X}", title_id); |
| 1939 | Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); | 1939 | QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig); |
| 1940 | system->HIDCore().ReloadInputDevices(); | 1940 | system->HIDCore().ReloadInputDevices(); |
| 1941 | system->ApplySettings(); | 1941 | system->ApplySettings(); |
| 1942 | } | 1942 | } |
| @@ -2122,6 +2122,10 @@ void GMainWindow::OnEmulationStopped() { | |||
| 2122 | 2122 | ||
| 2123 | discord_rpc->Update(); | 2123 | discord_rpc->Update(); |
| 2124 | 2124 | ||
| 2125 | #ifdef __unix__ | ||
| 2126 | Common::Linux::StopGamemode(); | ||
| 2127 | #endif | ||
| 2128 | |||
| 2125 | // The emulation is stopped, so closing the window or not does not matter anymore | 2129 | // The emulation is stopped, so closing the window or not does not matter anymore |
| 2126 | disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | 2130 | disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |
| 2127 | 2131 | ||
| @@ -2161,6 +2165,10 @@ void GMainWindow::OnEmulationStopped() { | |||
| 2161 | emu_frametime_label->setVisible(false); | 2165 | emu_frametime_label->setVisible(false); |
| 2162 | renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); | 2166 | renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); |
| 2163 | 2167 | ||
| 2168 | if (!firmware_label->text().isEmpty()) { | ||
| 2169 | firmware_label->setVisible(true); | ||
| 2170 | } | ||
| 2171 | |||
| 2164 | current_game_path.clear(); | 2172 | current_game_path.clear(); |
| 2165 | 2173 | ||
| 2166 | // When closing the game, destroy the GLWindow to clear the context after the game is closed | 2174 | // When closing the game, destroy the GLWindow to clear the context after the game is closed |
| @@ -2705,11 +2713,6 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 2705 | } | 2713 | } |
| 2706 | 2714 | ||
| 2707 | const auto base_romfs = base_nca->GetRomFS(); | 2715 | const auto base_romfs = base_nca->GetRomFS(); |
| 2708 | if (!base_romfs) { | ||
| 2709 | failed(); | ||
| 2710 | return; | ||
| 2711 | } | ||
| 2712 | |||
| 2713 | const auto dump_dir = | 2716 | const auto dump_dir = |
| 2714 | target == DumpRomFSTarget::Normal | 2717 | target == DumpRomFSTarget::Normal |
| 2715 | ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) | 2718 | ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) |
| @@ -3135,7 +3138,7 @@ void GMainWindow::OnGameListAddDirectory() { | |||
| 3135 | return; | 3138 | return; |
| 3136 | } | 3139 | } |
| 3137 | 3140 | ||
| 3138 | UISettings::GameDir game_dir{dir_path, false, true}; | 3141 | UISettings::GameDir game_dir{dir_path.toStdString(), false, true}; |
| 3139 | if (!UISettings::values.game_dirs.contains(game_dir)) { | 3142 | if (!UISettings::values.game_dirs.contains(game_dir)) { |
| 3140 | UISettings::values.game_dirs.append(game_dir); | 3143 | UISettings::values.game_dirs.append(game_dir); |
| 3141 | game_list->PopulateAsync(UISettings::values.game_dirs); | 3144 | game_list->PopulateAsync(UISettings::values.game_dirs); |
| @@ -3181,14 +3184,14 @@ void GMainWindow::OnMenuLoadFile() { | |||
| 3181 | "%1 is an identifier for the Switch executable file extensions.") | 3184 | "%1 is an identifier for the Switch executable file extensions.") |
| 3182 | .arg(extensions); | 3185 | .arg(extensions); |
| 3183 | const QString filename = QFileDialog::getOpenFileName( | 3186 | const QString filename = QFileDialog::getOpenFileName( |
| 3184 | this, tr("Load File"), UISettings::values.roms_path, file_filter); | 3187 | this, tr("Load File"), QString::fromStdString(UISettings::values.roms_path), file_filter); |
| 3185 | is_load_file_select_active = false; | 3188 | is_load_file_select_active = false; |
| 3186 | 3189 | ||
| 3187 | if (filename.isEmpty()) { | 3190 | if (filename.isEmpty()) { |
| 3188 | return; | 3191 | return; |
| 3189 | } | 3192 | } |
| 3190 | 3193 | ||
| 3191 | UISettings::values.roms_path = QFileInfo(filename).path(); | 3194 | UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); |
| 3192 | BootGame(filename); | 3195 | BootGame(filename); |
| 3193 | } | 3196 | } |
| 3194 | 3197 | ||
| @@ -3221,7 +3224,8 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 3221 | "Image (*.xci)"); | 3224 | "Image (*.xci)"); |
| 3222 | 3225 | ||
| 3223 | QStringList filenames = QFileDialog::getOpenFileNames( | 3226 | QStringList filenames = QFileDialog::getOpenFileNames( |
| 3224 | this, tr("Install Files"), UISettings::values.roms_path, file_filter); | 3227 | this, tr("Install Files"), QString::fromStdString(UISettings::values.roms_path), |
| 3228 | file_filter); | ||
| 3225 | 3229 | ||
| 3226 | if (filenames.isEmpty()) { | 3230 | if (filenames.isEmpty()) { |
| 3227 | return; | 3231 | return; |
| @@ -3239,7 +3243,7 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 3239 | } | 3243 | } |
| 3240 | 3244 | ||
| 3241 | // Save folder location of the first selected file | 3245 | // Save folder location of the first selected file |
| 3242 | UISettings::values.roms_path = QFileInfo(filenames[0]).path(); | 3246 | UISettings::values.roms_path = QFileInfo(filenames[0]).path().toStdString(); |
| 3243 | 3247 | ||
| 3244 | int remaining = filenames.size(); | 3248 | int remaining = filenames.size(); |
| 3245 | 3249 | ||
| @@ -3499,6 +3503,10 @@ void GMainWindow::OnStartGame() { | |||
| 3499 | play_time_manager->Start(); | 3503 | play_time_manager->Start(); |
| 3500 | 3504 | ||
| 3501 | discord_rpc->Update(); | 3505 | discord_rpc->Update(); |
| 3506 | |||
| 3507 | #ifdef __unix__ | ||
| 3508 | Common::Linux::StartGamemode(); | ||
| 3509 | #endif | ||
| 3502 | } | 3510 | } |
| 3503 | 3511 | ||
| 3504 | void GMainWindow::OnRestartGame() { | 3512 | void GMainWindow::OnRestartGame() { |
| @@ -3519,6 +3527,10 @@ void GMainWindow::OnPauseGame() { | |||
| 3519 | play_time_manager->Stop(); | 3527 | play_time_manager->Stop(); |
| 3520 | UpdateMenuState(); | 3528 | UpdateMenuState(); |
| 3521 | AllowOSSleep(); | 3529 | AllowOSSleep(); |
| 3530 | |||
| 3531 | #ifdef __unix__ | ||
| 3532 | Common::Linux::StopGamemode(); | ||
| 3533 | #endif | ||
| 3522 | } | 3534 | } |
| 3523 | 3535 | ||
| 3524 | void GMainWindow::OnPauseContinueGame() { | 3536 | void GMainWindow::OnPauseContinueGame() { |
| @@ -3584,7 +3596,7 @@ void GMainWindow::OnExit() { | |||
| 3584 | 3596 | ||
| 3585 | void GMainWindow::OnSaveConfig() { | 3597 | void GMainWindow::OnSaveConfig() { |
| 3586 | system->ApplySettings(); | 3598 | system->ApplySettings(); |
| 3587 | config->Save(); | 3599 | config->SaveAllValues(); |
| 3588 | } | 3600 | } |
| 3589 | 3601 | ||
| 3590 | void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { | 3602 | void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { |
| @@ -3800,6 +3812,9 @@ void GMainWindow::OnConfigure() { | |||
| 3800 | const auto old_theme = UISettings::values.theme; | 3812 | const auto old_theme = UISettings::values.theme; |
| 3801 | const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); | 3813 | const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); |
| 3802 | const auto old_language_index = Settings::values.language_index.GetValue(); | 3814 | const auto old_language_index = Settings::values.language_index.GetValue(); |
| 3815 | #ifdef __unix__ | ||
| 3816 | const bool old_gamemode = Settings::values.enable_gamemode.GetValue(); | ||
| 3817 | #endif | ||
| 3803 | 3818 | ||
| 3804 | Settings::SetConfiguringGlobal(true); | 3819 | Settings::SetConfiguringGlobal(true); |
| 3805 | ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), | 3820 | ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), |
| @@ -3840,7 +3855,7 @@ void GMainWindow::OnConfigure() { | |||
| 3840 | 3855 | ||
| 3841 | Settings::values.disabled_addons.clear(); | 3856 | Settings::values.disabled_addons.clear(); |
| 3842 | 3857 | ||
| 3843 | config = std::make_unique<Config>(); | 3858 | config = std::make_unique<QtConfig>(); |
| 3844 | UISettings::values.reset_to_defaults = false; | 3859 | UISettings::values.reset_to_defaults = false; |
| 3845 | 3860 | ||
| 3846 | UISettings::values.game_dirs = std::move(old_game_dirs); | 3861 | UISettings::values.game_dirs = std::move(old_game_dirs); |
| @@ -3861,6 +3876,11 @@ void GMainWindow::OnConfigure() { | |||
| 3861 | if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { | 3876 | if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { |
| 3862 | SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); | 3877 | SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); |
| 3863 | } | 3878 | } |
| 3879 | #ifdef __unix__ | ||
| 3880 | if (Settings::values.enable_gamemode.GetValue() != old_gamemode) { | ||
| 3881 | SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); | ||
| 3882 | } | ||
| 3883 | #endif | ||
| 3864 | 3884 | ||
| 3865 | if (!multiplayer_state->IsHostingPublicRoom()) { | 3885 | if (!multiplayer_state->IsHostingPublicRoom()) { |
| 3866 | multiplayer_state->UpdateCredentials(); | 3886 | multiplayer_state->UpdateCredentials(); |
| @@ -3875,7 +3895,7 @@ void GMainWindow::OnConfigure() { | |||
| 3875 | 3895 | ||
| 3876 | UISettings::values.configuration_applied = false; | 3896 | UISettings::values.configuration_applied = false; |
| 3877 | 3897 | ||
| 3878 | config->Save(); | 3898 | config->SaveAllValues(); |
| 3879 | 3899 | ||
| 3880 | if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { | 3900 | if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { |
| 3881 | render_window->installEventFilter(render_window); | 3901 | render_window->installEventFilter(render_window); |
| @@ -4091,7 +4111,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file | |||
| 4091 | UISettings::values.configuration_applied = false; | 4111 | UISettings::values.configuration_applied = false; |
| 4092 | 4112 | ||
| 4093 | if (!is_powered_on) { | 4113 | if (!is_powered_on) { |
| 4094 | config->Save(); | 4114 | config->SaveAllValues(); |
| 4095 | } | 4115 | } |
| 4096 | } | 4116 | } |
| 4097 | 4117 | ||
| @@ -4324,7 +4344,7 @@ void GMainWindow::OnAlbum() { | |||
| 4324 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); | 4344 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); |
| 4325 | 4345 | ||
| 4326 | const auto filename = QString::fromStdString(album_nca->GetFullPath()); | 4346 | const auto filename = QString::fromStdString(album_nca->GetFullPath()); |
| 4327 | UISettings::values.roms_path = QFileInfo(filename).path(); | 4347 | UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); |
| 4328 | BootGame(filename, AlbumId); | 4348 | BootGame(filename, AlbumId); |
| 4329 | } | 4349 | } |
| 4330 | 4350 | ||
| @@ -4348,7 +4368,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { | |||
| 4348 | system->GetAppletManager().SetCabinetMode(mode); | 4368 | system->GetAppletManager().SetCabinetMode(mode); |
| 4349 | 4369 | ||
| 4350 | const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); | 4370 | const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); |
| 4351 | UISettings::values.roms_path = QFileInfo(filename).path(); | 4371 | UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); |
| 4352 | BootGame(filename, CabinetId); | 4372 | BootGame(filename, CabinetId); |
| 4353 | } | 4373 | } |
| 4354 | 4374 | ||
| @@ -4371,10 +4391,35 @@ void GMainWindow::OnMiiEdit() { | |||
| 4371 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); | 4391 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); |
| 4372 | 4392 | ||
| 4373 | const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); | 4393 | const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); |
| 4374 | UISettings::values.roms_path = QFileInfo(filename).path(); | 4394 | UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); |
| 4375 | BootGame(filename, MiiEditId); | 4395 | BootGame(filename, MiiEditId); |
| 4376 | } | 4396 | } |
| 4377 | 4397 | ||
| 4398 | void GMainWindow::OnOpenControllerMenu() { | ||
| 4399 | constexpr u64 ControllerAppletId = | ||
| 4400 | static_cast<u64>(Service::AM::Applets::AppletProgramId::Controller); | ||
| 4401 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); | ||
| 4402 | if (!bis_system) { | ||
| 4403 | QMessageBox::warning(this, tr("No firmware available"), | ||
| 4404 | tr("Please install the firmware to use the Controller Menu.")); | ||
| 4405 | return; | ||
| 4406 | } | ||
| 4407 | |||
| 4408 | auto controller_applet_nca = | ||
| 4409 | bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program); | ||
| 4410 | if (!controller_applet_nca) { | ||
| 4411 | QMessageBox::warning(this, tr("Controller Applet"), | ||
| 4412 | tr("Controller Menu is not available. Please reinstall firmware.")); | ||
| 4413 | return; | ||
| 4414 | } | ||
| 4415 | |||
| 4416 | system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller); | ||
| 4417 | |||
| 4418 | const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath())); | ||
| 4419 | UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); | ||
| 4420 | BootGame(filename, ControllerAppletId); | ||
| 4421 | } | ||
| 4422 | |||
| 4378 | void GMainWindow::OnCaptureScreenshot() { | 4423 | void GMainWindow::OnCaptureScreenshot() { |
| 4379 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { | 4424 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { |
| 4380 | return; | 4425 | return; |
| @@ -4561,11 +4606,13 @@ void GMainWindow::UpdateStatusBar() { | |||
| 4561 | emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); | 4606 | emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); |
| 4562 | game_fps_label->setVisible(true); | 4607 | game_fps_label->setVisible(true); |
| 4563 | emu_frametime_label->setVisible(true); | 4608 | emu_frametime_label->setVisible(true); |
| 4609 | firmware_label->setVisible(false); | ||
| 4564 | } | 4610 | } |
| 4565 | 4611 | ||
| 4566 | void GMainWindow::UpdateGPUAccuracyButton() { | 4612 | void GMainWindow::UpdateGPUAccuracyButton() { |
| 4567 | const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); | 4613 | const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); |
| 4568 | const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second; | 4614 | const auto gpu_accuracy_text = |
| 4615 | ConfigurationShared::gpu_accuracy_texts_map.find(gpu_accuracy)->second; | ||
| 4569 | gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); | 4616 | gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); |
| 4570 | gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal); | 4617 | gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal); |
| 4571 | } | 4618 | } |
| @@ -4574,31 +4621,32 @@ void GMainWindow::UpdateDockedButton() { | |||
| 4574 | const auto console_mode = Settings::values.use_docked_mode.GetValue(); | 4621 | const auto console_mode = Settings::values.use_docked_mode.GetValue(); |
| 4575 | dock_status_button->setChecked(Settings::IsDockedMode()); | 4622 | dock_status_button->setChecked(Settings::IsDockedMode()); |
| 4576 | dock_status_button->setText( | 4623 | dock_status_button->setText( |
| 4577 | Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); | 4624 | ConfigurationShared::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); |
| 4578 | } | 4625 | } |
| 4579 | 4626 | ||
| 4580 | void GMainWindow::UpdateAPIText() { | 4627 | void GMainWindow::UpdateAPIText() { |
| 4581 | const auto api = Settings::values.renderer_backend.GetValue(); | 4628 | const auto api = Settings::values.renderer_backend.GetValue(); |
| 4582 | const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second; | 4629 | const auto renderer_status_text = |
| 4630 | ConfigurationShared::renderer_backend_texts_map.find(api)->second; | ||
| 4583 | renderer_status_button->setText( | 4631 | renderer_status_button->setText( |
| 4584 | api == Settings::RendererBackend::OpenGL | 4632 | api == Settings::RendererBackend::OpenGL |
| 4585 | ? tr("%1 %2").arg( | 4633 | ? tr("%1 %2").arg(renderer_status_text.toUpper(), |
| 4586 | renderer_status_text.toUpper(), | 4634 | ConfigurationShared::shader_backend_texts_map |
| 4587 | Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue()) | 4635 | .find(Settings::values.shader_backend.GetValue()) |
| 4588 | ->second) | 4636 | ->second) |
| 4589 | : renderer_status_text.toUpper()); | 4637 | : renderer_status_text.toUpper()); |
| 4590 | } | 4638 | } |
| 4591 | 4639 | ||
| 4592 | void GMainWindow::UpdateFilterText() { | 4640 | void GMainWindow::UpdateFilterText() { |
| 4593 | const auto filter = Settings::values.scaling_filter.GetValue(); | 4641 | const auto filter = Settings::values.scaling_filter.GetValue(); |
| 4594 | const auto filter_text = Config::scaling_filter_texts_map.find(filter)->second; | 4642 | const auto filter_text = ConfigurationShared::scaling_filter_texts_map.find(filter)->second; |
| 4595 | filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR") | 4643 | filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR") |
| 4596 | : filter_text.toUpper()); | 4644 | : filter_text.toUpper()); |
| 4597 | } | 4645 | } |
| 4598 | 4646 | ||
| 4599 | void GMainWindow::UpdateAAText() { | 4647 | void GMainWindow::UpdateAAText() { |
| 4600 | const auto aa_mode = Settings::values.anti_aliasing.GetValue(); | 4648 | const auto aa_mode = Settings::values.anti_aliasing.GetValue(); |
| 4601 | const auto aa_text = Config::anti_aliasing_texts_map.find(aa_mode)->second; | 4649 | const auto aa_text = ConfigurationShared::anti_aliasing_texts_map.find(aa_mode)->second; |
| 4602 | aa_status_button->setText(aa_mode == Settings::AntiAliasing::None | 4650 | aa_status_button->setText(aa_mode == Settings::AntiAliasing::None |
| 4603 | ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA")) | 4651 | ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA")) |
| 4604 | : aa_text.toUpper()); | 4652 | : aa_text.toUpper()); |
| @@ -4668,26 +4716,10 @@ void GMainWindow::ShowMouseCursor() { | |||
| 4668 | } | 4716 | } |
| 4669 | } | 4717 | } |
| 4670 | 4718 | ||
| 4671 | void GMainWindow::CenterMouseCursor() { | ||
| 4672 | if (emu_thread == nullptr || !Settings::values.mouse_panning) { | ||
| 4673 | mouse_center_timer.stop(); | ||
| 4674 | return; | ||
| 4675 | } | ||
| 4676 | if (!this->isActiveWindow()) { | ||
| 4677 | mouse_center_timer.stop(); | ||
| 4678 | return; | ||
| 4679 | } | ||
| 4680 | const int center_x = render_window->width() / 2; | ||
| 4681 | const int center_y = render_window->height() / 2; | ||
| 4682 | |||
| 4683 | QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); | ||
| 4684 | } | ||
| 4685 | |||
| 4686 | void GMainWindow::OnMouseActivity() { | 4719 | void GMainWindow::OnMouseActivity() { |
| 4687 | if (!Settings::values.mouse_panning) { | 4720 | if (!Settings::values.mouse_panning) { |
| 4688 | ShowMouseCursor(); | 4721 | ShowMouseCursor(); |
| 4689 | } | 4722 | } |
| 4690 | mouse_center_timer.stop(); | ||
| 4691 | } | 4723 | } |
| 4692 | 4724 | ||
| 4693 | void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | 4725 | void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { |
| @@ -4778,6 +4810,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 4778 | "games.")); | 4810 | "games.")); |
| 4779 | } | 4811 | } |
| 4780 | 4812 | ||
| 4813 | SetFirmwareVersion(); | ||
| 4814 | |||
| 4781 | if (behavior == ReinitializeKeyBehavior::Warning) { | 4815 | if (behavior == ReinitializeKeyBehavior::Warning) { |
| 4782 | game_list->PopulateAsync(UISettings::values.game_dirs); | 4816 | game_list->PopulateAsync(UISettings::values.game_dirs); |
| 4783 | } | 4817 | } |
| @@ -4805,7 +4839,7 @@ bool GMainWindow::CheckSystemArchiveDecryption() { | |||
| 4805 | } | 4839 | } |
| 4806 | 4840 | ||
| 4807 | bool GMainWindow::CheckFirmwarePresence() { | 4841 | bool GMainWindow::CheckFirmwarePresence() { |
| 4808 | constexpr u64 MiiEditId = 0x0100000000001009ull; | 4842 | constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); |
| 4809 | 4843 | ||
| 4810 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); | 4844 | auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); |
| 4811 | if (!bis_system) { | 4845 | if (!bis_system) { |
| @@ -4820,6 +4854,28 @@ bool GMainWindow::CheckFirmwarePresence() { | |||
| 4820 | return true; | 4854 | return true; |
| 4821 | } | 4855 | } |
| 4822 | 4856 | ||
| 4857 | void GMainWindow::SetFirmwareVersion() { | ||
| 4858 | Service::Set::FirmwareVersionFormat firmware_data{}; | ||
| 4859 | const auto result = Service::Set::GetFirmwareVersionImpl( | ||
| 4860 | firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2); | ||
| 4861 | |||
| 4862 | if (result.IsError() || !CheckFirmwarePresence()) { | ||
| 4863 | LOG_INFO(Frontend, "Installed firmware: No firmware available"); | ||
| 4864 | firmware_label->setVisible(false); | ||
| 4865 | return; | ||
| 4866 | } | ||
| 4867 | |||
| 4868 | firmware_label->setVisible(true); | ||
| 4869 | |||
| 4870 | const std::string display_version(firmware_data.display_version.data()); | ||
| 4871 | const std::string display_title(firmware_data.display_title.data()); | ||
| 4872 | |||
| 4873 | LOG_INFO(Frontend, "Installed firmware: {}", display_title); | ||
| 4874 | |||
| 4875 | firmware_label->setText(QString::fromStdString(display_version)); | ||
| 4876 | firmware_label->setToolTip(QString::fromStdString(display_title)); | ||
| 4877 | } | ||
| 4878 | |||
| 4823 | bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, | 4879 | bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, |
| 4824 | u64* selected_title_id, u8* selected_content_record_type) { | 4880 | u64* selected_title_id, u8* selected_content_record_type) { |
| 4825 | using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>; | 4881 | using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>; |
| @@ -4901,6 +4957,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 4901 | 4957 | ||
| 4902 | UpdateUISettings(); | 4958 | UpdateUISettings(); |
| 4903 | game_list->SaveInterfaceLayout(); | 4959 | game_list->SaveInterfaceLayout(); |
| 4960 | UISettings::SaveWindowState(); | ||
| 4904 | hotkey_registry.SaveHotkeys(); | 4961 | hotkey_registry.SaveHotkeys(); |
| 4905 | 4962 | ||
| 4906 | // Unload controllers early | 4963 | // Unload controllers early |
| @@ -4963,22 +5020,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { | |||
| 4963 | AcceptDropEvent(event); | 5020 | AcceptDropEvent(event); |
| 4964 | } | 5021 | } |
| 4965 | 5022 | ||
| 4966 | void GMainWindow::leaveEvent(QEvent* event) { | ||
| 4967 | if (Settings::values.mouse_panning) { | ||
| 4968 | const QRect& rect = geometry(); | ||
| 4969 | QPoint position = QCursor::pos(); | ||
| 4970 | |||
| 4971 | qint32 x = qBound(rect.left(), position.x(), rect.right()); | ||
| 4972 | qint32 y = qBound(rect.top(), position.y(), rect.bottom()); | ||
| 4973 | // Only start the timer if the mouse has left the window bound. | ||
| 4974 | // The leave event is also triggered when the window looses focus. | ||
| 4975 | if (x != position.x() || y != position.y()) { | ||
| 4976 | mouse_center_timer.start(); | ||
| 4977 | } | ||
| 4978 | event->accept(); | ||
| 4979 | } | ||
| 4980 | } | ||
| 4981 | |||
| 4982 | bool GMainWindow::ConfirmChangeGame() { | 5023 | bool GMainWindow::ConfirmChangeGame() { |
| 4983 | if (emu_thread == nullptr) | 5024 | if (emu_thread == nullptr) |
| 4984 | return true; | 5025 | return true; |
| @@ -5055,9 +5096,9 @@ static void AdjustLinkColor() { | |||
| 5055 | } | 5096 | } |
| 5056 | 5097 | ||
| 5057 | void GMainWindow::UpdateUITheme() { | 5098 | void GMainWindow::UpdateUITheme() { |
| 5058 | const QString default_theme = | 5099 | const QString default_theme = QString::fromUtf8( |
| 5059 | QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second); | 5100 | UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second); |
| 5060 | QString current_theme = UISettings::values.theme; | 5101 | QString current_theme = QString::fromStdString(UISettings::values.theme); |
| 5061 | 5102 | ||
| 5062 | if (current_theme.isEmpty()) { | 5103 | if (current_theme.isEmpty()) { |
| 5063 | current_theme = default_theme; | 5104 | current_theme = default_theme; |
| @@ -5085,7 +5126,7 @@ void GMainWindow::UpdateUITheme() { | |||
| 5085 | QFile f(theme_uri); | 5126 | QFile f(theme_uri); |
| 5086 | if (!f.open(QFile::ReadOnly | QFile::Text)) { | 5127 | if (!f.open(QFile::ReadOnly | QFile::Text)) { |
| 5087 | LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme", | 5128 | LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme", |
| 5088 | UISettings::values.theme.toStdString()); | 5129 | UISettings::values.theme); |
| 5089 | current_theme = default_theme; | 5130 | current_theme = default_theme; |
| 5090 | } | 5131 | } |
| 5091 | } | 5132 | } |
| @@ -5098,7 +5139,7 @@ void GMainWindow::UpdateUITheme() { | |||
| 5098 | setStyleSheet(ts.readAll()); | 5139 | setStyleSheet(ts.readAll()); |
| 5099 | } else { | 5140 | } else { |
| 5100 | LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found", | 5141 | LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found", |
| 5101 | UISettings::values.theme.toStdString()); | 5142 | UISettings::values.theme); |
| 5102 | qApp->setStyleSheet({}); | 5143 | qApp->setStyleSheet({}); |
| 5103 | setStyleSheet({}); | 5144 | setStyleSheet({}); |
| 5104 | } | 5145 | } |
| @@ -5107,27 +5148,28 @@ void GMainWindow::UpdateUITheme() { | |||
| 5107 | void GMainWindow::LoadTranslation() { | 5148 | void GMainWindow::LoadTranslation() { |
| 5108 | bool loaded; | 5149 | bool loaded; |
| 5109 | 5150 | ||
| 5110 | if (UISettings::values.language.isEmpty()) { | 5151 | if (UISettings::values.language.empty()) { |
| 5111 | // If the selected language is empty, use system locale | 5152 | // If the selected language is empty, use system locale |
| 5112 | loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); | 5153 | loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); |
| 5113 | } else { | 5154 | } else { |
| 5114 | // Otherwise load from the specified file | 5155 | // Otherwise load from the specified file |
| 5115 | loaded = translator.load(UISettings::values.language, QStringLiteral(":/languages/")); | 5156 | loaded = translator.load(QString::fromStdString(UISettings::values.language), |
| 5157 | QStringLiteral(":/languages/")); | ||
| 5116 | } | 5158 | } |
| 5117 | 5159 | ||
| 5118 | if (loaded) { | 5160 | if (loaded) { |
| 5119 | qApp->installTranslator(&translator); | 5161 | qApp->installTranslator(&translator); |
| 5120 | } else { | 5162 | } else { |
| 5121 | UISettings::values.language = QStringLiteral("en"); | 5163 | UISettings::values.language = std::string("en"); |
| 5122 | } | 5164 | } |
| 5123 | } | 5165 | } |
| 5124 | 5166 | ||
| 5125 | void GMainWindow::OnLanguageChanged(const QString& locale) { | 5167 | void GMainWindow::OnLanguageChanged(const QString& locale) { |
| 5126 | if (UISettings::values.language != QStringLiteral("en")) { | 5168 | if (UISettings::values.language != std::string("en")) { |
| 5127 | qApp->removeTranslator(&translator); | 5169 | qApp->removeTranslator(&translator); |
| 5128 | } | 5170 | } |
| 5129 | 5171 | ||
| 5130 | UISettings::values.language = locale; | 5172 | UISettings::values.language = locale.toStdString(); |
| 5131 | LoadTranslation(); | 5173 | LoadTranslation(); |
| 5132 | ui->retranslateUi(this); | 5174 | ui->retranslateUi(this); |
| 5133 | multiplayer_state->retranslateUi(); | 5175 | multiplayer_state->retranslateUi(); |
| @@ -5147,13 +5189,21 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { | |||
| 5147 | discord_rpc->Update(); | 5189 | discord_rpc->Update(); |
| 5148 | } | 5190 | } |
| 5149 | 5191 | ||
| 5192 | #ifdef __unix__ | ||
| 5193 | void GMainWindow::SetGamemodeEnabled(bool state) { | ||
| 5194 | if (emulation_running) { | ||
| 5195 | Common::Linux::SetGamemodeState(state); | ||
| 5196 | } | ||
| 5197 | } | ||
| 5198 | #endif | ||
| 5199 | |||
| 5150 | void GMainWindow::changeEvent(QEvent* event) { | 5200 | void GMainWindow::changeEvent(QEvent* event) { |
| 5151 | #ifdef __unix__ | 5201 | #ifdef __unix__ |
| 5152 | // PaletteChange event appears to only reach so far into the GUI, explicitly asking to | 5202 | // PaletteChange event appears to only reach so far into the GUI, explicitly asking to |
| 5153 | // UpdateUITheme is a decent work around | 5203 | // UpdateUITheme is a decent work around |
| 5154 | if (event->type() == QEvent::PaletteChange) { | 5204 | if (event->type() == QEvent::PaletteChange) { |
| 5155 | const QPalette test_palette(qApp->palette()); | 5205 | const QPalette test_palette(qApp->palette()); |
| 5156 | const QString current_theme = UISettings::values.theme; | 5206 | const QString current_theme = QString::fromStdString(UISettings::values.theme); |
| 5157 | // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too | 5207 | // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too |
| 5158 | static QColor last_window_color; | 5208 | static QColor last_window_color; |
| 5159 | const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); | 5209 | const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); |
| @@ -5247,7 +5297,8 @@ static void SetHighDPIAttributes() { | |||
| 5247 | } | 5297 | } |
| 5248 | 5298 | ||
| 5249 | int main(int argc, char* argv[]) { | 5299 | int main(int argc, char* argv[]) { |
| 5250 | std::unique_ptr<Config> config = std::make_unique<Config>(); | 5300 | std::unique_ptr<QtConfig> config = std::make_unique<QtConfig>(); |
| 5301 | UISettings::RestoreWindowState(config); | ||
| 5251 | bool has_broken_vulkan = false; | 5302 | bool has_broken_vulkan = false; |
| 5252 | bool is_child = false; | 5303 | bool is_child = false; |
| 5253 | if (CheckEnvVars(&is_child)) { | 5304 | if (CheckEnvVars(&is_child)) { |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index f67c4cfda..530e445f9 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | #include "common/announce_multiplayer_room.h" | 16 | #include "common/announce_multiplayer_room.h" |
| 17 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 18 | #include "configuration/qt_config.h" | ||
| 18 | #include "input_common/drivers/tas_input.h" | 19 | #include "input_common/drivers/tas_input.h" |
| 19 | #include "yuzu/compatibility_list.h" | 20 | #include "yuzu/compatibility_list.h" |
| 20 | #include "yuzu/hotkeys.h" | 21 | #include "yuzu/hotkeys.h" |
| @@ -26,7 +27,7 @@ | |||
| 26 | #include <QtDBus/QtDBus> | 27 | #include <QtDBus/QtDBus> |
| 27 | #endif | 28 | #endif |
| 28 | 29 | ||
| 29 | class Config; | 30 | class QtConfig; |
| 30 | class ClickableLabel; | 31 | class ClickableLabel; |
| 31 | class EmuThread; | 32 | class EmuThread; |
| 32 | class GameList; | 33 | class GameList; |
| @@ -185,7 +186,7 @@ class GMainWindow : public QMainWindow { | |||
| 185 | public: | 186 | public: |
| 186 | void filterBarSetChecked(bool state); | 187 | void filterBarSetChecked(bool state); |
| 187 | void UpdateUITheme(); | 188 | void UpdateUITheme(); |
| 188 | explicit GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan); | 189 | explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan); |
| 189 | ~GMainWindow() override; | 190 | ~GMainWindow() override; |
| 190 | 191 | ||
| 191 | bool DropAction(QDropEvent* event); | 192 | bool DropAction(QDropEvent* event); |
| @@ -339,6 +340,7 @@ private: | |||
| 339 | void SetupSigInterrupts(); | 340 | void SetupSigInterrupts(); |
| 340 | static void HandleSigInterrupt(int); | 341 | static void HandleSigInterrupt(int); |
| 341 | void OnSigInterruptNotifierActivated(); | 342 | void OnSigInterruptNotifierActivated(); |
| 343 | void SetGamemodeEnabled(bool state); | ||
| 342 | #endif | 344 | #endif |
| 343 | 345 | ||
| 344 | private slots: | 346 | private slots: |
| @@ -410,6 +412,7 @@ private slots: | |||
| 410 | void OnAlbum(); | 412 | void OnAlbum(); |
| 411 | void OnCabinet(Service::NFP::CabinetMode mode); | 413 | void OnCabinet(Service::NFP::CabinetMode mode); |
| 412 | void OnMiiEdit(); | 414 | void OnMiiEdit(); |
| 415 | void OnOpenControllerMenu(); | ||
| 413 | void OnCaptureScreenshot(); | 416 | void OnCaptureScreenshot(); |
| 414 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); | 417 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); |
| 415 | void OnLanguageChanged(const QString& locale); | 418 | void OnLanguageChanged(const QString& locale); |
| @@ -449,13 +452,13 @@ private: | |||
| 449 | void UpdateInputDrivers(); | 452 | void UpdateInputDrivers(); |
| 450 | void HideMouseCursor(); | 453 | void HideMouseCursor(); |
| 451 | void ShowMouseCursor(); | 454 | void ShowMouseCursor(); |
| 452 | void CenterMouseCursor(); | ||
| 453 | void OpenURL(const QUrl& url); | 455 | void OpenURL(const QUrl& url); |
| 454 | void LoadTranslation(); | 456 | void LoadTranslation(); |
| 455 | void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); | 457 | void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); |
| 456 | bool CheckDarkMode(); | 458 | bool CheckDarkMode(); |
| 457 | bool CheckSystemArchiveDecryption(); | 459 | bool CheckSystemArchiveDecryption(); |
| 458 | bool CheckFirmwarePresence(); | 460 | bool CheckFirmwarePresence(); |
| 461 | void SetFirmwareVersion(); | ||
| 459 | void ConfigureFilesystemProvider(const std::string& filepath); | 462 | void ConfigureFilesystemProvider(const std::string& filepath); |
| 460 | /** | 463 | /** |
| 461 | * 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 |
| @@ -510,6 +513,7 @@ private: | |||
| 510 | QLabel* game_fps_label = nullptr; | 513 | QLabel* game_fps_label = nullptr; |
| 511 | QLabel* emu_frametime_label = nullptr; | 514 | QLabel* emu_frametime_label = nullptr; |
| 512 | QLabel* tas_label = nullptr; | 515 | QLabel* tas_label = nullptr; |
| 516 | QLabel* firmware_label = nullptr; | ||
| 513 | QPushButton* gpu_accuracy_button = nullptr; | 517 | QPushButton* gpu_accuracy_button = nullptr; |
| 514 | QPushButton* renderer_status_button = nullptr; | 518 | QPushButton* renderer_status_button = nullptr; |
| 515 | QPushButton* dock_status_button = nullptr; | 519 | QPushButton* dock_status_button = nullptr; |
| @@ -520,7 +524,7 @@ private: | |||
| 520 | QSlider* volume_slider = nullptr; | 524 | QSlider* volume_slider = nullptr; |
| 521 | QTimer status_bar_update_timer; | 525 | QTimer status_bar_update_timer; |
| 522 | 526 | ||
| 523 | std::unique_ptr<Config> config; | 527 | std::unique_ptr<QtConfig> config; |
| 524 | 528 | ||
| 525 | // Whether emulation is currently running in yuzu. | 529 | // Whether emulation is currently running in yuzu. |
| 526 | bool emulation_running = false; | 530 | bool emulation_running = false; |
| @@ -531,7 +535,6 @@ private: | |||
| 531 | bool auto_paused = false; | 535 | bool auto_paused = false; |
| 532 | bool auto_muted = false; | 536 | bool auto_muted = false; |
| 533 | QTimer mouse_hide_timer; | 537 | QTimer mouse_hide_timer; |
| 534 | QTimer mouse_center_timer; | ||
| 535 | QTimer update_input_timer; | 538 | QTimer update_input_timer; |
| 536 | 539 | ||
| 537 | QString startup_icon_theme; | 540 | QString startup_icon_theme; |
| @@ -588,5 +591,4 @@ protected: | |||
| 588 | void dropEvent(QDropEvent* event) override; | 591 | void dropEvent(QDropEvent* event) override; |
| 589 | void dragEnterEvent(QDragEnterEvent* event) override; | 592 | void dragEnterEvent(QDragEnterEvent* event) override; |
| 590 | void dragMoveEvent(QDragMoveEvent* event) override; | 593 | void dragMoveEvent(QDragMoveEvent* event) override; |
| 591 | void leaveEvent(QEvent* event) override; | ||
| 592 | }; | 594 | }; |
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 3485a6347..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 { |
| @@ -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 7b2a47496..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" |
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..a81635fa4 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" |
| @@ -62,6 +63,10 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; | |||
| 62 | } | 63 | } |
| 63 | #endif | 64 | #endif |
| 64 | 65 | ||
| 66 | #ifdef __unix__ | ||
| 67 | #include "common/linux/gamemode.h" | ||
| 68 | #endif | ||
| 69 | |||
| 65 | static void PrintHelp(const char* argv0) { | 70 | static void PrintHelp(const char* argv0) { |
| 66 | std::cout << "Usage: " << argv0 | 71 | std::cout << "Usage: " << argv0 |
| 67 | << " [options] <filename>\n" | 72 | << " [options] <filename>\n" |
| @@ -300,7 +305,7 @@ int main(int argc, char** argv) { | |||
| 300 | } | 305 | } |
| 301 | } | 306 | } |
| 302 | 307 | ||
| 303 | Config config{config_path}; | 308 | SdlConfig config{config_path}; |
| 304 | 309 | ||
| 305 | // apply the log_filter setting | 310 | // apply the log_filter setting |
| 306 | // the logger was initialized before and doesn't pick up the filter on its own | 311 | // the logger was initialized before and doesn't pick up the filter on its own |
| @@ -424,6 +429,10 @@ int main(int argc, char** argv) { | |||
| 424 | exit(0); | 429 | exit(0); |
| 425 | }); | 430 | }); |
| 426 | 431 | ||
| 432 | #ifdef __unix__ | ||
| 433 | Common::Linux::StartGamemode(); | ||
| 434 | #endif | ||
| 435 | |||
| 427 | void(system.Run()); | 436 | void(system.Run()); |
| 428 | if (system.DebuggerEnabled()) { | 437 | if (system.DebuggerEnabled()) { |
| 429 | system.InitializeDebugger(); | 438 | system.InitializeDebugger(); |
| @@ -435,6 +444,10 @@ int main(int argc, char** argv) { | |||
| 435 | void(system.Pause()); | 444 | void(system.Pause()); |
| 436 | system.ShutdownMainProcess(); | 445 | system.ShutdownMainProcess(); |
| 437 | 446 | ||
| 447 | #ifdef __unix__ | ||
| 448 | Common::Linux::StopGamemode(); | ||
| 449 | #endif | ||
| 450 | |||
| 438 | detached_tasks.WaitForAllTasks(); | 451 | detached_tasks.WaitForAllTasks(); |
| 439 | return 0; | 452 | return 0; |
| 440 | } | 453 | } |