summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/StringInputViewHolder.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt13
-rw-r--r--src/android/app/src/main/jni/native.cpp3
-rw-r--r--src/android/app/src/main/jni/native_input.cpp76
-rw-r--r--src/android/app/src/main/res/values/strings.xml1
-rw-r--r--src/common/settings.h6
-rw-r--r--src/common/settings_enums.h2
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/device_memory_manager.inc12
-rw-r--r--src/core/file_sys/fssystem/fssystem_aes_xts_storage.h1
-rw-r--r--src/core/hle/service/btm/btm.cpp272
-rw-r--r--src/core/hle/service/btm/btm.h4
-rw-r--r--src/core/hle/service/btm/btm_debug.cpp33
-rw-r--r--src/core/hle/service/btm/btm_debug.h21
-rw-r--r--src/core/hle/service/btm/btm_system.cpp31
-rw-r--r--src/core/hle/service/btm/btm_system.h25
-rw-r--r--src/core/hle/service/btm/btm_system_core.cpp127
-rw-r--r--src/core/hle/service/btm/btm_system_core.h60
-rw-r--r--src/core/hle/service/btm/btm_user.cpp30
-rw-r--r--src/core/hle/service/btm/btm_user.h25
-rw-r--r--src/core/hle/service/btm/btm_user_core.cpp103
-rw-r--r--src/core/hle/service/btm/btm_user_core.h47
-rw-r--r--src/core/hle/service/ns/application_manager_interface.cpp6
-rw-r--r--src/core/hle/service/ns/application_manager_interface.h2
-rw-r--r--src/core/hle/service/ns/ns_types.h5
-rw-r--r--src/core/hle/service/ns/query_service.cpp5
-rw-r--r--src/core/hle/service/ns/query_service.h4
-rw-r--r--src/core/hle/service/nvnflinger/display.h26
-rw-r--r--src/core/hle/service/nvnflinger/hardware_composer.cpp12
-rw-r--r--src/core/hle/service/nvnflinger/surface_flinger.cpp39
-rw-r--r--src/core/hle/service/nvnflinger/surface_flinger.h6
-rw-r--r--src/core/hle/service/vi/container.cpp52
-rw-r--r--src/core/hle/service/vi/container.h3
-rw-r--r--src/core/hle/service/vi/layer.h16
-rw-r--r--src/core/hle/service/vi/layer_list.h6
-rw-r--r--src/core/hle/service/vi/shared_buffer_manager.cpp10
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_special.cpp8
-rw-r--r--src/shader_recompiler/profile.h1
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp1
-rw-r--r--src/video_core/surface.cpp14
-rw-r--r--src/video_core/surface.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h6
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp14
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h5
-rw-r--r--src/yuzu/configuration/shared_translation.cpp10
-rw-r--r--src/yuzu/main.cpp109
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/main.ui8
62 files changed, 977 insertions, 458 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt
index d35de80c4..a84ac77a2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt
@@ -64,17 +64,17 @@ data class PlayerInput(
fun hasMapping(): Boolean {
var hasMapping = false
buttons.forEach {
- if (it != "[empty]") {
+ if (it != "[empty]" && it.isNotEmpty()) {
hasMapping = true
}
}
analogs.forEach {
- if (it != "[empty]") {
+ if (it != "[empty]" && it.isNotEmpty()) {
hasMapping = true
}
}
motions.forEach {
- if (it != "[empty]") {
+ if (it != "[empty]" && it.isNotEmpty()) {
hasMapping = true
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
index a0d8cfede..6f16cf5b1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
@@ -6,7 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.model
import org.yuzu.yuzu_emu.utils.NativeConfig
enum class StringSetting(override val key: String) : AbstractStringSetting {
- DRIVER_PATH("driver_path");
+ DRIVER_PATH("driver_path"),
+ DEVICE_NAME("device_name");
override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)
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 8f724835e..5fdf98318 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
@@ -16,6 +16,7 @@ import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
+import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.utils.NativeConfig
/**
@@ -75,6 +76,9 @@ abstract class SettingsItem(
get() = NativeLibrary.isRunning() && !setting.global &&
!NativeConfig.isPerGameConfigLoaded()
+ val clearable: Boolean
+ get() = !setting.global && NativeConfig.isPerGameConfigLoaded()
+
companion object {
const val TYPE_HEADER = 0
const val TYPE_SWITCH = 1
@@ -87,6 +91,7 @@ abstract class SettingsItem(
const val TYPE_INPUT = 8
const val TYPE_INT_SINGLE_CHOICE = 9
const val TYPE_INPUT_PROFILE = 10
+ const val TYPE_STRING_INPUT = 11
const val FASTMEM_COMBINED = "fastmem_combined"
@@ -105,6 +110,7 @@ abstract class SettingsItem(
// List of all general
val settingsItems = HashMap<String, SettingsItem>().apply {
+ put(StringInputSetting(StringSetting.DEVICE_NAME, titleId = R.string.device_name))
put(
SwitchSetting(
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt
new file mode 100644
index 000000000..1eb999416
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model.view
+
+import androidx.annotation.StringRes
+import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
+
+class StringInputSetting(
+ setting: AbstractStringSetting,
+ @StringRes titleId: Int = 0,
+ titleString: String = "",
+ @StringRes descriptionId: Int = 0,
+ descriptionString: String = ""
+) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
+ override val type = TYPE_STRING_INPUT
+
+ fun getSelectedValue(needsGlobal: Boolean = false) = setting.getValueAsString(needsGlobal)
+
+ fun setSelectedValue(selection: String) =
+ (setting as AbstractStringSetting).setString(selection)
+}
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 45c8faa10..500ac6e66 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
@@ -85,6 +85,10 @@ class SettingsAdapter(
InputProfileViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
+ SettingsItem.TYPE_STRING_INPUT -> {
+ StringInputViewHolder(ListItemSettingBinding.inflate(inflater), this)
+ }
+
else -> {
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
}
@@ -392,6 +396,15 @@ class SettingsAdapter(
popup.show()
}
+ fun onStringInputClick(item: StringInputSetting, position: Int) {
+ SettingsDialogFragment.newInstance(
+ settingsViewModel,
+ item,
+ SettingsItem.TYPE_STRING_INPUT,
+ position
+ ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
+ }
+
fun onLongClick(item: SettingsItem, position: Int): Boolean {
SettingsDialogFragment.newInstance(
settingsViewModel,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
index a81ff6b1a..7f562a1f4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
@@ -14,6 +14,7 @@ import androidx.fragment.app.activityViewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
@@ -23,6 +24,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.IntSingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.StringInputSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
import org.yuzu.yuzu_emu.utils.ParamPackage
import org.yuzu.yuzu_emu.utils.collect
@@ -37,6 +39,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
private val settingsViewModel: SettingsViewModel by activityViewModels()
private lateinit var sliderBinding: DialogSliderBinding
+ private lateinit var stringInputBinding: DialogEditTextBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -131,6 +134,18 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
.create()
}
+ SettingsItem.TYPE_STRING_INPUT -> {
+ stringInputBinding = DialogEditTextBinding.inflate(layoutInflater)
+ val item = settingsViewModel.clickedItem as StringInputSetting
+ stringInputBinding.editText.setText(item.getSelectedValue())
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(item.title)
+ .setView(stringInputBinding.root)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, defaultCancelListener)
+ .create()
+ }
+
SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
val item = settingsViewModel.clickedItem as StringSingleChoiceSetting
MaterialAlertDialogBuilder(requireContext())
@@ -158,6 +173,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
): View? {
return when (type) {
SettingsItem.TYPE_SLIDER -> sliderBinding.root
+ SettingsItem.TYPE_STRING_INPUT -> stringInputBinding.root
else -> super.onCreateView(inflater, container, savedInstanceState)
}
}
@@ -200,6 +216,13 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
val sliderSetting = settingsViewModel.clickedItem as SliderSetting
sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value)
}
+
+ is StringInputSetting -> {
+ val stringInputSetting = settingsViewModel.clickedItem as StringInputSetting
+ stringInputSetting.setSelectedValue(
+ (stringInputBinding.editText.text ?: "").toString()
+ )
+ }
}
closeDialog()
}
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 e491c29a2..3ea5f5008 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
@@ -23,6 +23,7 @@ import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
+import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NativeConfig
@@ -153,6 +154,7 @@ class SettingsFragmentPresenter(
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
+ add(StringSetting.DEVICE_NAME.key)
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(BooleanSetting.USE_DOCKED_MODE.key)
@@ -778,7 +780,7 @@ class SettingsFragmentPresenter(
playerIndex: Int,
paramName: String,
stick: NativeAnalog,
- defaultValue: Int
+ defaultValue: Float
): AbstractIntSetting =
object : AbstractIntSetting {
val params get() = NativeInput.getStickParam(playerIndex, stick)
@@ -786,7 +788,7 @@ class SettingsFragmentPresenter(
override val key = ""
override fun getInt(needsGlobal: Boolean): Int =
- (params.get(paramName, 0.15f) * 100).toInt()
+ (params.get(paramName, defaultValue) * 100).toInt()
override fun setInt(value: Int) {
val tempParams = params
@@ -794,12 +796,12 @@ class SettingsFragmentPresenter(
NativeInput.setStickParam(playerIndex, stick, tempParams)
}
- override val defaultValue = defaultValue
+ override val defaultValue = (defaultValue * 100).toInt()
override fun getValueAsString(needsGlobal: Boolean): String =
getInt(needsGlobal).toString()
- override fun reset() = setInt(defaultValue)
+ override fun reset() = setInt(this.defaultValue)
}
private fun getExtraStickSettings(
@@ -809,11 +811,11 @@ class SettingsFragmentPresenter(
val stickIsController =
NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
val modifierRangeSetting =
- getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 50)
+ getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 0.5f)
val stickRangeSetting =
- getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 95)
+ getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 0.95f)
val stickDeadzoneSetting =
- getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 15)
+ getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 0.15f)
val out = mutableListOf<SettingsItem>().apply {
if (stickIsController) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index 367db7fd2..0309fad59 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -13,7 +13,6 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
-import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
@@ -32,9 +31,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
binding.textSettingValue.text = dateFormatter.format(zonedTime)
- binding.buttonClear.setVisible(
- !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
- )
+ binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
index e2fe0b072..489f55455 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
@@ -10,7 +10,6 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
-import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
@@ -48,9 +47,7 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
binding.textSettingValue.setVisible(false)
}
- binding.buttonClear.setVisible(
- !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
- )
+ binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
index a37b59b44..90a7138cb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
@@ -9,7 +9,6 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
-import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
@@ -28,9 +27,7 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
setting.units
)
- binding.buttonClear.setVisible(
- !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
- )
+ binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/StringInputViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/StringInputViewHolder.kt
new file mode 100644
index 000000000..a4fd36f62
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/StringInputViewHolder.kt
@@ -0,0 +1,45 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui.viewholder
+
+import android.view.View
+import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
+import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.features.settings.model.view.StringInputSetting
+import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
+import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+
+class StringInputViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
+ SettingViewHolder(binding.root, adapter) {
+ private lateinit var setting: StringInputSetting
+
+ override fun bind(item: SettingsItem) {
+ setting = item as StringInputSetting
+ binding.textSettingName.text = setting.title
+ binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
+ binding.textSettingDescription.text = setting.description
+ binding.textSettingValue.setVisible(true)
+ binding.textSettingValue.text = setting.getSelectedValue()
+
+ binding.buttonClear.setVisible(setting.clearable)
+ binding.buttonClear.setOnClickListener {
+ adapter.onClearClick(setting, bindingAdapterPosition)
+ }
+
+ setStyle(setting.isEditable, binding)
+ }
+
+ override fun onClick(clicked: View) {
+ if (setting.isEditable) {
+ adapter.onStringInputClick(setting, bindingAdapterPosition)
+ }
+ }
+
+ override fun onLongClick(clicked: View): Boolean {
+ if (setting.isEditable) {
+ return adapter.onLongClick(setting, bindingAdapterPosition)
+ }
+ return false
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
index 53f7b301f..e5763264a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
@@ -9,7 +9,6 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
-import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
@@ -29,9 +28,7 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
adapter.onBooleanClick(setting, binding.switchWidget.isChecked, bindingAdapterPosition)
}
- binding.buttonClear.setVisible(
- !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
- )
+ binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
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 c3b2b11f8..bcc880e17 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
@@ -810,7 +810,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
}
- binding.doneControlConfig.setVisible(false)
+ binding.doneControlConfig.setVisible(true)
binding.surfaceInputOverlay.setIsInEditMode(true)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
index 66907085a..737e03584 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
@@ -28,6 +28,7 @@ import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
import org.yuzu.yuzu_emu.features.input.model.NativeButton
+import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
@@ -99,12 +100,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
}
var shouldUpdateView = false
- val playerIndex =
- if (NativeInput.isHandheldOnly()) {
- NativeInput.ConsoleDevice
- } else {
- NativeInput.Player1Device
- }
+ val playerIndex = when (NativeInput.getStyleIndex(0)) {
+ NpadStyleIndex.Handheld -> 8
+ else -> 0
+ }
for (button in overlayButtons) {
if (!button.updateStatus(event)) {
@@ -664,7 +663,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
val overlayControlData = NativeConfig.getOverlayControlData()
overlayControlData.forEach {
- it.enabled = OverlayControl.from(it.id)?.defaultVisibility == false
+ it.enabled = OverlayControl.from(it.id)?.defaultVisibility == true
}
NativeConfig.setOverlayControlData(overlayControlData)
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 4ea82e217..1226219ad 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -292,6 +292,9 @@ void EmulationSession::ShutdownEmulation() {
// Unload user input.
m_system.HIDCore().UnloadInputDevices();
+ // Enable all controllers
+ m_system.HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
+
// Shutdown the main emulated process
if (m_load_result == Core::SystemResultStatus::Success) {
m_system.DetachDebugger();
diff --git a/src/android/app/src/main/jni/native_input.cpp b/src/android/app/src/main/jni/native_input.cpp
index 37a65f2b8..4935a4607 100644
--- a/src/android/app/src/main/jni/native_input.cpp
+++ b/src/android/app/src/main/jni/native_input.cpp
@@ -102,8 +102,50 @@ void ApplyControllerConfig(size_t player_index,
}
}
+std::vector<s32> GetSupportedStyles(int player_index) {
+ auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ const auto npad_style_set = hid_core.GetSupportedStyleTag();
+ std::vector<s32> supported_indexes;
+ if (npad_style_set.fullkey == 1) {
+ supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Fullkey));
+ }
+
+ if (npad_style_set.joycon_dual == 1) {
+ supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconDual));
+ }
+
+ if (npad_style_set.joycon_left == 1) {
+ supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconLeft));
+ }
+
+ if (npad_style_set.joycon_right == 1) {
+ supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconRight));
+ }
+
+ if (player_index == 0 && npad_style_set.handheld == 1) {
+ supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Handheld));
+ }
+
+ if (npad_style_set.gamecube == 1) {
+ supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::GameCube));
+ }
+
+ return supported_indexes;
+}
+
void ConnectController(size_t player_index, bool connected) {
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
+ ApplyControllerConfig(player_index, [&](Core::HID::EmulatedController* controller) {
+ auto supported_styles = GetSupportedStyles(player_index);
+ auto controller_style = controller->GetNpadStyleIndex(true);
+ auto style = std::find(supported_styles.begin(), supported_styles.end(),
+ static_cast<int>(controller_style));
+ if (style == supported_styles.end() && !supported_styles.empty()) {
+ controller->SetNpadStyleIndex(
+ static_cast<Core::HID::NpadStyleIndex>(supported_styles[0]));
+ }
+ });
+
if (player_index == 0) {
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
@@ -522,36 +564,10 @@ jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonNameImpl(JNIEnv
jintArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl(
JNIEnv* env, jobject j_obj, jint j_player_index) {
- auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
- const auto npad_style_set = hid_core.GetSupportedStyleTag();
- std::vector<s32> supported_indexes;
- if (npad_style_set.fullkey == 1) {
- supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Fullkey));
- }
-
- if (npad_style_set.joycon_dual == 1) {
- supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconDual));
- }
-
- if (npad_style_set.joycon_left == 1) {
- supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconLeft));
- }
-
- if (npad_style_set.joycon_right == 1) {
- supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconRight));
- }
-
- if (j_player_index == 0 && npad_style_set.handheld == 1) {
- supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Handheld));
- }
-
- if (npad_style_set.gamecube == 1) {
- supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::GameCube));
- }
-
- jintArray j_supported_indexes = env->NewIntArray(supported_indexes.size());
- env->SetIntArrayRegion(j_supported_indexes, 0, supported_indexes.size(),
- supported_indexes.data());
+ auto supported_styles = GetSupportedStyles(j_player_index);
+ jintArray j_supported_indexes = env->NewIntArray(supported_styles.size());
+ env->SetIntArrayRegion(j_supported_indexes, 0, supported_styles.size(),
+ supported_styles.data());
return j_supported_indexes;
}
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 6a631f664..f7f19cdad 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -209,6 +209,7 @@
<string name="value_with_units">%1$s%2$s</string>
<!-- System settings strings -->
+ <string name="device_name">Device name</string>
<string name="use_docked_mode">Docked Mode</string>
<string name="use_docked_mode_description">Increases resolution, decreasing performance. Handheld Mode is used when disabled, lowering resolution and increasing performance.</string>
<string name="emulated_region">Emulated region</string>
diff --git a/src/common/settings.h b/src/common/settings.h
index aa054dc24..b2b071e7e 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -384,6 +384,12 @@ struct Values {
AstcRecompression::Bc3,
"astc_recompression",
Category::RendererAdvanced};
+ SwitchableSetting<VramUsageMode, true> vram_usage_mode{linkage,
+ VramUsageMode::Conservative,
+ VramUsageMode::Conservative,
+ VramUsageMode::Aggressive,
+ "vram_usage_mode",
+ Category::RendererAdvanced};
SwitchableSetting<bool> async_presentation{linkage,
#ifdef ANDROID
true,
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index f42367e67..6e247e930 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -122,6 +122,8 @@ ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
+ENUM(VramUsageMode, Conservative, Aggressive);
+
ENUM(RendererBackend, OpenGL, Vulkan, Null);
ENUM(ShaderBackend, Glsl, Glasm, SpirV);
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 24dcc405f..cfce352c9 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -547,6 +547,16 @@ add_library(core STATIC
hle/service/btdrv/btdrv.h
hle/service/btm/btm.cpp
hle/service/btm/btm.h
+ hle/service/btm/btm_debug.cpp
+ hle/service/btm/btm_debug.h
+ hle/service/btm/btm_system.cpp
+ hle/service/btm/btm_system.h
+ hle/service/btm/btm_system_core.cpp
+ hle/service/btm/btm_system_core.h
+ hle/service/btm/btm_user.cpp
+ hle/service/btm/btm_user.h
+ hle/service/btm/btm_user_core.cpp
+ hle/service/btm/btm_user_core.h
hle/service/caps/caps.cpp
hle/service/caps/caps.h
hle/service/caps/caps_a.cpp
diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc
index 37c1e69c3..f104d495b 100644
--- a/src/core/device_memory_manager.inc
+++ b/src/core/device_memory_manager.inc
@@ -522,13 +522,17 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
auto* memory_device_inter = registered_processes[asid.id];
const auto release_pending = [&] {
if (uncache_bytes > 0) {
- MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
- uncache_bytes, false);
+ if (memory_device_inter != nullptr) {
+ MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
+ uncache_bytes, false);
+ }
uncache_bytes = 0;
}
if (cache_bytes > 0) {
- MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
- cache_bytes, true);
+ if (memory_device_inter != nullptr) {
+ MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
+ cache_bytes, true);
+ }
cache_bytes = 0;
}
};
diff --git a/src/core/file_sys/fssystem/fssystem_aes_xts_storage.h b/src/core/file_sys/fssystem/fssystem_aes_xts_storage.h
index f342efb57..0e83ca1b9 100644
--- a/src/core/file_sys/fssystem/fssystem_aes_xts_storage.h
+++ b/src/core/file_sys/fssystem/fssystem_aes_xts_storage.h
@@ -3,6 +3,7 @@
#pragma once
+#include <mutex>
#include <optional>
#include "core/crypto/aes_util.h"
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 2dc23e674..d120dade8 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -3,141 +3,18 @@
#include <memory>
-#include "common/logging/log.h"
-#include "core/core.h"
-#include "core/hle/kernel/k_event.h"
#include "core/hle/service/btm/btm.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/btm/btm_debug.h"
+#include "core/hle/service/btm/btm_system.h"
+#include "core/hle/service/btm/btm_user.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::BTM {
-class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
+class IBtm final : public ServiceFramework<IBtm> {
public:
- explicit IBtmUserCore(Core::System& system_)
- : ServiceFramework{system_, "IBtmUserCore"}, service_context{system_, "IBtmUserCore"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"},
- {1, nullptr, "GetBleScanFilterParameter"},
- {2, nullptr, "GetBleScanFilterParameter2"},
- {3, nullptr, "StartBleScanForGeneral"},
- {4, nullptr, "StopBleScanForGeneral"},
- {5, nullptr, "GetBleScanResultsForGeneral"},
- {6, nullptr, "StartBleScanForPaired"},
- {7, nullptr, "StopBleScanForPaired"},
- {8, nullptr, "StartBleScanForSmartDevice"},
- {9, nullptr, "StopBleScanForSmartDevice"},
- {10, nullptr, "GetBleScanResultsForSmartDevice"},
- {17, &IBtmUserCore::AcquireBleConnectionEvent, "AcquireBleConnectionEvent"},
- {18, nullptr, "BleConnect"},
- {19, nullptr, "BleDisconnect"},
- {20, nullptr, "BleGetConnectionState"},
- {21, nullptr, "AcquireBlePairingEvent"},
- {22, nullptr, "BlePairDevice"},
- {23, nullptr, "BleUnPairDevice"},
- {24, nullptr, "BleUnPairDevice2"},
- {25, nullptr, "BleGetPairedDevices"},
- {26, &IBtmUserCore::AcquireBleServiceDiscoveryEvent, "AcquireBleServiceDiscoveryEvent"},
- {27, nullptr, "GetGattServices"},
- {28, nullptr, "GetGattService"},
- {29, nullptr, "GetGattIncludedServices"},
- {30, nullptr, "GetBelongingGattService"},
- {31, nullptr, "GetGattCharacteristics"},
- {32, nullptr, "GetGattDescriptors"},
- {33, &IBtmUserCore::AcquireBleMtuConfigEvent, "AcquireBleMtuConfigEvent"},
- {34, nullptr, "ConfigureBleMtu"},
- {35, nullptr, "GetBleMtu"},
- {36, nullptr, "RegisterBleGattDataPath"},
- {37, nullptr, "UnregisterBleGattDataPath"},
- };
- // clang-format on
- RegisterHandlers(functions);
-
- scan_event = service_context.CreateEvent("IBtmUserCore:ScanEvent");
- connection_event = service_context.CreateEvent("IBtmUserCore:ConnectionEvent");
- service_discovery_event = service_context.CreateEvent("IBtmUserCore:DiscoveryEvent");
- config_event = service_context.CreateEvent("IBtmUserCore:ConfigEvent");
- }
-
- ~IBtmUserCore() override {
- service_context.CloseEvent(scan_event);
- service_context.CloseEvent(connection_event);
- service_context.CloseEvent(service_discovery_event);
- service_context.CloseEvent(config_event);
- }
-
-private:
- void AcquireBleScanEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3, 1};
- rb.Push(ResultSuccess);
- rb.Push(true);
- rb.PushCopyObjects(scan_event->GetReadableEvent());
- }
-
- void AcquireBleConnectionEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3, 1};
- rb.Push(ResultSuccess);
- rb.Push(true);
- rb.PushCopyObjects(connection_event->GetReadableEvent());
- }
-
- void AcquireBleServiceDiscoveryEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3, 1};
- rb.Push(ResultSuccess);
- rb.Push(true);
- rb.PushCopyObjects(service_discovery_event->GetReadableEvent());
- }
-
- void AcquireBleMtuConfigEvent(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 3, 1};
- rb.Push(ResultSuccess);
- rb.Push(true);
- rb.PushCopyObjects(config_event->GetReadableEvent());
- }
-
- KernelHelpers::ServiceContext service_context;
-
- Kernel::KEvent* scan_event;
- Kernel::KEvent* connection_event;
- Kernel::KEvent* service_discovery_event;
- Kernel::KEvent* config_event;
-};
-
-class BTM_USR final : public ServiceFramework<BTM_USR> {
-public:
- explicit BTM_USR(Core::System& system_) : ServiceFramework{system_, "btm:u"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &BTM_USR::GetCore, "GetCore"},
- };
- // clang-format on
- RegisterHandlers(functions);
- }
-
-private:
- void GetCore(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IBtmUserCore>(system);
- }
-};
-
-class BTM final : public ServiceFramework<BTM> {
-public:
- explicit BTM(Core::System& system_) : ServiceFramework{system_, "btm"} {
+ explicit IBtm(Core::System& system_) : ServiceFramework{system_, "btm"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetState"},
@@ -232,144 +109,13 @@ public:
}
};
-class BTM_DBG final : public ServiceFramework<BTM_DBG> {
-public:
- explicit BTM_DBG(Core::System& system_) : ServiceFramework{system_, "btm:dbg"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "AcquireDiscoveryEvent"},
- {1, nullptr, "StartDiscovery"},
- {2, nullptr, "CancelDiscovery"},
- {3, nullptr, "GetDeviceProperty"},
- {4, nullptr, "CreateBond"},
- {5, nullptr, "CancelBond"},
- {6, nullptr, "SetTsiMode"},
- {7, nullptr, "GeneralTest"},
- {8, nullptr, "HidConnect"},
- {9, nullptr, "GeneralGet"},
- {10, nullptr, "GetGattClientDisconnectionReason"},
- {11, nullptr, "GetBleConnectionParameter"},
- {12, nullptr, "GetBleConnectionParameterRequest"},
- {13, nullptr, "Unknown13"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-class IBtmSystemCore final : public ServiceFramework<IBtmSystemCore> {
-public:
- explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"},
- {1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"},
- {2, nullptr, "ClearGamepadPairingDatabase"},
- {3, nullptr, "GetPairedGamepadCount"},
- {4, nullptr, "EnableRadio"},
- {5, nullptr, "DisableRadio"},
- {6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"},
- {7, nullptr, "AcquireRadioEvent"},
- {8, nullptr, "AcquireGamepadPairingEvent"},
- {9, nullptr, "IsGamepadPairingStarted"},
- {10, nullptr, "StartAudioDeviceDiscovery"},
- {11, nullptr, "StopAudioDeviceDiscovery"},
- {12, nullptr, "IsDiscoveryingAudioDevice"},
- {13, nullptr, "GetDiscoveredAudioDevice"},
- {14, nullptr, "AcquireAudioDeviceConnectionEvent"},
- {15, nullptr, "ConnectAudioDevice"},
- {16, nullptr, "IsConnectingAudioDevice"},
- {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"},
- {18, nullptr, "DisconnectAudioDevice"},
- {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
- {20, &IBtmSystemCore::GetPairedAudioDevices, "GetPairedAudioDevices"},
- {21, nullptr, "RemoveAudioDevicePairing"},
- {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"},
- {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"}
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
-private:
- void IsRadioEnabled(HLERequestContext& ctx) {
- LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(true);
- }
-
- void StartGamepadPairing(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void CancelGamepadPairing(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetConnectedAudioDevices(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(0);
- }
-
- void GetPairedAudioDevices(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(0);
- }
-
- void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-};
-
-class BTM_SYS final : public ServiceFramework<BTM_SYS> {
-public:
- explicit BTM_SYS(Core::System& system_) : ServiceFramework{system_, "btm:sys"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &BTM_SYS::GetCore, "GetCore"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
-private:
- void GetCore(HLERequestContext& ctx) {
- LOG_WARNING(Service_BTM, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IBtmSystemCore>(system);
- }
-};
-
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
- server_manager->RegisterNamedService("btm", std::make_shared<BTM>(system));
- server_manager->RegisterNamedService("btm:dbg", std::make_shared<BTM_DBG>(system));
- server_manager->RegisterNamedService("btm:sys", std::make_shared<BTM_SYS>(system));
- server_manager->RegisterNamedService("btm:u", std::make_shared<BTM_USR>(system));
+ server_manager->RegisterNamedService("btm", std::make_shared<IBtm>(system));
+ server_manager->RegisterNamedService("btm:dbg", std::make_shared<IBtmDebug>(system));
+ server_manager->RegisterNamedService("btm:sys", std::make_shared<IBtmSystem>(system));
+ server_manager->RegisterNamedService("btm:u", std::make_shared<IBtmUser>(system));
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h
index a99b34364..0bf77d053 100644
--- a/src/core/hle/service/btm/btm.h
+++ b/src/core/hle/service/btm/btm.h
@@ -3,10 +3,6 @@
#pragma once
-namespace Service::SM {
-class ServiceManager;
-}
-
namespace Core {
class System;
};
diff --git a/src/core/hle/service/btm/btm_debug.cpp b/src/core/hle/service/btm/btm_debug.cpp
new file mode 100644
index 000000000..4d61d2641
--- /dev/null
+++ b/src/core/hle/service/btm/btm_debug.cpp
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/btm/btm_debug.h"
+
+namespace Service::BTM {
+
+IBtmDebug::IBtmDebug(Core::System& system_) : ServiceFramework{system_, "btm:dbg"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "AcquireDiscoveryEvent"},
+ {1, nullptr, "StartDiscovery"},
+ {2, nullptr, "CancelDiscovery"},
+ {3, nullptr, "GetDeviceProperty"},
+ {4, nullptr, "CreateBond"},
+ {5, nullptr, "CancelBond"},
+ {6, nullptr, "SetTsiMode"},
+ {7, nullptr, "GeneralTest"},
+ {8, nullptr, "HidConnect"},
+ {9, nullptr, "GeneralGet"},
+ {10, nullptr, "GetGattClientDisconnectionReason"},
+ {11, nullptr, "GetBleConnectionParameter"},
+ {12, nullptr, "GetBleConnectionParameterRequest"},
+ {13, nullptr, "Unknown13"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IBtmDebug::~IBtmDebug() = default;
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_debug.h b/src/core/hle/service/btm/btm_debug.h
new file mode 100644
index 000000000..bf4f7e14f
--- /dev/null
+++ b/src/core/hle/service/btm/btm_debug.h
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::BTM {
+
+class IBtmDebug final : public ServiceFramework<IBtmDebug> {
+public:
+ explicit IBtmDebug(Core::System& system_);
+ ~IBtmDebug() override;
+};
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_system.cpp b/src/core/hle/service/btm/btm_system.cpp
new file mode 100644
index 000000000..99718a7b0
--- /dev/null
+++ b/src/core/hle/service/btm/btm_system.cpp
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/logging/log.h"
+#include "core/hle/service/btm/btm_system.h"
+#include "core/hle/service/btm/btm_system_core.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/service.h"
+
+namespace Service::BTM {
+
+IBtmSystem::IBtmSystem(Core::System& system_) : ServiceFramework{system_, "btm:sys"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&IBtmSystem::GetCore>, "GetCore"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IBtmSystem::~IBtmSystem() = default;
+
+Result IBtmSystem::GetCore(OutInterface<IBtmSystemCore> out_interface) {
+ LOG_WARNING(Service_BTM, "called");
+
+ *out_interface = std::make_shared<IBtmSystemCore>(system);
+ R_SUCCEED();
+}
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_system.h b/src/core/hle/service/btm/btm_system.h
new file mode 100644
index 000000000..fe1c6dbd7
--- /dev/null
+++ b/src/core/hle/service/btm/btm_system.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::BTM {
+class IBtmSystemCore;
+
+class IBtmSystem final : public ServiceFramework<IBtmSystem> {
+public:
+ explicit IBtmSystem(Core::System& system_);
+ ~IBtmSystem() override;
+
+private:
+ Result GetCore(OutInterface<IBtmSystemCore> out_interface);
+};
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_system_core.cpp b/src/core/hle/service/btm/btm_system_core.cpp
new file mode 100644
index 000000000..4bc8a9e8b
--- /dev/null
+++ b/src/core/hle/service/btm/btm_system_core.cpp
@@ -0,0 +1,127 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/logging/log.h"
+#include "core/hle/service/btm/btm_system_core.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/set/system_settings_server.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::BTM {
+
+IBtmSystemCore::IBtmSystemCore(Core::System& system_)
+ : ServiceFramework{system_, "IBtmSystemCore"}, service_context{system_, "IBtmSystemCore"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&IBtmSystemCore::StartGamepadPairing>, "StartGamepadPairing"},
+ {1, C<&IBtmSystemCore::CancelGamepadPairing>, "CancelGamepadPairing"},
+ {2, nullptr, "ClearGamepadPairingDatabase"},
+ {3, nullptr, "GetPairedGamepadCount"},
+ {4, C<&IBtmSystemCore::EnableRadio>, "EnableRadio"},
+ {5, C<&IBtmSystemCore::DisableRadio>, "DisableRadio"},
+ {6, C<&IBtmSystemCore::IsRadioEnabled>, "IsRadioEnabled"},
+ {7, C<&IBtmSystemCore::AcquireRadioEvent>, "AcquireRadioEvent"},
+ {8, nullptr, "AcquireGamepadPairingEvent"},
+ {9, nullptr, "IsGamepadPairingStarted"},
+ {10, nullptr, "StartAudioDeviceDiscovery"},
+ {11, nullptr, "StopAudioDeviceDiscovery"},
+ {12, nullptr, "IsDiscoveryingAudioDevice"},
+ {13, nullptr, "GetDiscoveredAudioDevice"},
+ {14, C<&IBtmSystemCore::AcquireAudioDeviceConnectionEvent>, "AcquireAudioDeviceConnectionEvent"},
+ {15, nullptr, "ConnectAudioDevice"},
+ {16, nullptr, "IsConnectingAudioDevice"},
+ {17, C<&IBtmSystemCore::GetConnectedAudioDevices>, "GetConnectedAudioDevices"},
+ {18, nullptr, "DisconnectAudioDevice"},
+ {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
+ {20, C<&IBtmSystemCore::GetPairedAudioDevices>, "GetPairedAudioDevices"},
+ {21, nullptr, "RemoveAudioDevicePairing"},
+ {22, C<&IBtmSystemCore::RequestAudioDeviceConnectionRejection>, "RequestAudioDeviceConnectionRejection"},
+ {23, C<&IBtmSystemCore::CancelAudioDeviceConnectionRejection>, "CancelAudioDeviceConnectionRejection"}
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ radio_event = service_context.CreateEvent("IBtmSystemCore::RadioEvent");
+ audio_device_connection_event =
+ service_context.CreateEvent("IBtmSystemCore::AudioDeviceConnectionEvent");
+
+ m_set_sys =
+ system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
+}
+
+IBtmSystemCore::~IBtmSystemCore() {
+ service_context.CloseEvent(radio_event);
+ service_context.CloseEvent(audio_device_connection_event);
+}
+
+Result IBtmSystemCore::StartGamepadPairing() {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::CancelGamepadPairing() {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::EnableRadio() {
+ LOG_DEBUG(Service_BTM, "called");
+
+ R_RETURN(m_set_sys->SetBluetoothEnableFlag(true));
+}
+Result IBtmSystemCore::DisableRadio() {
+ LOG_DEBUG(Service_BTM, "called");
+
+ R_RETURN(m_set_sys->SetBluetoothEnableFlag(false));
+}
+
+Result IBtmSystemCore::IsRadioEnabled(Out<bool> out_is_enabled) {
+ LOG_DEBUG(Service_BTM, "called");
+
+ R_RETURN(m_set_sys->GetBluetoothEnableFlag(out_is_enabled));
+}
+
+Result IBtmSystemCore::AcquireRadioEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_is_valid = true;
+ *out_event = &radio_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::AcquireAudioDeviceConnectionEvent(
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_event = &audio_device_connection_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::GetConnectedAudioDevices(
+ Out<s32> out_count, OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_count = 0;
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::GetPairedAudioDevices(
+ Out<s32> out_count, OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_count = 0;
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::RequestAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
+ R_SUCCEED();
+}
+
+Result IBtmSystemCore::CancelAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
+ R_SUCCEED();
+}
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_system_core.h b/src/core/hle/service/btm/btm_system_core.h
new file mode 100644
index 000000000..06498b21e
--- /dev/null
+++ b/src/core/hle/service/btm/btm_system_core.h
@@ -0,0 +1,60 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Core {
+class System;
+}
+
+namespace Service::Set {
+class ISystemSettingsServer;
+}
+
+namespace Service::BTM {
+
+class IBtmSystemCore final : public ServiceFramework<IBtmSystemCore> {
+public:
+ explicit IBtmSystemCore(Core::System& system_);
+ ~IBtmSystemCore() override;
+
+private:
+ Result StartGamepadPairing();
+ Result CancelGamepadPairing();
+ Result EnableRadio();
+ Result DisableRadio();
+ Result IsRadioEnabled(Out<bool> out_is_enabled);
+
+ Result AcquireRadioEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Result AcquireAudioDeviceConnectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Result GetConnectedAudioDevices(
+ Out<s32> out_count,
+ OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices);
+
+ Result GetPairedAudioDevices(
+ Out<s32> out_count,
+ OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices);
+
+ Result RequestAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid);
+ Result CancelAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid);
+
+ KernelHelpers::ServiceContext service_context;
+
+ Kernel::KEvent* radio_event;
+ Kernel::KEvent* audio_device_connection_event;
+ std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
+};
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_user.cpp b/src/core/hle/service/btm/btm_user.cpp
new file mode 100644
index 000000000..d2e228f8d
--- /dev/null
+++ b/src/core/hle/service/btm/btm_user.cpp
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/logging/log.h"
+#include "core/hle/service/btm/btm_user.h"
+#include "core/hle/service/btm/btm_user_core.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::BTM {
+
+IBtmUser::IBtmUser(Core::System& system_) : ServiceFramework{system_, "btm:u"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&IBtmUser::GetCore>, "GetCore"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IBtmUser::~IBtmUser() = default;
+
+Result IBtmUser::GetCore(OutInterface<IBtmUserCore> out_interface) {
+ LOG_WARNING(Service_BTM, "called");
+
+ *out_interface = std::make_shared<IBtmUserCore>(system);
+ R_SUCCEED();
+}
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_user.h b/src/core/hle/service/btm/btm_user.h
new file mode 100644
index 000000000..d9ee5db45
--- /dev/null
+++ b/src/core/hle/service/btm/btm_user.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::BTM {
+class IBtmUserCore;
+
+class IBtmUser final : public ServiceFramework<IBtmUser> {
+public:
+ explicit IBtmUser(Core::System& system_);
+ ~IBtmUser() override;
+
+private:
+ Result GetCore(OutInterface<IBtmUserCore> out_interface);
+};
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_user_core.cpp b/src/core/hle/service/btm/btm_user_core.cpp
new file mode 100644
index 000000000..6f9fa589b
--- /dev/null
+++ b/src/core/hle/service/btm/btm_user_core.cpp
@@ -0,0 +1,103 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/service/btm/btm_user_core.h"
+#include "core/hle/service/cmif_serialization.h"
+
+namespace Service::BTM {
+
+IBtmUserCore::IBtmUserCore(Core::System& system_)
+ : ServiceFramework{system_, "IBtmUserCore"}, service_context{system_, "IBtmUserCore"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, C<&IBtmUserCore::AcquireBleScanEvent>, "AcquireBleScanEvent"},
+ {1, nullptr, "GetBleScanFilterParameter"},
+ {2, nullptr, "GetBleScanFilterParameter2"},
+ {3, nullptr, "StartBleScanForGeneral"},
+ {4, nullptr, "StopBleScanForGeneral"},
+ {5, nullptr, "GetBleScanResultsForGeneral"},
+ {6, nullptr, "StartBleScanForPaired"},
+ {7, nullptr, "StopBleScanForPaired"},
+ {8, nullptr, "StartBleScanForSmartDevice"},
+ {9, nullptr, "StopBleScanForSmartDevice"},
+ {10, nullptr, "GetBleScanResultsForSmartDevice"},
+ {17, C<&IBtmUserCore::AcquireBleConnectionEvent>, "AcquireBleConnectionEvent"},
+ {18, nullptr, "BleConnect"},
+ {19, nullptr, "BleDisconnect"},
+ {20, nullptr, "BleGetConnectionState"},
+ {21, nullptr, "AcquireBlePairingEvent"},
+ {22, nullptr, "BlePairDevice"},
+ {23, nullptr, "BleUnPairDevice"},
+ {24, nullptr, "BleUnPairDevice2"},
+ {25, nullptr, "BleGetPairedDevices"},
+ {26, C<&IBtmUserCore::AcquireBleServiceDiscoveryEvent>, "AcquireBleServiceDiscoveryEvent"},
+ {27, nullptr, "GetGattServices"},
+ {28, nullptr, "GetGattService"},
+ {29, nullptr, "GetGattIncludedServices"},
+ {30, nullptr, "GetBelongingGattService"},
+ {31, nullptr, "GetGattCharacteristics"},
+ {32, nullptr, "GetGattDescriptors"},
+ {33, C<&IBtmUserCore::AcquireBleMtuConfigEvent>, "AcquireBleMtuConfigEvent"},
+ {34, nullptr, "ConfigureBleMtu"},
+ {35, nullptr, "GetBleMtu"},
+ {36, nullptr, "RegisterBleGattDataPath"},
+ {37, nullptr, "UnregisterBleGattDataPath"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+
+ scan_event = service_context.CreateEvent("IBtmUserCore:ScanEvent");
+ connection_event = service_context.CreateEvent("IBtmUserCore:ConnectionEvent");
+ service_discovery_event = service_context.CreateEvent("IBtmUserCore:DiscoveryEvent");
+ config_event = service_context.CreateEvent("IBtmUserCore:ConfigEvent");
+}
+
+IBtmUserCore::~IBtmUserCore() {
+ service_context.CloseEvent(scan_event);
+ service_context.CloseEvent(connection_event);
+ service_context.CloseEvent(service_discovery_event);
+ service_context.CloseEvent(config_event);
+}
+
+Result IBtmUserCore::AcquireBleScanEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_is_valid = true;
+ *out_event = &scan_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+Result IBtmUserCore::AcquireBleConnectionEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_is_valid = true;
+ *out_event = &connection_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+Result IBtmUserCore::AcquireBleServiceDiscoveryEvent(
+ Out<bool> out_is_valid, OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_is_valid = true;
+ *out_event = &service_discovery_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+Result IBtmUserCore::AcquireBleMtuConfigEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ *out_is_valid = true;
+ *out_event = &config_event->GetReadableEvent();
+ R_SUCCEED();
+}
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm_user_core.h b/src/core/hle/service/btm/btm_user_core.h
new file mode 100644
index 000000000..dc0a22e81
--- /dev/null
+++ b/src/core/hle/service/btm/btm_user_core.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Core {
+class System;
+}
+
+namespace Service::BTM {
+
+class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
+public:
+ explicit IBtmUserCore(Core::System& system_);
+ ~IBtmUserCore() override;
+
+private:
+ Result AcquireBleScanEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Result AcquireBleConnectionEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Result AcquireBleServiceDiscoveryEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ Result AcquireBleMtuConfigEvent(Out<bool> out_is_valid,
+ OutCopyHandle<Kernel::KReadableEvent> out_event);
+
+ KernelHelpers::ServiceContext service_context;
+
+ Kernel::KEvent* scan_event;
+ Kernel::KEvent* connection_event;
+ Kernel::KEvent* service_discovery_event;
+ Kernel::KEvent* config_event;
+};
+
+} // namespace Service::BTM
diff --git a/src/core/hle/service/ns/application_manager_interface.cpp b/src/core/hle/service/ns/application_manager_interface.cpp
index 2e3a44c0d..7a91727f9 100644
--- a/src/core/hle/service/ns/application_manager_interface.cpp
+++ b/src/core/hle/service/ns/application_manager_interface.cpp
@@ -436,14 +436,14 @@ Result IApplicationManagerInterface::GetApplicationViewWithPromotionInfo(
Result IApplicationManagerInterface::GetApplicationRightsOnClient(
OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
- Common::UUID account_id, u32 flags, u64 application_id) {
+ u32 flags, u64 application_id, Uid account_id) {
LOG_WARNING(Service_NS, "(STUBBED) called, flags={}, application_id={:016X}, account_id={}",
- flags, application_id, account_id.FormattedString());
+ flags, application_id, account_id.uuid.FormattedString());
if (!out_rights.empty()) {
ApplicationRightsOnClient rights{};
rights.application_id = application_id;
- rights.uid = account_id;
+ rights.uid = account_id.uuid;
rights.flags = 0;
rights.flags2 = 0;
diff --git a/src/core/hle/service/ns/application_manager_interface.h b/src/core/hle/service/ns/application_manager_interface.h
index 350ec37ce..f33d269b3 100644
--- a/src/core/hle/service/ns/application_manager_interface.h
+++ b/src/core/hle/service/ns/application_manager_interface.h
@@ -37,7 +37,7 @@ public:
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
Result GetApplicationRightsOnClient(
OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
- Common::UUID account_id, u32 flags, u64 application_id);
+ u32 flags, u64 application_id, Uid account_id);
Result CheckSdCardMountStatus();
Result GetSdCardMountStatusChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id);
diff --git a/src/core/hle/service/ns/ns_types.h b/src/core/hle/service/ns/ns_types.h
index 38421b0f4..2dd664c4e 100644
--- a/src/core/hle/service/ns/ns_types.h
+++ b/src/core/hle/service/ns/ns_types.h
@@ -108,4 +108,9 @@ struct ContentPath {
};
static_assert(sizeof(ContentPath) == 0x10, "ContentPath has incorrect size.");
+struct Uid {
+ alignas(8) Common::UUID uuid;
+};
+static_assert(sizeof(Uid) == 0x10, "Uid has incorrect size.");
+
} // namespace Service::NS
diff --git a/src/core/hle/service/ns/query_service.cpp b/src/core/hle/service/ns/query_service.cpp
index 946b7fa23..138400541 100644
--- a/src/core/hle/service/ns/query_service.cpp
+++ b/src/core/hle/service/ns/query_service.cpp
@@ -41,8 +41,7 @@ IQueryService::IQueryService(Core::System& system_) : ServiceFramework{system_,
IQueryService::~IQueryService() = default;
Result IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId(
- Out<PlayStatistics> out_play_statistics, bool unknown, Common::UUID account_id,
- u64 application_id) {
+ Out<PlayStatistics> out_play_statistics, bool unknown, u64 application_id, Uid account_id) {
// TODO(German77): Read statistics of the game
*out_play_statistics = {
.application_id = application_id,
@@ -50,7 +49,7 @@ Result IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId(
};
LOG_WARNING(Service_NS, "(STUBBED) called. unknown={}. application_id={:016X}, account_id={}",
- unknown, application_id, account_id.FormattedString());
+ unknown, application_id, account_id.uuid.FormattedString());
R_SUCCEED();
}
diff --git a/src/core/hle/service/ns/query_service.h b/src/core/hle/service/ns/query_service.h
index 6cdbfa277..c4c82b752 100644
--- a/src/core/hle/service/ns/query_service.h
+++ b/src/core/hle/service/ns/query_service.h
@@ -5,6 +5,7 @@
#include "common/uuid.h"
#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/service.h"
namespace Service::NS {
@@ -29,8 +30,7 @@ public:
private:
Result QueryPlayStatisticsByApplicationIdAndUserAccountId(
- Out<PlayStatistics> out_play_statistics, bool unknown, Common::UUID account_id,
- u64 application_id);
+ Out<PlayStatistics> out_play_statistics, bool unknown, u64 application_id, Uid account_id);
};
} // namespace Service::NS
diff --git a/src/core/hle/service/nvnflinger/display.h b/src/core/hle/service/nvnflinger/display.h
index f27cbf144..40aa59787 100644
--- a/src/core/hle/service/nvnflinger/display.h
+++ b/src/core/hle/service/nvnflinger/display.h
@@ -3,8 +3,6 @@
#pragma once
-#include <list>
-
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
#include "core/hle/service/nvnflinger/hwc_layer.h"
@@ -26,18 +24,12 @@ struct Layer {
};
struct LayerStack {
- std::list<Layer> layers;
-};
-
-struct Display {
- explicit Display(u64 id_) {
- id = id_;
- }
+ std::vector<std::shared_ptr<Layer>> layers;
- Layer* FindLayer(s32 consumer_id) {
- for (auto& layer : stack.layers) {
- if (layer.consumer_id == consumer_id) {
- return &layer;
+ std::shared_ptr<Layer> FindLayer(s32 consumer_id) {
+ for (auto& layer : layers) {
+ if (layer->consumer_id == consumer_id) {
+ return layer;
}
}
@@ -45,7 +37,13 @@ struct Display {
}
bool HasLayers() {
- return !stack.layers.empty();
+ return !layers.empty();
+ }
+};
+
+struct Display {
+ explicit Display(u64 id_) {
+ id = id_;
}
u64 id;
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp
index 02215a786..f2dfe85a9 100644
--- a/src/core/hle/service/nvnflinger/hardware_composer.cpp
+++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp
@@ -55,10 +55,10 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
// Acquire all necessary framebuffers.
for (auto& layer : display.stack.layers) {
- auto consumer_id = layer.consumer_id;
+ auto consumer_id = layer->consumer_id;
// Try to fetch the framebuffer (either new or stale).
- const auto result = this->CacheFramebufferLocked(layer, consumer_id);
+ const auto result = this->CacheFramebufferLocked(*layer, consumer_id);
// If we failed, skip this layer.
if (result == CacheStatus::NoBufferAvailable) {
@@ -75,7 +75,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
const auto& igbp_buffer = *item.graphic_buffer;
// TODO: get proper Z-index from layer
- if (layer.visible) {
+ if (layer->visible) {
composition_stack.emplace_back(HwcLayer{
.buffer_handle = igbp_buffer.BufferId(),
.offset = igbp_buffer.Offset(),
@@ -84,7 +84,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
.height = igbp_buffer.Height(),
.stride = igbp_buffer.Stride(),
.z_index = 0,
- .blending = layer.blending,
+ .blending = layer->blending,
.transform = static_cast<android::BufferTransformFlags>(item.transform),
.crop_rect = item.crop,
.acquire_fence = item.fence,
@@ -134,7 +134,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
continue;
}
- if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
+ if (const auto layer = display.stack.FindLayer(layer_id); layer != nullptr) {
// TODO: support release fence
// This is needed to prevent screen tearing
layer->buffer_item_consumer->ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
@@ -153,7 +153,7 @@ void HardwareComposer::RemoveLayerLocked(Display& display, ConsumerId consumer_i
}
// Try to release the buffer item.
- auto* const layer = display.FindLayer(consumer_id);
+ const auto layer = display.stack.FindLayer(consumer_id);
if (layer && it->second.is_acquired) {
layer->buffer_item_consumer->ReleaseBuffer(it->second.item, android::Fence::NoFence());
}
diff --git a/src/core/hle/service/nvnflinger/surface_flinger.cpp b/src/core/hle/service/nvnflinger/surface_flinger.cpp
index 41a705717..8362b65e5 100644
--- a/src/core/hle/service/nvnflinger/surface_flinger.cpp
+++ b/src/core/hle/service/nvnflinger/surface_flinger.cpp
@@ -36,7 +36,7 @@ void SurfaceFlinger::RemoveDisplay(u64 display_id) {
bool SurfaceFlinger::ComposeDisplay(s32* out_swap_interval, f32* out_compose_speed_scale,
u64 display_id) {
auto* const display = this->FindDisplay(display_id);
- if (!display || !display->HasLayers()) {
+ if (!display || !display->stack.HasLayers()) {
return false;
}
@@ -46,19 +46,34 @@ bool SurfaceFlinger::ComposeDisplay(s32* out_swap_interval, f32* out_compose_spe
return true;
}
-void SurfaceFlinger::AddLayerToDisplayStack(u64 display_id, s32 consumer_binder_id) {
- auto* const display = this->FindDisplay(display_id);
+void SurfaceFlinger::CreateLayer(s32 consumer_binder_id) {
auto binder = std::static_pointer_cast<android::BufferQueueConsumer>(
m_server.TryGetBinder(consumer_binder_id));
-
- if (!display || !binder) {
+ if (!binder) {
return;
}
auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(binder));
buffer_item_consumer->Connect(false);
- display->stack.layers.emplace_back(std::move(buffer_item_consumer), consumer_binder_id);
+ m_layers.layers.emplace_back(
+ std::make_shared<Layer>(std::move(buffer_item_consumer), consumer_binder_id));
+}
+
+void SurfaceFlinger::DestroyLayer(s32 consumer_binder_id) {
+ std::erase_if(m_layers.layers,
+ [&](auto& layer) { return layer->consumer_id == consumer_binder_id; });
+}
+
+void SurfaceFlinger::AddLayerToDisplayStack(u64 display_id, s32 consumer_binder_id) {
+ auto* const display = this->FindDisplay(display_id);
+ auto layer = this->FindLayer(consumer_binder_id);
+
+ if (!display || !layer) {
+ return;
+ }
+
+ display->stack.layers.emplace_back(std::move(layer));
}
void SurfaceFlinger::RemoveLayerFromDisplayStack(u64 display_id, s32 consumer_binder_id) {
@@ -69,18 +84,18 @@ void SurfaceFlinger::RemoveLayerFromDisplayStack(u64 display_id, s32 consumer_bi
m_composer.RemoveLayerLocked(*display, consumer_binder_id);
std::erase_if(display->stack.layers,
- [&](auto& layer) { return layer.consumer_id == consumer_binder_id; });
+ [&](auto& layer) { return layer->consumer_id == consumer_binder_id; });
}
void SurfaceFlinger::SetLayerVisibility(s32 consumer_binder_id, bool visible) {
- if (auto* layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
+ if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
layer->visible = visible;
return;
}
}
void SurfaceFlinger::SetLayerBlending(s32 consumer_binder_id, LayerBlending blending) {
- if (auto* layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
+ if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
layer->blending = blending;
return;
}
@@ -96,9 +111,9 @@ Display* SurfaceFlinger::FindDisplay(u64 display_id) {
return nullptr;
}
-Layer* SurfaceFlinger::FindLayer(s32 consumer_binder_id) {
- for (auto& display : m_displays) {
- if (auto* layer = display.FindLayer(consumer_binder_id); layer != nullptr) {
+std::shared_ptr<Layer> SurfaceFlinger::FindLayer(s32 consumer_binder_id) {
+ for (auto& layer : m_layers.layers) {
+ if (layer->consumer_id == consumer_binder_id) {
return layer;
}
}
diff --git a/src/core/hle/service/nvnflinger/surface_flinger.h b/src/core/hle/service/nvnflinger/surface_flinger.h
index d8c53fbda..406281c83 100644
--- a/src/core/hle/service/nvnflinger/surface_flinger.h
+++ b/src/core/hle/service/nvnflinger/surface_flinger.h
@@ -36,6 +36,9 @@ public:
void RemoveDisplay(u64 display_id);
bool ComposeDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id);
+ void CreateLayer(s32 consumer_binder_id);
+ void DestroyLayer(s32 consumer_binder_id);
+
void AddLayerToDisplayStack(u64 display_id, s32 consumer_binder_id);
void RemoveLayerFromDisplayStack(u64 display_id, s32 consumer_binder_id);
@@ -44,7 +47,7 @@ public:
private:
Display* FindDisplay(u64 display_id);
- Layer* FindLayer(s32 consumer_binder_id);
+ std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);
public:
// TODO: these don't belong here
@@ -57,6 +60,7 @@ private:
KernelHelpers::ServiceContext m_context;
std::vector<Display> m_displays;
+ LayerStack m_layers;
std::shared_ptr<Nvidia::Module> nvdrv;
s32 disp_fd;
HardwareComposer m_composer;
diff --git a/src/core/hle/service/vi/container.cpp b/src/core/hle/service/vi/container.cpp
index 310a207f1..9074f4ae0 100644
--- a/src/core/hle/service/vi/container.cpp
+++ b/src/core/hle/service/vi/container.cpp
@@ -43,11 +43,7 @@ void Container::OnTerminate() {
m_is_shut_down = true;
- m_layers.ForEachLayer([&](auto& layer) {
- if (layer.IsOpen()) {
- this->DestroyBufferQueueLocked(&layer);
- }
- });
+ m_layers.ForEachLayer([&](auto& layer) { this->DestroyLayerLocked(layer.GetId()); });
m_displays.ForEachDisplay(
[&](auto& display) { m_surface_flinger->RemoveDisplay(display.GetId()); });
@@ -161,16 +157,29 @@ Result Container::CreateLayerLocked(u64* out_layer_id, u64 display_id, u64 owner
auto* const display = m_displays.GetDisplayById(display_id);
R_UNLESS(display != nullptr, VI::ResultNotFound);
- auto* const layer = m_layers.CreateLayer(owner_aruid, display);
+ s32 consumer_binder_id, producer_binder_id;
+ m_surface_flinger->CreateBufferQueue(&consumer_binder_id, &producer_binder_id);
+
+ auto* const layer =
+ m_layers.CreateLayer(owner_aruid, display, consumer_binder_id, producer_binder_id);
R_UNLESS(layer != nullptr, VI::ResultNotFound);
+ m_surface_flinger->CreateLayer(consumer_binder_id);
+
*out_layer_id = layer->GetId();
R_SUCCEED();
}
Result Container::DestroyLayerLocked(u64 layer_id) {
- R_SUCCEED_IF(m_layers.DestroyLayer(layer_id));
- R_THROW(VI::ResultNotFound);
+ auto* const layer = m_layers.GetLayerById(layer_id);
+ R_UNLESS(layer != nullptr, VI::ResultNotFound);
+
+ m_surface_flinger->DestroyLayer(layer->GetConsumerBinderId());
+ m_surface_flinger->DestroyBufferQueue(layer->GetConsumerBinderId(),
+ layer->GetProducerBinderId());
+ m_layers.DestroyLayer(layer_id);
+
+ R_SUCCEED();
}
Result Container::OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid) {
@@ -181,7 +190,12 @@ Result Container::OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64
R_UNLESS(!layer->IsOpen(), VI::ResultOperationFailed);
R_UNLESS(layer->GetOwnerAruid() == aruid, VI::ResultPermissionDenied);
- this->CreateBufferQueueLocked(layer);
+ layer->Open();
+
+ if (auto* display = layer->GetDisplay(); display != nullptr) {
+ m_surface_flinger->AddLayerToDisplayStack(display->GetId(), layer->GetConsumerBinderId());
+ }
+
*out_producer_binder_id = layer->GetProducerBinderId();
R_SUCCEED();
@@ -192,30 +206,14 @@ Result Container::CloseLayerLocked(u64 layer_id) {
R_UNLESS(layer != nullptr, VI::ResultNotFound);
R_UNLESS(layer->IsOpen(), VI::ResultOperationFailed);
- this->DestroyBufferQueueLocked(layer);
-
- R_SUCCEED();
-}
-
-void Container::CreateBufferQueueLocked(Layer* layer) {
- s32 consumer_binder_id, producer_binder_id;
- m_surface_flinger->CreateBufferQueue(&consumer_binder_id, &producer_binder_id);
- layer->Open(consumer_binder_id, producer_binder_id);
-
- if (auto* display = layer->GetDisplay(); display != nullptr) {
- m_surface_flinger->AddLayerToDisplayStack(display->GetId(), consumer_binder_id);
- }
-}
-
-void Container::DestroyBufferQueueLocked(Layer* layer) {
if (auto* display = layer->GetDisplay(); display != nullptr) {
m_surface_flinger->RemoveLayerFromDisplayStack(display->GetId(),
layer->GetConsumerBinderId());
}
layer->Close();
- m_surface_flinger->DestroyBufferQueue(layer->GetConsumerBinderId(),
- layer->GetProducerBinderId());
+
+ R_SUCCEED();
}
bool Container::ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale,
diff --git a/src/core/hle/service/vi/container.h b/src/core/hle/service/vi/container.h
index cd0d2ca86..5eac4d77d 100644
--- a/src/core/hle/service/vi/container.h
+++ b/src/core/hle/service/vi/container.h
@@ -72,9 +72,6 @@ private:
Result OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid);
Result CloseLayerLocked(u64 layer_id);
- void CreateBufferQueueLocked(Layer* layer);
- void DestroyBufferQueueLocked(Layer* layer);
-
public:
bool ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id);
diff --git a/src/core/hle/service/vi/layer.h b/src/core/hle/service/vi/layer.h
index b85c8df61..e4c9c9864 100644
--- a/src/core/hle/service/vi/layer.h
+++ b/src/core/hle/service/vi/layer.h
@@ -13,29 +13,31 @@ class Layer {
public:
constexpr Layer() = default;
- void Initialize(u64 id, u64 owner_aruid, Display* display) {
+ void Initialize(u64 id, u64 owner_aruid, Display* display, s32 consumer_binder_id,
+ s32 producer_binder_id) {
m_id = id;
m_owner_aruid = owner_aruid;
m_display = display;
+ m_consumer_binder_id = consumer_binder_id;
+ m_producer_binder_id = producer_binder_id;
m_is_initialized = true;
}
void Finalize() {
m_id = {};
+ m_owner_aruid = {};
m_display = {};
+ m_consumer_binder_id = {};
+ m_producer_binder_id = {};
m_is_initialized = {};
}
- void Open(s32 consumer_binder_id, s32 producer_binder_id) {
- m_consumer_binder_id = consumer_binder_id;
- m_producer_binder_id = producer_binder_id;
+ void Open() {
m_is_open = true;
}
void Close() {
- m_producer_binder_id = {};
- m_consumer_binder_id = {};
- m_is_open = {};
+ m_is_open = false;
}
u64 GetId() const {
diff --git a/src/core/hle/service/vi/layer_list.h b/src/core/hle/service/vi/layer_list.h
index 1738ede9a..4afca6f40 100644
--- a/src/core/hle/service/vi/layer_list.h
+++ b/src/core/hle/service/vi/layer_list.h
@@ -11,13 +11,15 @@ class LayerList {
public:
constexpr LayerList() = default;
- Layer* CreateLayer(u64 owner_aruid, Display* display) {
+ Layer* CreateLayer(u64 owner_aruid, Display* display, s32 consumer_binder_id,
+ s32 producer_binder_id) {
Layer* const layer = GetFreeLayer();
if (!layer) {
return nullptr;
}
- layer->Initialize(++m_next_id, owner_aruid, display);
+ layer->Initialize(++m_next_id, owner_aruid, display, consumer_binder_id,
+ producer_binder_id);
return layer;
}
diff --git a/src/core/hle/service/vi/shared_buffer_manager.cpp b/src/core/hle/service/vi/shared_buffer_manager.cpp
index 869b18961..12cba16fa 100644
--- a/src/core/hle/service/vi/shared_buffer_manager.cpp
+++ b/src/core/hle/service/vi/shared_buffer_manager.cpp
@@ -285,7 +285,7 @@ void SharedBufferManager::DestroySession(Kernel::KProcess* owner_process) {
auto& session = it->second;
// Destroy the layer.
- R_ASSERT(m_container.DestroyStrayLayer(session.layer_id));
+ m_container.DestroyStrayLayer(session.layer_id);
// Close nvmap handle.
FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd);
@@ -322,8 +322,6 @@ Result SharedBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
Result SharedBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
std::array<s32, 4>& out_slot_indexes,
s64* out_target_slot, u64 layer_id) {
- std::scoped_lock lk{m_guard};
-
// Get the producer.
std::shared_ptr<android::BufferQueueProducer> producer;
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
@@ -347,8 +345,6 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
Common::Rectangle<s32> crop_region,
u32 transform, s32 swap_interval, u64 layer_id,
s64 slot) {
- std::scoped_lock lk{m_guard};
-
// Get the producer.
std::shared_ptr<android::BufferQueueProducer> producer;
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
@@ -379,8 +375,6 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
}
Result SharedBufferManager::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
- std::scoped_lock lk{m_guard};
-
// Get the producer.
std::shared_ptr<android::BufferQueueProducer> producer;
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
@@ -394,8 +388,6 @@ Result SharedBufferManager::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
Result SharedBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event,
u64 layer_id) {
- std::scoped_lock lk{m_guard};
-
// Get the producer.
std::shared_ptr<android::BufferQueueProducer> producer;
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 0031fa5fb..3f9698d6b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -261,7 +261,9 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
case Stage::Geometry:
execution_model = spv::ExecutionModel::Geometry;
ctx.AddCapability(spv::Capability::Geometry);
- ctx.AddCapability(spv::Capability::GeometryStreams);
+ if (ctx.profile.support_geometry_streams) {
+ ctx.AddCapability(spv::Capability::GeometryStreams);
+ }
switch (ctx.runtime_info.input_topology) {
case InputTopology::Points:
ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
index 9f7b6bb4b..f60da758e 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
@@ -129,7 +129,9 @@ void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) {
ConvertDepthMode(ctx);
}
- if (stream.IsImmediate()) {
+ if (!ctx.profile.support_geometry_streams) {
+ throw NotImplementedException("Geometry streams");
+ } else if (stream.IsImmediate()) {
ctx.OpEmitStreamVertex(ctx.Def(stream));
} else {
LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
@@ -140,7 +142,9 @@ void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
}
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
- if (stream.IsImmediate()) {
+ if (!ctx.profile.support_geometry_streams) {
+ throw NotImplementedException("Geometry streams");
+ } else if (stream.IsImmediate()) {
ctx.OpEndStreamPrimitive(ctx.Def(stream));
} else {
LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index 7578d41cc..90e46bb1b 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -44,6 +44,7 @@ struct Profile {
bool support_gl_derivative_control{};
bool support_scaled_attributes{};
bool support_multi_viewport{};
+ bool support_geometry_streams{};
bool warp_size_potentially_larger_than_guest{};
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 296c90e85..ed7a5b27e 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -35,7 +35,7 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
const s64 min_spacing_critical = device_local_memory - 512_MiB;
const s64 mem_threshold = std::min(device_local_memory, TARGET_THRESHOLD);
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
- const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
+ const s64 min_vacancy_critical = (2 * mem_threshold) / 10;
minimum_memory = static_cast<u64>(
std::max(std::min(device_local_memory - min_vacancy_expected, min_spacing_expected),
DEFAULT_EXPECTED_MEMORY));
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 20f7a9702..d34b585d6 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -352,6 +352,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_native_ndc = device.IsExtDepthClipControlSupported(),
.support_scaled_attributes = !device.MustEmulateScaledFormats(),
.support_multi_viewport = device.SupportsMultiViewport(),
+ .support_geometry_streams = device.AreTransformFeedbackGeometryStreamsSupported(),
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 5b3c7aa5a..9055b1b92 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -3,6 +3,7 @@
#include "common/common_types.h"
#include "common/math_util.h"
+#include "common/settings.h"
#include "video_core/surface.h"
namespace VideoCore::Surface {
@@ -400,11 +401,20 @@ std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
return {DefaultBlockWidth(format), DefaultBlockHeight(format)};
}
-u64 EstimatedDecompressedSize(u64 base_size, PixelFormat format) {
+u64 TranscodedAstcSize(u64 base_size, PixelFormat format) {
constexpr u64 RGBA8_PIXEL_SIZE = 4;
const u64 base_block_size = static_cast<u64>(DefaultBlockWidth(format)) *
static_cast<u64>(DefaultBlockHeight(format)) * RGBA8_PIXEL_SIZE;
- return (base_size * base_block_size) / BytesPerBlock(format);
+ const u64 uncompressed_size = (base_size * base_block_size) / BytesPerBlock(format);
+
+ switch (Settings::values.astc_recompression.GetValue()) {
+ case Settings::AstcRecompression::Bc1:
+ return uncompressed_size / 8;
+ case Settings::AstcRecompression::Bc3:
+ return uncompressed_size / 4;
+ default:
+ return uncompressed_size;
+ }
}
} // namespace VideoCore::Surface
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index a5e8e2f62..ec9cd2fbf 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -517,6 +517,6 @@ size_t PixelComponentSizeBitsInteger(PixelFormat format);
std::pair<u32, u32> GetASTCBlockSize(PixelFormat format);
-u64 EstimatedDecompressedSize(u64 base_size, PixelFormat format);
+u64 TranscodedAstcSize(u64 base_size, PixelFormat format);
} // namespace VideoCore::Surface
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 01c3561c9..53b4876f2 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -55,7 +55,7 @@ TextureCache<P>::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag
const s64 min_spacing_critical = device_local_memory - 512_MiB;
const s64 mem_threshold = std::min(device_local_memory, TARGET_THRESHOLD);
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
- const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
+ const s64 min_vacancy_critical = (2 * mem_threshold) / 10;
expected_memory = static_cast<u64>(
std::max(std::min(device_local_memory - min_vacancy_expected, min_spacing_expected),
DEFAULT_EXPECTED_MEMORY));
@@ -1979,7 +1979,7 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
if ((IsPixelFormatASTC(image.info.format) &&
True(image.flags & ImageFlagBits::AcceleratedUpload)) ||
True(image.flags & ImageFlagBits::Converted)) {
- tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
+ tentative_size = TranscodedAstcSize(tentative_size, image.info.format);
}
total_used_memory += Common::AlignUp(tentative_size, 1024);
image.lru_index = lru_cache.Insert(image_id, frame_tick);
@@ -2149,7 +2149,7 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
if ((IsPixelFormatASTC(image.info.format) &&
True(image.flags & ImageFlagBits::AcceleratedUpload)) ||
True(image.flags & ImageFlagBits::Converted)) {
- tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
+ tentative_size = TranscodedAstcSize(tentative_size, image.info.format);
}
total_used_memory -= Common::AlignUp(tentative_size, 1024);
const GPUVAddr gpu_addr = image.gpu_addr;
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index d7216d349..b94924a58 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -1297,10 +1297,6 @@ u64 Device::GetDeviceMemoryUsage() const {
}
void Device::CollectPhysicalMemoryInfo() {
- // Account for resolution scaling in memory limits
- const size_t normal_memory = 6_GiB;
- const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1);
-
// Calculate limits using memory budget
VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};
budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
@@ -1331,7 +1327,15 @@ void Device::CollectPhysicalMemoryInfo() {
if (!is_integrated) {
const u64 reserve_memory = std::min<u64>(device_access_memory / 8, 1_GiB);
device_access_memory -= reserve_memory;
- device_access_memory = std::min<u64>(device_access_memory, normal_memory + scaler_memory);
+
+ if (Settings::values.vram_usage_mode.GetValue() != Settings::VramUsageMode::Aggressive) {
+ // Account for resolution scaling in memory limits
+ const size_t normal_memory = 6_GiB;
+ const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1);
+ device_access_memory =
+ std::min<u64>(device_access_memory, normal_memory + scaler_memory);
+ }
+
return;
}
const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index a2ec26697..e3abe8ddf 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -499,6 +499,11 @@ public:
return extensions.transform_feedback;
}
+ /// Returns true if the device supports VK_EXT_transform_feedback properly.
+ bool AreTransformFeedbackGeometryStreamsSupported() const {
+ return features.transform_feedback.geometryStreams;
+ }
+
/// Returns true if the device supports VK_EXT_custom_border_color.
bool IsExtCustomBorderColorSupported() const {
return extensions.custom_border_color;
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index d138b53c8..0549e8ae4 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -164,6 +164,11 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
"the emulator to decompress to an intermediate format any card supports, RGBA8.\n"
"This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but "
"negatively affecting image quality."));
+ INSERT(Settings, vram_usage_mode, tr("VRAM Usage Mode:"),
+ tr("Selects whether the emulator should prefer to conserve memory or make maximum usage "
+ "of available video memory for performance. Has no effect on integrated graphics. "
+ "Aggressive mode may severely impact the performance of other applications such as "
+ "recording software."));
INSERT(
Settings, vsync_mode, tr("VSync Mode:"),
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
@@ -315,6 +320,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),
PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),
}});
+ translations->insert({Settings::EnumMetadata<Settings::VramUsageMode>::Index(),
+ {
+ PAIR(VramUsageMode, Conservative, tr("Conservative")),
+ PAIR(VramUsageMode, Aggressive, tr("Aggressive")),
+ }});
translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
{
#ifdef HAS_OPENGL
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 236642fb9..b2ae3db52 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1604,6 +1604,7 @@ void GMainWindow::ConnectMenuEvents() {
connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder);
connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents);
connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware);
+ connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys);
connect_menu(ui->action_About, &GMainWindow::OnAbout);
}
@@ -1633,6 +1634,7 @@ void GMainWindow::UpdateMenuState() {
}
ui->action_Install_Firmware->setEnabled(!emulation_running);
+ ui->action_Install_Keys->setEnabled(!emulation_running);
for (QAction* action : applet_actions) {
action->setEnabled(is_firmware_available && !emulation_running);
@@ -4169,9 +4171,8 @@ void GMainWindow::OnInstallFirmware() {
return;
}
- QString firmware_source_location =
- QFileDialog::getExistingDirectory(this, tr("Select Dumped Firmware Source Location"),
- QString::fromStdString(""), QFileDialog::ShowDirsOnly);
+ const QString firmware_source_location = QFileDialog::getExistingDirectory(
+ this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly);
if (firmware_source_location.isEmpty()) {
return;
}
@@ -4202,8 +4203,9 @@ void GMainWindow::OnInstallFirmware() {
std::vector<std::filesystem::path> out;
const Common::FS::DirEntryCallable callback =
[&out](const std::filesystem::directory_entry& entry) {
- if (entry.path().has_extension() && entry.path().extension() == ".nca")
+ if (entry.path().has_extension() && entry.path().extension() == ".nca") {
out.emplace_back(entry.path());
+ }
return true;
};
@@ -4235,7 +4237,6 @@ void GMainWindow::OnInstallFirmware() {
auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered");
bool success = true;
- bool cancelled = false;
int i = 0;
for (const auto& firmware_src_path : out) {
i++;
@@ -4250,24 +4251,22 @@ void GMainWindow::OnInstallFirmware() {
success = false;
}
- if (QtProgressCallback(100, 20 + (int)(((float)(i) / (float)out.size()) * 70.0))) {
- success = false;
- cancelled = true;
- break;
+ if (QtProgressCallback(
+ 100, 20 + static_cast<int>(((i) / static_cast<float>(out.size())) * 70.0))) {
+ progress.close();
+ QMessageBox::warning(
+ this, tr("Firmware install failed"),
+ tr("Firmware installation cancelled, firmware may be in bad state, "
+ "restart yuzu or re-install firmware."));
+ return;
}
}
- if (!success && !cancelled) {
+ if (!success) {
progress.close();
QMessageBox::critical(this, tr("Firmware install failed"),
tr("One or more firmware files failed to copy into NAND."));
return;
- } else if (cancelled) {
- progress.close();
- QMessageBox::warning(this, tr("Firmware install failed"),
- tr("Firmware installation cancelled, firmware may be in bad state, "
- "restart yuzu or re-install firmware."));
- return;
}
// Re-scan VFS for the newly placed firmware files.
@@ -4295,6 +4294,84 @@ void GMainWindow::OnInstallFirmware() {
OnCheckFirmwareDecryption();
}
+void GMainWindow::OnInstallDecryptionKeys() {
+ // Don't do this while emulation is running.
+ if (emu_thread != nullptr && emu_thread->IsRunning()) {
+ return;
+ }
+
+ const QString key_source_location = QFileDialog::getOpenFileName(
+ this, tr("Select Dumped Keys Location"), {}, QStringLiteral("prod.keys (prod.keys)"), {},
+ QFileDialog::ReadOnly);
+ if (key_source_location.isEmpty()) {
+ return;
+ }
+
+ // Verify that it contains prod.keys, title.keys and optionally, key_retail.bin
+ LOG_INFO(Frontend, "Installing key files from {}", key_source_location.toStdString());
+
+ const std::filesystem::path prod_key_path = key_source_location.toStdString();
+ const std::filesystem::path key_source_path = prod_key_path.parent_path();
+ if (!Common::FS::IsDir(key_source_path)) {
+ return;
+ }
+
+ bool prod_keys_found = false;
+ std::vector<std::filesystem::path> source_key_files;
+
+ if (Common::FS::Exists(prod_key_path)) {
+ prod_keys_found = true;
+ source_key_files.emplace_back(prod_key_path);
+ }
+
+ if (Common::FS::Exists(key_source_path / "title.keys")) {
+ source_key_files.emplace_back(key_source_path / "title.keys");
+ }
+
+ if (Common::FS::Exists(key_source_path / "key_retail.bin")) {
+ source_key_files.emplace_back(key_source_path / "key_retail.bin");
+ }
+
+ // There should be at least prod.keys.
+ if (source_key_files.empty() || !prod_keys_found) {
+ QMessageBox::warning(this, tr("Decryption Keys install failed"),
+ tr("prod.keys is a required decryption key file."));
+ return;
+ }
+
+ const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
+ for (auto key_file : source_key_files) {
+ std::filesystem::path destination_key_file = yuzu_keys_dir / key_file.filename();
+ if (!std::filesystem::copy_file(key_file, destination_key_file,
+ std::filesystem::copy_options::overwrite_existing)) {
+ LOG_ERROR(Frontend, "Failed to copy file {} to {}", key_file.string(),
+ destination_key_file.string());
+ QMessageBox::critical(this, tr("Decryption Keys install failed"),
+ tr("One or more keys failed to copy."));
+ return;
+ }
+ }
+
+ // Reinitialize the key manager, re-read the vfs (for update/dlc files),
+ // and re-populate the game list in the UI if the user has already added
+ // game folders.
+ Core::Crypto::KeyManager::Instance().ReloadKeys();
+ system->GetFileSystemController().CreateFactories(*vfs);
+ game_list->PopulateAsync(UISettings::values.game_dirs);
+
+ if (ContentManager::AreKeysPresent()) {
+ QMessageBox::information(this, tr("Decryption Keys install succeeded"),
+ tr("Decryption Keys were successfully installed"));
+ } else {
+ QMessageBox::critical(
+ this, tr("Decryption Keys install failed"),
+ tr("Decryption Keys failed to initialize. Check that your dumping tools are "
+ "up to date and re-dump keys."));
+ }
+
+ OnCheckFirmwareDecryption();
+}
+
void GMainWindow::OnAbout() {
AboutDialog aboutDialog(this);
aboutDialog.exec();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 1f0e35c67..fce643f3f 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -381,6 +381,7 @@ private slots:
void OnOpenYuzuFolder();
void OnVerifyInstalledContents();
void OnInstallFirmware();
+ void OnInstallDecryptionKeys();
void OnAbout();
void OnToggleFilterBar();
void OnToggleStatusBar();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 6ff444a22..85dc1f2f6 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -165,8 +165,9 @@
<addaction name="separator"/>
<addaction name="action_Configure_Tas"/>
</widget>
- <addaction name="action_Verify_installed_contents"/>
+ <addaction name="action_Install_Keys"/>
<addaction name="action_Install_Firmware"/>
+ <addaction name="action_Verify_installed_contents"/>
<addaction name="separator"/>
<addaction name="menu_cabinet_applet"/>
<addaction name="action_Load_Album"/>
@@ -469,6 +470,11 @@
<string>Install Firmware</string>
</property>
</action>
+ <action name="action_Install_Keys">
+ <property name="text">
+ <string>Install Decryption Keys</string>
+ </property>
+ </action>
</widget>
<resources>
<include location="yuzu.qrc"/>