Compare commits

...

38 Commits

Author SHA1 Message Date
1374149fa5 Android #42 2023-08-17 00:57:12 +00:00
a8c4f01f6c Merge pull request #11287 from liamwhite/replaced-bytes
gdbstub: fixup replaced instruction bytes in memory reads
2023-08-15 15:36:14 +02:00
6d665a94ea Merge pull request #11256 from FearlessTobi/revert-10075
Partially Revert "Silence nifm spam"
2023-08-14 16:28:13 -07:00
bbc6b08fc7 Merge pull request #11273 from t895/setup-completion
android: Setup additions
2023-08-14 15:41:35 -07:00
fbda084acb gdbstub: fixup replaced instruction bytes in memory reads 2023-08-14 16:33:27 -04:00
2694f81462 Revert "Silence nifm spam"
This reverts commit 4da4ecb1ff.
2023-08-14 21:23:09 +02:00
d5adaeafdf Merge pull request #11271 from t895/settings-tweaks
android: Settings tweaks
2023-08-14 11:44:38 -07:00
58a4c86797 Merge pull request #11282 from ameerj/glasm-xfb
gl_graphics_pipeline: GLASM: Fix transform feedback with multiple buffers
2023-08-14 09:19:20 -04:00
35a77c3bb2 Merge pull request #11283 from ameerj/glasm-pipeline-detection
gl_graphics_pipeline: Fix GLASM storage buffer detection
2023-08-14 09:19:10 -04:00
c1016b68ae Merge pull request #11281 from liamwhite/vi-scale-mode
nvnflinger: add missing scale mode
2023-08-14 09:19:03 -04:00
b30df50076 Merge pull request #11259 from german77/hid
service: hid: Implement functions needed by QLaunch
2023-08-14 09:18:55 -04:00
5afe1367ba Merge pull request #11263 from liamwhite/my-feature-branch
vulkan_device: disable features associated with unloaded extensions
2023-08-14 09:18:47 -04:00
24700af3c2 Merge pull request #11264 from liamwhite/stray-code
ssl_backend_securetransport: remove stray .Code()
2023-08-14 09:18:32 -04:00
f9ef721ca6 gl_graphics_pipeline: Fix GLASM storage buffer detection 2023-08-13 17:06:45 -04:00
c34ed4bbd8 gl_graphics_pipeline: GLASM: Fix transform feedback with multiple buffers 2023-08-13 16:50:01 -04:00
7351884588 nvnflinger: add missing scale mode 2023-08-13 13:57:02 -04:00
242ce2a0b3 android: Page forward on setup step completion 2023-08-12 20:21:47 -04:00
8ab3685a39 android: Adjust setup fragment layout
Fixes padding issues in small and large layouts and allows viewpager to reach into system insets.
2023-08-12 17:02:59 -04:00
8bd0521b58 android: Show complete indicator during setup 2023-08-12 16:53:14 -04:00
64ea5522d3 android: Remove redundant option from slider dialog
You can already reset any setting by long pressing the settings item.
2023-08-12 15:45:27 -04:00
798a439eb1 android: Reduce opacity of non-editable settings 2023-08-12 15:42:55 -04:00
786b609151 android: Use string resource for slider value/units 2023-08-12 15:42:54 -04:00
89a2d308c3 android: Display setting value in setting list items 2023-08-12 14:38:46 -04:00
0d4bf53ad9 android: Set switch listener before assigning new value
Previously the switch could have its old listener triggered when recycled.
2023-08-12 01:00:42 -04:00
8b98c4e5a0 ssl_backend_securetransport: remove stray .Code() 2023-08-11 23:32:46 -04:00
26ff214719 Merge pull request #11219 from zeltermann/title-id-search
Allow searching by a substring of the title ID
2023-08-11 16:53:27 -04:00
640f7cd945 Merge pull request #11253 from liamwhite/i-hate-this-toolchain
general: fix apple clang build
2023-08-11 16:53:20 -04:00
7d8f748696 vulkan_device: disable features associated with unloaded extensions 2023-08-11 14:54:12 -04:00
bdd96118d1 service: hid: Implement functions needed by QLaunch 2023-08-11 10:13:21 -06:00
1ed9e8812b Allow searching by a substring of the title ID 2023-08-11 00:07:12 +07:00
9d3a293a4e Merge pull request #11093 from liamwhite/result-ergonomics
core: remove ResultVal type
2023-08-09 21:24:31 -07:00
3d6ce9dd2b Merge pull request #11247 from german77/pctl
service: pctl: Partially revert 11221
2023-08-09 21:24:06 -07:00
023b9b38cc general: fix apple clang build 2023-08-09 22:38:37 -04:00
6a43aff745 service: pctl: Partially revert 11221 2023-08-08 16:52:21 -06:00
1e394c6cdf fs: return result on null outputs 2023-08-08 16:12:04 -04:00
37b278a9a8 general: fix incorrect conversions 2023-08-08 11:09:37 -04:00
83eee1d226 ssl: remove ResultVal use 2023-08-08 11:09:37 -04:00
84cb20bc72 core: remove ResultVal type 2023-08-08 11:09:37 -04:00
81 changed files with 1355 additions and 1062 deletions

View File

@ -1,3 +1,11 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
End of merge log. You can find the original README.md below the break.
-----
<!-- <!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later SPDX-License-Identifier: GPL-2.0-or-later

View File

@ -49,6 +49,7 @@ class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List
val context = YuzuApplication.appContext val context = YuzuApplication.appContext
binding.textSettingName.text = context.getString(license.titleId) binding.textSettingName.text = context.getString(license.titleId)
binding.textSettingDescription.text = context.getString(license.descriptionId) binding.textSettingDescription.text = context.getString(license.descriptionId)
binding.textSettingValue.visibility = View.GONE
} }
} }
} }

View File

@ -5,13 +5,19 @@ package org.yuzu.yuzu_emu.adapters
import android.text.Html import android.text.Html
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import org.yuzu.yuzu_emu.databinding.PageSetupBinding import org.yuzu.yuzu_emu.databinding.PageSetupBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.SetupCallback
import org.yuzu.yuzu_emu.model.SetupPage import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.utils.ViewUtils
class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) : class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() { RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
@ -26,7 +32,7 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
holder.bind(pages[position]) holder.bind(pages[position])
inner class SetupPageViewHolder(val binding: PageSetupBinding) : inner class SetupPageViewHolder(val binding: PageSetupBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root), SetupCallback {
lateinit var page: SetupPage lateinit var page: SetupPage
init { init {
@ -35,6 +41,12 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
fun bind(page: SetupPage) { fun bind(page: SetupPage) {
this.page = page this.page = page
if (page.stepCompleted.invoke() == StepState.COMPLETE) {
binding.buttonAction.visibility = View.INVISIBLE
binding.textConfirmation.visibility = View.VISIBLE
}
binding.icon.setImageDrawable( binding.icon.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(
activity.resources, activity.resources,
@ -62,9 +74,15 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
MaterialButton.ICON_GRAVITY_END MaterialButton.ICON_GRAVITY_END
} }
setOnClickListener { setOnClickListener {
page.buttonAction.invoke() page.buttonAction.invoke(this@SetupPageViewHolder)
} }
} }
} }
override fun onStepCompleted() {
ViewUtils.hideView(binding.buttonAction, 200)
ViewUtils.showView(binding.textConfirmation, 200)
ViewModelProvider(activity)[HomeViewModel::class.java].setShouldPageForward(true)
}
} }
} }

View File

@ -207,8 +207,11 @@ class SettingsAdapter(
val sliderBinding = DialogSliderBinding.inflate(inflater) val sliderBinding = DialogSliderBinding.inflate(inflater)
textSliderValue = sliderBinding.textValue textSliderValue = sliderBinding.textValue
textSliderValue!!.text = sliderProgress.toString() textSliderValue!!.text = String.format(
sliderBinding.textUnits.text = item.units context.getString(R.string.value_with_units),
sliderProgress.toString(),
item.units
)
sliderBinding.slider.apply { sliderBinding.slider.apply {
valueFrom = item.min.toFloat() valueFrom = item.min.toFloat()
@ -216,7 +219,11 @@ class SettingsAdapter(
value = sliderProgress.toFloat() value = sliderProgress.toFloat()
addOnChangeListener { _: Slider, value: Float, _: Boolean -> addOnChangeListener { _: Slider, value: Float, _: Boolean ->
sliderProgress = value.toInt() sliderProgress = value.toInt()
textSliderValue!!.text = sliderProgress.toString() textSliderValue!!.text = String.format(
context.getString(R.string.value_with_units),
sliderProgress.toString(),
item.units
)
} }
} }
@ -225,10 +232,6 @@ class SettingsAdapter(
.setView(sliderBinding.root) .setView(sliderBinding.root)
.setPositiveButton(android.R.string.ok, this) .setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, defaultCancelListener) .setNegativeButton(android.R.string.cancel, defaultCancelListener)
.setNeutralButton(R.string.slider_default) { dialog: DialogInterface, which: Int ->
sliderBinding.slider.value = item.defaultValue!!.toFloat()
onClick(dialog, which)
}
.show() .show()
} }

View File

@ -25,12 +25,17 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE binding.textSettingDescription.visibility = View.VISIBLE
} else { } else {
val epochTime = setting.value.toLong() binding.textSettingDescription.visibility = View.GONE
val instant = Instant.ofEpochMilli(epochTime * 1000)
val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
binding.textSettingDescription.text = dateFormatter.format(zonedTime)
} }
binding.textSettingValue.visibility = View.VISIBLE
val epochTime = setting.value.toLong()
val instant = Instant.ofEpochMilli(epochTime * 1000)
val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
binding.textSettingValue.text = dateFormatter.format(zonedTime)
setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@ -23,6 +23,9 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
} else { } else {
binding.textSettingDescription.visibility = View.GONE binding.textSettingDescription.visibility = View.GONE
} }
binding.textSettingValue.visibility = View.GONE
setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@ -5,6 +5,8 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
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.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
@ -33,4 +35,18 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings
abstract override fun onClick(clicked: View) abstract override fun onClick(clicked: View)
abstract override fun onLongClick(clicked: View): Boolean abstract override fun onLongClick(clicked: View): Boolean
fun setStyle(isEditable: Boolean, binding: ListItemSettingBinding) {
val opacity = if (isEditable) 1.0f else 0.5f
binding.textSettingName.alpha = opacity
binding.textSettingDescription.alpha = opacity
binding.textSettingValue.alpha = opacity
}
fun setStyle(isEditable: Boolean, binding: ListItemSettingSwitchBinding) {
binding.switchWidget.isEnabled = isEditable
val opacity = if (isEditable) 1.0f else 0.5f
binding.textSettingName.alpha = opacity
binding.textSettingDescription.alpha = opacity
}
} }

View File

@ -17,28 +17,33 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
override fun bind(item: SettingsItem) { override fun bind(item: SettingsItem) {
setting = item setting = item
binding.textSettingName.setText(item.nameId) binding.textSettingName.setText(item.nameId)
binding.textSettingDescription.visibility = View.VISIBLE
if (item.descriptionId != 0) { if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)
} else if (item is SingleChoiceSetting) { binding.textSettingDescription.visibility = View.VISIBLE
val resMgr = binding.textSettingDescription.context.resources } else {
binding.textSettingDescription.visibility = View.GONE
}
binding.textSettingValue.visibility = View.VISIBLE
if (item is SingleChoiceSetting) {
val resMgr = binding.textSettingValue.context.resources
val values = resMgr.getIntArray(item.valuesId) val values = resMgr.getIntArray(item.valuesId)
for (i in values.indices) { for (i in values.indices) {
if (values[i] == item.selectedValue) { if (values[i] == item.selectedValue) {
binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i] binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i]
return break
} }
} }
} else if (item is StringSingleChoiceSetting) { } else if (item is StringSingleChoiceSetting) {
for (i in item.values!!.indices) { for (i in item.values!!.indices) {
if (item.values[i] == item.selectedValue) { if (item.values[i] == item.selectedValue) {
binding.textSettingDescription.text = item.choices[i] binding.textSettingValue.text = item.choices[i]
return break
} }
} }
} else {
binding.textSettingDescription.visibility = View.GONE
} }
setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View import android.view.View
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 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.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
@ -22,6 +23,14 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
} else { } else {
binding.textSettingDescription.visibility = View.GONE binding.textSettingDescription.visibility = View.GONE
} }
binding.textSettingValue.visibility = View.VISIBLE
binding.textSettingValue.text = String.format(
binding.textSettingValue.context.getString(R.string.value_with_units),
setting.selectedValue,
setting.units
)
setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@ -22,6 +22,7 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
} else { } else {
binding.textSettingDescription.visibility = View.GONE binding.textSettingDescription.visibility = View.GONE
} }
binding.textSettingValue.visibility = View.GONE
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@ -25,12 +25,12 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
binding.textSettingDescription.text = "" binding.textSettingDescription.text = ""
binding.textSettingDescription.visibility = View.GONE binding.textSettingDescription.visibility = View.GONE
} }
binding.switchWidget.isChecked = setting.isChecked
binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked) adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked)
} }
binding.switchWidget.isChecked = setting.isChecked
binding.switchWidget.isEnabled = setting.isEditable setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@ -19,6 +19,7 @@ import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController import androidx.navigation.findNavController
@ -32,10 +33,13 @@ import org.yuzu.yuzu_emu.adapters.SetupAdapter
import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.SetupCallback
import org.yuzu.yuzu_emu.model.SetupPage import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.GameHelper import org.yuzu.yuzu_emu.utils.GameHelper
import org.yuzu.yuzu_emu.utils.ViewUtils
class SetupFragment : Fragment() { class SetupFragment : Fragment() {
private var _binding: FragmentSetupBinding? = null private var _binding: FragmentSetupBinding? = null
@ -112,14 +116,22 @@ class SetupFragment : Fragment() {
0, 0,
false, false,
R.string.give_permission, R.string.give_permission,
{ permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) }, {
notificationCallback = it
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
},
true, true,
R.string.notification_warning, R.string.notification_warning,
R.string.notification_warning_description, R.string.notification_warning_description,
0, 0,
{ {
NotificationManagerCompat.from(requireContext()) if (NotificationManagerCompat.from(requireContext())
.areNotificationsEnabled() .areNotificationsEnabled()
) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
}
} }
) )
) )
@ -133,12 +145,22 @@ class SetupFragment : Fragment() {
R.drawable.ic_add, R.drawable.ic_add,
true, true,
R.string.select_keys, R.string.select_keys,
{ mainActivity.getProdKey.launch(arrayOf("*/*")) }, {
keyCallback = it
getProdKey.launch(arrayOf("*/*"))
},
true, true,
R.string.install_prod_keys_warning, R.string.install_prod_keys_warning,
R.string.install_prod_keys_warning_description, R.string.install_prod_keys_warning_description,
R.string.install_prod_keys_warning_help, R.string.install_prod_keys_warning_help,
{ File(DirectoryInitialization.userDirectory + "/keys/prod.keys").exists() } {
val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
if (file.exists()) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
}
}
) )
) )
add( add(
@ -150,9 +172,8 @@ class SetupFragment : Fragment() {
true, true,
R.string.add_games, R.string.add_games,
{ {
mainActivity.getGamesDirectory.launch( gamesDirCallback = it
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
)
}, },
true, true,
R.string.add_games_warning, R.string.add_games_warning,
@ -163,7 +184,11 @@ class SetupFragment : Fragment() {
PreferenceManager.getDefaultSharedPreferences( PreferenceManager.getDefaultSharedPreferences(
YuzuApplication.appContext YuzuApplication.appContext
) )
preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty() if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
}
} }
) )
) )
@ -181,6 +206,13 @@ class SetupFragment : Fragment() {
) )
} }
homeViewModel.shouldPageForward.observe(viewLifecycleOwner) {
if (it) {
pageForward()
homeViewModel.setShouldPageForward(false)
}
}
binding.viewPager2.apply { binding.viewPager2.apply {
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages) adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
offscreenPageLimit = 2 offscreenPageLimit = 2
@ -194,15 +226,15 @@ class SetupFragment : Fragment() {
super.onPageSelected(position) super.onPageSelected(position)
if (position == 1 && previousPosition == 0) { if (position == 1 && previousPosition == 0) {
showView(binding.buttonNext) ViewUtils.showView(binding.buttonNext)
showView(binding.buttonBack) ViewUtils.showView(binding.buttonBack)
} else if (position == 0 && previousPosition == 1) { } else if (position == 0 && previousPosition == 1) {
hideView(binding.buttonBack) ViewUtils.hideView(binding.buttonBack)
hideView(binding.buttonNext) ViewUtils.hideView(binding.buttonNext)
} else if (position == pages.size - 1 && previousPosition == pages.size - 2) { } else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
hideView(binding.buttonNext) ViewUtils.hideView(binding.buttonNext)
} else if (position == pages.size - 2 && previousPosition == pages.size - 1) { } else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
showView(binding.buttonNext) ViewUtils.showView(binding.buttonNext)
} }
previousPosition = position previousPosition = position
@ -215,7 +247,8 @@ class SetupFragment : Fragment() {
// Checks if the user has completed the task on the current page // Checks if the user has completed the task on the current page
if (currentPage.hasWarning) { if (currentPage.hasWarning) {
if (currentPage.taskCompleted.invoke()) { val stepState = currentPage.stepCompleted.invoke()
if (stepState != StepState.INCOMPLETE) {
pageForward() pageForward()
return@setOnClickListener return@setOnClickListener
} }
@ -264,9 +297,15 @@ class SetupFragment : Fragment() {
_binding = null _binding = null
} }
private lateinit var notificationCallback: SetupCallback
@RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private val permissionLauncher = private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (it) {
notificationCallback.onStepCompleted()
}
if (!it && if (!it &&
!shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS) !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
) { ) {
@ -277,6 +316,27 @@ class SetupFragment : Fragment() {
} }
} }
private lateinit var keyCallback: SetupCallback
val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
if (mainActivity.processKey(result)) {
keyCallback.onStepCompleted()
}
}
}
private lateinit var gamesDirCallback: SetupCallback
val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result != null) {
mainActivity.processGamesDir(result)
gamesDirCallback.onStepCompleted()
}
}
private fun finishSetup() { private fun finishSetup() {
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
.putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false) .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
@ -284,33 +344,6 @@ class SetupFragment : Fragment() {
mainActivity.finishSetup(binding.root.findNavController()) mainActivity.finishSetup(binding.root.findNavController())
} }
private fun showView(view: View) {
view.apply {
alpha = 0f
visibility = View.VISIBLE
isClickable = true
}.animate().apply {
duration = 300
alpha(1f)
}.start()
}
private fun hideView(view: View) {
if (view.visibility == View.INVISIBLE) {
return
}
view.apply {
alpha = 1f
isClickable = false
}.animate().apply {
duration = 300
alpha(0f)
}.withEndAction {
view.visibility = View.INVISIBLE
}
}
fun pageForward() { fun pageForward() {
binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1 binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1
} }
@ -326,15 +359,29 @@ class SetupFragment : Fragment() {
private fun setInsets() = private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root
) { view: View, windowInsets: WindowInsetsCompat -> ) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
view.setPadding(
barInsets.left + cutoutInsets.left, val leftPadding = barInsets.left + cutoutInsets.left
barInsets.top + cutoutInsets.top, val topPadding = barInsets.top + cutoutInsets.top
barInsets.right + cutoutInsets.right, val rightPadding = barInsets.right + cutoutInsets.right
barInsets.bottom + cutoutInsets.bottom val bottomPadding = barInsets.bottom + cutoutInsets.bottom
)
if (resources.getBoolean(R.bool.small_layout)) {
binding.viewPager2
.updatePadding(left = leftPadding, top = topPadding, right = rightPadding)
binding.constraintButtons
.updatePadding(left = leftPadding, right = rightPadding, bottom = bottomPadding)
} else {
binding.viewPager2.updatePadding(top = topPadding, bottom = bottomPadding)
binding.constraintButtons
.updatePadding(
left = leftPadding,
right = rightPadding,
bottom = bottomPadding
)
}
windowInsets windowInsets
} }
} }

View File

@ -14,6 +14,9 @@ class HomeViewModel : ViewModel() {
private val _statusBarShadeVisible = MutableLiveData(true) private val _statusBarShadeVisible = MutableLiveData(true)
val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible
private val _shouldPageForward = MutableLiveData(false)
val shouldPageForward: LiveData<Boolean> get() = _shouldPageForward
var navigatedToSetup = false var navigatedToSetup = false
init { init {
@ -33,4 +36,8 @@ class HomeViewModel : ViewModel() {
} }
_statusBarShadeVisible.value = visible _statusBarShadeVisible.value = visible
} }
fun setShouldPageForward(pageForward: Boolean) {
_shouldPageForward.value = pageForward
}
} }

View File

@ -10,10 +10,20 @@ data class SetupPage(
val buttonIconId: Int, val buttonIconId: Int,
val leftAlignedIcon: Boolean, val leftAlignedIcon: Boolean,
val buttonTextId: Int, val buttonTextId: Int,
val buttonAction: () -> Unit, val buttonAction: (callback: SetupCallback) -> Unit,
val hasWarning: Boolean, val hasWarning: Boolean,
val warningTitleId: Int = 0, val warningTitleId: Int = 0,
val warningDescriptionId: Int = 0, val warningDescriptionId: Int = 0,
val warningHelpLinkId: Int = 0, val warningHelpLinkId: Int = 0,
val taskCompleted: () -> Boolean = { true } val stepCompleted: () -> StepState = { StepState.UNDEFINED }
) )
interface SetupCallback {
fun onStepCompleted()
}
enum class StepState {
COMPLETE,
INCOMPLETE,
UNDEFINED
}

View File

@ -266,74 +266,81 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getGamesDirectory = val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result -> registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result == null) { if (result != null) {
return@registerForActivityResult processGamesDir(result)
} }
contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
// When a new directory is picked, we currently will reset the existing games
// database. This effectively means that only one game directory is supported.
PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
.putString(GameHelper.KEY_GAME_PATH, result.toString())
.apply()
Toast.makeText(
applicationContext,
R.string.games_dir_selected,
Toast.LENGTH_LONG
).show()
gamesViewModel.reloadGames(true)
} }
fun processGamesDir(result: Uri) {
contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
// When a new directory is picked, we currently will reset the existing games
// database. This effectively means that only one game directory is supported.
PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
.putString(GameHelper.KEY_GAME_PATH, result.toString())
.apply()
Toast.makeText(
applicationContext,
R.string.games_dir_selected,
Toast.LENGTH_LONG
).show()
gamesViewModel.reloadGames(true)
}
val getProdKey = val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) { if (result != null) {
return@registerForActivityResult processKey(result)
}
if (FileUtil.getExtension(result) != "keys") {
MessageDialogFragment.newInstance(
R.string.reading_keys_failure,
R.string.install_prod_keys_failure_extension_description
).show(supportFragmentManager, MessageDialogFragment.TAG)
return@registerForActivityResult
}
contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage(
applicationContext,
result,
dstPath,
"prod.keys"
)
) {
if (NativeLibrary.reloadKeys()) {
Toast.makeText(
applicationContext,
R.string.install_keys_success,
Toast.LENGTH_SHORT
).show()
gamesViewModel.reloadGames(true)
} else {
MessageDialogFragment.newInstance(
R.string.invalid_keys_error,
R.string.install_keys_failure_description,
R.string.dumping_keys_quickstart_link
).show(supportFragmentManager, MessageDialogFragment.TAG)
}
} }
} }
fun processKey(result: Uri): Boolean {
if (FileUtil.getExtension(result) != "keys") {
MessageDialogFragment.newInstance(
R.string.reading_keys_failure,
R.string.install_prod_keys_failure_extension_description
).show(supportFragmentManager, MessageDialogFragment.TAG)
return false
}
contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage(
applicationContext,
result,
dstPath,
"prod.keys"
)
) {
if (NativeLibrary.reloadKeys()) {
Toast.makeText(
applicationContext,
R.string.install_keys_success,
Toast.LENGTH_SHORT
).show()
gamesViewModel.reloadGames(true)
return true
} else {
MessageDialogFragment.newInstance(
R.string.invalid_keys_error,
R.string.install_keys_failure_description,
R.string.dumping_keys_quickstart_link
).show(supportFragmentManager, MessageDialogFragment.TAG)
return false
}
}
return false
}
val getFirmware = val getFirmware =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) { if (result == null) {

View File

@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import android.view.View
object ViewUtils {
fun showView(view: View, length: Long = 300) {
view.apply {
alpha = 0f
visibility = View.VISIBLE
isClickable = true
}.animate().apply {
duration = length
alpha(1f)
}.start()
}
fun hideView(view: View, length: Long = 300) {
if (view.visibility == View.INVISIBLE) {
return
}
view.apply {
alpha = 1f
isClickable = false
}.animate().apply {
duration = length
alpha(0f)
}.withEndAction {
view.visibility = View.INVISIBLE
}.start()
}
}

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/setup_root" android:id="@+id/setup_root"
@ -8,33 +8,39 @@
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2" android:id="@+id/viewPager2"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent" android:layout_alignParentTop="true"
app:layout_constraintEnd_toEndOf="parent" android:layout_alignParentBottom="true"
app:layout_constraintStart_toStartOf="parent" android:clipToPadding="false" />
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton <androidx.constraintlayout.widget.ConstraintLayout
style="@style/Widget.Material3.Button.TextButton" android:id="@+id/constraint_buttons"
android:id="@+id/button_next" android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:layout_alignParentBottom="true"
android:text="@string/next" android:layout_margin="8dp">
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/button_back" android:id="@+id/button_next"
style="@style/Widget.Material3.Button.TextButton" style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:text="@string/next"
android:text="@string/back" android:visibility="invisible"
android:visibility="invisible" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> <com.google.android.material.button.MaterialButton
android:id="@+id/button_back"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/back"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>

View File

@ -21,45 +21,76 @@
</LinearLayout> </LinearLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1">
android:orientation="vertical"
android:gravity="center">
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.DisplaySmall"
android:id="@+id/text_title" android:id="@+id/text_title"
android:layout_width="match_parent" style="@style/TextAppearance.Material3.DisplaySmall"
android:layout_height="wrap_content" android:layout_width="0dp"
android:textAlignment="center" android:layout_height="0dp"
android:gravity="center"
android:textColor="?attr/colorOnSurface" android:textColor="?attr/colorOnSurface"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/text_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_weight="2"
tools:text="@string/welcome" /> tools:text="@string/welcome" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleLarge"
android:id="@+id/text_description" android:id="@+id/text_description"
android:layout_width="match_parent" style="@style/TextAppearance.Material3.TitleLarge"
android:layout_height="wrap_content" android:layout_width="0dp"
android:layout_marginTop="16dp" android:layout_height="0dp"
android:paddingHorizontal="32dp" android:gravity="center"
android:textAlignment="center" android:textSize="20sp"
android:textSize="26sp" android:paddingHorizontal="16dp"
app:lineHeight="40sp" app:layout_constraintBottom_toTopOf="@+id/button_action"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_title"
app:layout_constraintVertical_weight="2"
app:lineHeight="30sp"
tools:text="@string/welcome_description" /> tools:text="@string/welcome_description" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_confirmation"
style="@style/TextAppearance.Material3.TitleLarge"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingHorizontal="16dp"
android:paddingBottom="20dp"
android:gravity="center"
android:textSize="30sp"
android:visibility="invisible"
android:text="@string/step_complete"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_description"
app:layout_constraintVertical_weight="1"
app:lineHeight="30sp" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/button_action" android:id="@+id/button_action"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="56dp" android:layout_height="56dp"
android:layout_marginTop="32dp" android:layout_marginTop="16dp"
android:layout_marginBottom="48dp"
android:textSize="20sp" android:textSize="20sp"
app:iconSize="24sp"
app:iconGravity="end" app:iconGravity="end"
app:iconSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_description"
tools:text="Get started" /> tools:text="Get started" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout> </LinearLayout>

View File

@ -5,23 +5,16 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<TextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/text_value" android:id="@+id/text_value"
style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/spacing_medlarge" android:layout_marginBottom="@dimen/spacing_medlarge"
android:layout_marginTop="@dimen/spacing_medlarge" android:layout_marginTop="@dimen/spacing_medlarge"
tools:text="75" /> tools:text="75%" />
<TextView
android:id="@+id/text_units"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/text_value"
android:layout_toEndOf="@+id/text_value"
tools:text="%" />
<com.google.android.material.slider.Slider <com.google.android.material.slider.Slider
android:id="@+id/slider" android:id="@+id/slider"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/setup_root" android:id="@+id/setup_root"
@ -8,35 +8,39 @@
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2" android:id="@+id/viewPager2"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="0dp"
android:clipToPadding="false"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toTopOf="@+id/button_next"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.TextButton"
android:id="@+id/button_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="12dp" android:layout_above="@+id/constraint_buttons"
android:text="@string/next" android:layout_alignParentTop="true"
android:visibility="invisible" android:clipToPadding="false" />
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.button.MaterialButton <androidx.constraintlayout.widget.ConstraintLayout
style="@style/Widget.Material3.Button.TextButton" android:id="@+id/constraint_buttons"
android:id="@+id/button_back" android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="12dp" android:layout_margin="8dp"
android:text="@string/back" android:layout_alignParentBottom="true">
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> <com.google.android.material.button.MaterialButton
android:id="@+id/button_next"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_back"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/back"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>

View File

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
@ -11,31 +12,40 @@
android:minHeight="72dp" android:minHeight="72dp"
android:padding="@dimen/spacing_large"> android:padding="@dimen/spacing_large">
<com.google.android.material.textview.MaterialTextView <LinearLayout
style="@style/TextAppearance.Material3.HeadlineMedium" android:layout_width="match_parent"
android:id="@+id/text_setting_name"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:orientation="vertical">
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:textSize="16sp"
android:textAlignment="viewStart"
app:lineHeight="28dp"
tools:text="Setting Name" />
<TextView <com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodySmall" android:id="@+id/text_setting_name"
android:id="@+id/text_setting_description" style="@style/TextAppearance.Material3.HeadlineMedium"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:textAlignment="viewStart"
android:layout_alignParentStart="true" android:textSize="16sp"
android:layout_alignStart="@+id/text_setting_name" app:lineHeight="22dp"
android:layout_below="@+id/text_setting_name" tools:text="Setting Name" />
android:layout_marginTop="@dimen/spacing_small"
android:visibility="visible" <com.google.android.material.textview.MaterialTextView
android:textAlignment="viewStart" android:id="@+id/text_setting_description"
tools:text="@string/app_disclaimer" /> style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:textAlignment="viewStart"
tools:text="@string/app_disclaimer" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_value"
style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:textAlignment="viewStart"
android:textStyle="bold"
tools:text="1x" />
</LinearLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -21,11 +21,12 @@
app:layout_constraintVertical_chainStyle="spread" app:layout_constraintVertical_chainStyle="spread"
app:layout_constraintWidth_max="220dp" app:layout_constraintWidth_max="220dp"
app:layout_constraintWidth_min="110dp" app:layout_constraintWidth_min="110dp"
app:layout_constraintVertical_weight="3" /> app:layout_constraintVertical_weight="3"
tools:src="@drawable/ic_notification" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/text_title" android:id="@+id/text_title"
style="@style/TextAppearance.Material3.DisplayMedium" style="@style/TextAppearance.Material3.DisplaySmall"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:textAlignment="center" android:textAlignment="center"
@ -44,23 +45,42 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:textAlignment="center" android:textAlignment="center"
android:textSize="26sp" android:textSize="20sp"
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"
app:layout_constraintBottom_toTopOf="@+id/button_action" app:layout_constraintBottom_toTopOf="@+id/button_action"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_title" app:layout_constraintTop_toBottomOf="@+id/text_title"
app:layout_constraintVertical_weight="2" app:layout_constraintVertical_weight="2"
app:lineHeight="40sp" app:lineHeight="30sp"
tools:text="@string/welcome_description" /> tools:text="@string/welcome_description" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_confirmation"
style="@style/TextAppearance.Material3.TitleLarge"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:paddingHorizontal="16dp"
android:paddingTop="24dp"
android:textAlignment="center"
android:textSize="30sp"
android:visibility="invisible"
android:text="@string/step_complete"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_description"
app:layout_constraintVertical_weight="1"
app:lineHeight="30sp" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/button_action" android:id="@+id/button_action"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="56dp" android:layout_height="56dp"
android:textSize="20sp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="48dp" android:layout_marginBottom="48dp"
android:textSize="20sp"
app:iconGravity="end" app:iconGravity="end"
app:iconSize="24sp" app:iconSize="24sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"

View File

@ -29,6 +29,7 @@
<string name="back">Back</string> <string name="back">Back</string>
<string name="add_games">Add Games</string> <string name="add_games">Add Games</string>
<string name="add_games_description">Select your games folder</string> <string name="add_games_description">Select your games folder</string>
<string name="step_complete">Complete!</string>
<!-- Home strings --> <!-- Home strings -->
<string name="home_games">Games</string> <string name="home_games">Games</string>
@ -149,6 +150,7 @@
<string name="frame_limit_slider">Limit speed percent</string> <string name="frame_limit_slider">Limit speed percent</string>
<string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string> <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string>
<string name="cpu_accuracy">CPU accuracy</string> <string name="cpu_accuracy">CPU accuracy</string>
<string name="value_with_units">%1$s%2$s</string>
<!-- System settings strings --> <!-- System settings strings -->
<string name="use_docked_mode">Docked Mode</string> <string name="use_docked_mode">Docked Mode</string>

View File

@ -1,7 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <functional>
#include <string> #include <string>
#include <vector>
#include "common/settings_common.h" #include "common/settings_common.h"
namespace Settings { namespace Settings {

View File

@ -12,8 +12,8 @@ namespace Settings {
template <typename T> template <typename T>
struct EnumMetadata { struct EnumMetadata {
static constexpr std::vector<std::pair<std::string, T>> Canonicalizations(); static std::vector<std::pair<std::string, T>> Canonicalizations();
static constexpr u32 Index(); static u32 Index();
}; };
#define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__)) #define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__))
@ -66,11 +66,11 @@ struct EnumMetadata {
#define ENUM(NAME, ...) \ #define ENUM(NAME, ...) \
enum class NAME : u32 { __VA_ARGS__ }; \ enum class NAME : u32 { __VA_ARGS__ }; \
template <> \ template <> \
constexpr std::vector<std::pair<std::string, NAME>> EnumMetadata<NAME>::Canonicalizations() { \ inline std::vector<std::pair<std::string, NAME>> EnumMetadata<NAME>::Canonicalizations() { \
return {PAIR(NAME, __VA_ARGS__)}; \ return {PAIR(NAME, __VA_ARGS__)}; \
} \ } \
template <> \ template <> \
constexpr u32 EnumMetadata<NAME>::Index() { \ inline u32 EnumMetadata<NAME>::Index() { \
return __COUNTER__; \ return __COUNTER__; \
} }
@ -85,7 +85,7 @@ enum class AudioEngine : u32 {
}; };
template <> template <>
constexpr std::vector<std::pair<std::string, AudioEngine>> inline std::vector<std::pair<std::string, AudioEngine>>
EnumMetadata<AudioEngine>::Canonicalizations() { EnumMetadata<AudioEngine>::Canonicalizations() {
return { return {
{"auto", AudioEngine::Auto}, {"auto", AudioEngine::Auto},
@ -96,7 +96,7 @@ EnumMetadata<AudioEngine>::Canonicalizations() {
} }
template <> template <>
constexpr u32 EnumMetadata<AudioEngine>::Index() { inline u32 EnumMetadata<AudioEngine>::Index() {
// This is just a sufficiently large number that is more than the number of other enums declared // This is just a sufficiently large number that is more than the number of other enums declared
// here // here
return 100; return 100;
@ -147,7 +147,7 @@ ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
template <typename Type> template <typename Type>
constexpr std::string CanonicalizeEnum(Type id) { inline std::string CanonicalizeEnum(Type id) {
const auto group = EnumMetadata<Type>::Canonicalizations(); const auto group = EnumMetadata<Type>::Canonicalizations();
for (auto& [name, value] : group) { for (auto& [name, value] : group) {
if (value == id) { if (value == id) {
@ -158,7 +158,7 @@ constexpr std::string CanonicalizeEnum(Type id) {
} }
template <typename Type> template <typename Type>
constexpr Type ToEnum(const std::string& canonicalization) { inline Type ToEnum(const std::string& canonicalization) {
const auto group = EnumMetadata<Type>::Canonicalizations(); const auto group = EnumMetadata<Type>::Canonicalizations();
for (auto& [name, value] : group) { for (auto& [name, value] : group) {
if (name == canonicalization) { if (name == canonicalization) {

View File

@ -190,7 +190,7 @@ public:
} }
} }
[[nodiscard]] std::string constexpr Canonicalize() const override final { [[nodiscard]] std::string Canonicalize() const override final {
if constexpr (std::is_enum_v<Type>) { if constexpr (std::is_enum_v<Type>) {
return CanonicalizeEnum(this->GetValue()); return CanonicalizeEnum(this->GetValue());
} else { } else {
@ -256,11 +256,11 @@ public:
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
* @param other_setting_ A second Setting to associate to this one in metadata * @param other_setting_ A second Setting to associate to this one in metadata
*/ */
template <typename T = BasicSetting>
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name, explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
Category category_, u32 specialization_ = Specialization::Default, Category category_, u32 specialization_ = Specialization::Default,
bool save_ = true, bool runtime_modifiable_ = false, bool save_ = true, bool runtime_modifiable_ = false,
BasicSetting* other_setting_ = nullptr) typename std::enable_if<!ranged, T*>::type other_setting_ = nullptr)
requires(!ranged)
: Setting<Type, false>{ : Setting<Type, false>{
linkage, default_val, name, category_, specialization_, linkage, default_val, name, category_, specialization_,
save_, runtime_modifiable_, other_setting_} { save_, runtime_modifiable_, other_setting_} {
@ -282,12 +282,12 @@ public:
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
* @param other_setting_ A second Setting to associate to this one in metadata * @param other_setting_ A second Setting to associate to this one in metadata
*/ */
template <typename T = BasicSetting>
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val, explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
const Type& max_val, const std::string& name, Category category_, const Type& max_val, const std::string& name, Category category_,
u32 specialization_ = Specialization::Default, bool save_ = true, u32 specialization_ = Specialization::Default, bool save_ = true,
bool runtime_modifiable_ = false, bool runtime_modifiable_ = false,
BasicSetting* other_setting_ = nullptr) typename std::enable_if<ranged, T*>::type other_setting_ = nullptr)
requires(ranged)
: Setting<Type, true>{linkage, default_val, min_val, : Setting<Type, true>{linkage, default_val, min_val,
max_val, name, category_, max_val, name, category_,
specialization_, save_, runtime_modifiable_, specialization_, save_, runtime_modifiable_,

View File

@ -263,6 +263,23 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
std::vector<u8> mem(size); std::vector<u8> mem(size);
if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) { if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) {
// Restore any bytes belonging to replaced instructions.
auto it = replaced_instructions.lower_bound(addr);
for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
// Get the bytes of the instruction we previously replaced.
const u32 original_bytes = it->second;
// Calculate where to start writing to the output buffer.
const size_t output_offset = it->first - addr;
// Calculate how many bytes to write.
// The loop condition ensures output_offset < size.
const size_t n = std::min<size_t>(size - output_offset, sizeof(u32));
// Write the bytes to the output buffer.
std::memcpy(mem.data() + output_offset, &original_bytes, n);
}
SendReply(Common::HexToString(mem)); SendReply(Common::HexToString(mem));
} else { } else {
SendReply(GDB_STUB_REPLY_ERR); SendReply(GDB_STUB_REPLY_ERR);

View File

@ -35,7 +35,7 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw_file) {
update_raw = std::move(update_raw_file); update_raw = std::move(update_raw_file);
} }
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const { VirtualFile RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const {
if (!updatable) { if (!updatable) {
return file; return file;
} }
@ -45,12 +45,11 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_titl
return patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw); return patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw);
} }
ResultVal<VirtualFile> RomFSFactory::OpenPatchedRomFS(u64 title_id, ContentRecordType type) const { VirtualFile RomFSFactory::OpenPatchedRomFS(u64 title_id, ContentRecordType type) const {
auto nca = content_provider.GetEntry(title_id, type); auto nca = content_provider.GetEntry(title_id, type);
if (nca == nullptr) { if (nca == nullptr) {
// TODO: Find the right error code to use here return nullptr;
return ResultUnknown;
} }
const PatchManager patch_manager{title_id, filesystem_controller, content_provider}; const PatchManager patch_manager{title_id, filesystem_controller, content_provider};
@ -58,28 +57,20 @@ ResultVal<VirtualFile> RomFSFactory::OpenPatchedRomFS(u64 title_id, ContentRecor
return patch_manager.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), type); return patch_manager.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), type);
} }
ResultVal<VirtualFile> RomFSFactory::OpenPatchedRomFSWithProgramIndex( VirtualFile RomFSFactory::OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
u64 title_id, u8 program_index, ContentRecordType type) const { ContentRecordType type) const {
const auto res_title_id = GetBaseTitleIDWithProgramIndex(title_id, program_index); const auto res_title_id = GetBaseTitleIDWithProgramIndex(title_id, program_index);
return OpenPatchedRomFS(res_title_id, type); return OpenPatchedRomFS(res_title_id, type);
} }
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, VirtualFile RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) const {
ContentRecordType type) const {
const std::shared_ptr<NCA> res = GetEntry(title_id, storage, type); const std::shared_ptr<NCA> res = GetEntry(title_id, storage, type);
if (res == nullptr) { if (res == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here return nullptr;
return ResultUnknown;
} }
const auto romfs = res->GetRomFS(); return res->GetRomFS();
if (romfs == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return ResultUnknown;
}
return romfs;
} }
std::shared_ptr<NCA> RomFSFactory::GetEntry(u64 title_id, StorageId storage, std::shared_ptr<NCA> RomFSFactory::GetEntry(u64 title_id, StorageId storage,

View File

@ -41,13 +41,11 @@ public:
~RomFSFactory(); ~RomFSFactory();
void SetPackedUpdate(VirtualFile update_raw_file); void SetPackedUpdate(VirtualFile update_raw_file);
[[nodiscard]] ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const; [[nodiscard]] VirtualFile OpenCurrentProcess(u64 current_process_title_id) const;
[[nodiscard]] ResultVal<VirtualFile> OpenPatchedRomFS(u64 title_id, [[nodiscard]] VirtualFile OpenPatchedRomFS(u64 title_id, ContentRecordType type) const;
ContentRecordType type) const; [[nodiscard]] VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
[[nodiscard]] ResultVal<VirtualFile> OpenPatchedRomFSWithProgramIndex( ContentRecordType type) const;
u64 title_id, u8 program_index, ContentRecordType type) const; [[nodiscard]] VirtualFile Open(u64 title_id, StorageId storage, ContentRecordType type) const;
[[nodiscard]] ResultVal<VirtualFile> Open(u64 title_id, StorageId storage,
ContentRecordType type) const;
private: private:
[[nodiscard]] std::shared_ptr<NCA> GetEntry(u64 title_id, StorageId storage, [[nodiscard]] std::shared_ptr<NCA> GetEntry(u64 title_id, StorageId storage,

View File

@ -108,26 +108,16 @@ SaveDataFactory::SaveDataFactory(Core::System& system_, VirtualDir save_director
SaveDataFactory::~SaveDataFactory() = default; SaveDataFactory::~SaveDataFactory() = default;
ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const SaveDataAttribute& meta) const {
PrintSaveDataAttributeWarnings(meta); PrintSaveDataAttributeWarnings(meta);
const auto save_directory = const auto save_directory =
GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
auto out = dir->CreateDirectoryRelative(save_directory); return dir->CreateDirectoryRelative(save_directory);
// Return an error if the save data doesn't actually exist.
if (out == nullptr) {
// TODO(DarkLordZach): Find out correct error code.
return ResultUnknown;
}
return out;
} }
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const SaveDataAttribute& meta) const {
const auto save_directory = const auto save_directory =
GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
@ -138,12 +128,6 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
return Create(space, meta); return Create(space, meta);
} }
// Return an error if the save data doesn't actually exist.
if (out == nullptr) {
// TODO(Subv): Find out correct error code.
return ResultUnknown;
}
return out; return out;
} }

View File

@ -89,8 +89,8 @@ public:
explicit SaveDataFactory(Core::System& system_, VirtualDir save_directory_); explicit SaveDataFactory(Core::System& system_, VirtualDir save_directory_);
~SaveDataFactory(); ~SaveDataFactory();
ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const; VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const; VirtualDir Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;

View File

@ -23,7 +23,7 @@ SDMCFactory::SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_)
SDMCFactory::~SDMCFactory() = default; SDMCFactory::~SDMCFactory() = default;
ResultVal<VirtualDir> SDMCFactory::Open() const { VirtualDir SDMCFactory::Open() const {
return sd_dir; return sd_dir;
} }

View File

@ -18,7 +18,7 @@ public:
explicit SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_); explicit SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_);
~SDMCFactory(); ~SDMCFactory();
ResultVal<VirtualDir> Open() const; VirtualDir Open() const;
VirtualDir GetSDMCModificationLoadRoot(u64 title_id) const; VirtualDir GetSDMCModificationLoadRoot(u64 title_id) const;
VirtualDir GetSDMCContentDirectory() const; VirtualDir GetSDMCContentDirectory() const;

View File

@ -289,6 +289,19 @@ enum class GyroscopeZeroDriftMode : u32 {
Tight = 2, Tight = 2,
}; };
// This is nn::settings::system::TouchScreenMode
enum class TouchScreenMode : u32 {
Stylus = 0,
Standard = 1,
};
// This is nn::hid::TouchScreenModeForNx
enum class TouchScreenModeForNx : u8 {
UseSystemSetting,
Finger,
Heat2,
};
// This is nn::hid::NpadStyleTag // This is nn::hid::NpadStyleTag
struct NpadStyleTag { struct NpadStyleTag {
union { union {
@ -334,6 +347,14 @@ struct TouchState {
}; };
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
// This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
INSERT_PADDING_BYTES(0xF);
};
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x10,
"TouchScreenConfigurationForNx is an invalid size");
struct NpadColor { struct NpadColor {
u8 r{}; u8 r{};
u8 g{}; u8 g{};
@ -662,6 +683,11 @@ struct MouseState {
}; };
static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size"); static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
struct UniquePadId {
u64 id;
};
static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
/// Converts a NpadIdType to an array index. /// Converts a NpadIdType to an array index.
constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) { constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
switch (npad_id_type) { switch (npad_id_type) {

View File

@ -768,7 +768,7 @@ Result KPageTable::UnmapProcessMemory(KProcessAddress dst_addr, size_t size,
m_memory_block_slab_manager, num_allocator_blocks); m_memory_block_slab_manager, num_allocator_blocks);
R_TRY(allocator_result); R_TRY(allocator_result);
CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); R_TRY(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
// Apply the memory block update. // Apply the memory block update.
m_memory_block_manager.Update(std::addressof(allocator), dst_addr, num_pages, m_memory_block_manager.Update(std::addressof(allocator), dst_addr, num_pages,

View File

@ -283,159 +283,6 @@ private:
u32 description_end; u32 description_end;
}; };
/**
* This is an optional value type. It holds a `Result` and, if that code is ResultSuccess, it
* also holds a result of type `T`. If the code is an error code (not ResultSuccess), then trying
* to access the inner value with operator* is undefined behavior and will assert with Unwrap().
* Users of this class must be cognizant to check the status of the ResultVal with operator bool(),
* Code(), Succeeded() or Failed() prior to accessing the inner value.
*
* An example of how it could be used:
* \code
* ResultVal<int> Frobnicate(float strength) {
* if (strength < 0.f || strength > 1.0f) {
* // Can't frobnicate too weakly or too strongly
* return Result{ErrorModule::Common, 1};
* } else {
* // Frobnicated! Give caller a cookie
* return 42;
* }
* }
* \endcode
*
* \code
* auto frob_result = Frobnicate(0.75f);
* if (frob_result) {
* // Frobbed ok
* printf("My cookie is %d\n", *frob_result);
* } else {
* printf("Guess I overdid it. :( Error code: %ux\n", frob_result.Code().raw);
* }
* \endcode
*/
template <typename T>
class ResultVal {
public:
constexpr ResultVal() : expected{} {}
constexpr ResultVal(Result code) : expected{Common::Unexpected(code)} {}
constexpr ResultVal(ResultRange range) : expected{Common::Unexpected(range)} {}
template <typename U>
constexpr ResultVal(U&& val) : expected{std::forward<U>(val)} {}
template <typename... Args>
constexpr ResultVal(Args&&... args) : expected{std::in_place, std::forward<Args>(args)...} {}
~ResultVal() = default;
constexpr ResultVal(const ResultVal&) = default;
constexpr ResultVal(ResultVal&&) = default;
ResultVal& operator=(const ResultVal&) = default;
ResultVal& operator=(ResultVal&&) = default;
[[nodiscard]] constexpr explicit operator bool() const noexcept {
return expected.has_value();
}
[[nodiscard]] constexpr Result Code() const {
return expected.has_value() ? ResultSuccess : expected.error();
}
[[nodiscard]] constexpr bool Succeeded() const {
return expected.has_value();
}
[[nodiscard]] constexpr bool Failed() const {
return !expected.has_value();
}
[[nodiscard]] constexpr T* operator->() {
return std::addressof(expected.value());
}
[[nodiscard]] constexpr const T* operator->() const {
return std::addressof(expected.value());
}
[[nodiscard]] constexpr T& operator*() & {
return *expected;
}
[[nodiscard]] constexpr const T& operator*() const& {
return *expected;
}
[[nodiscard]] constexpr T&& operator*() && {
return *expected;
}
[[nodiscard]] constexpr const T&& operator*() const&& {
return *expected;
}
[[nodiscard]] constexpr T& Unwrap() & {
ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal");
return expected.value();
}
[[nodiscard]] constexpr const T& Unwrap() const& {
ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal");
return expected.value();
}
[[nodiscard]] constexpr T&& Unwrap() && {
ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal");
return std::move(expected.value());
}
[[nodiscard]] constexpr const T&& Unwrap() const&& {
ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal");
return std::move(expected.value());
}
template <typename U>
[[nodiscard]] constexpr T ValueOr(U&& v) const& {
return expected.value_or(v);
}
template <typename U>
[[nodiscard]] constexpr T ValueOr(U&& v) && {
return expected.value_or(v);
}
private:
// TODO (Morph): Replace this with C++23 std::expected.
Common::Expected<T, Result> expected;
};
/**
* Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps
* the contained value and assigns it to `target`, which can be either an l-value expression or a
* variable declaration. If it fails the return code is returned from the current function. Thus it
* can be used to cascade errors out, achieving something akin to exception handling.
*/
#define CASCADE_RESULT(target, source) \
auto CONCAT2(check_result_L, __LINE__) = source; \
if (CONCAT2(check_result_L, __LINE__).Failed()) { \
return CONCAT2(check_result_L, __LINE__).Code(); \
} \
target = std::move(*CONCAT2(check_result_L, __LINE__))
/**
* Analogous to CASCADE_RESULT, but for a bare Result. The code will be propagated if
* non-success, or discarded otherwise.
*/
#define CASCADE_CODE(source) \
do { \
auto CONCAT2(check_result_L, __LINE__) = source; \
if (CONCAT2(check_result_L, __LINE__).IsError()) { \
return CONCAT2(check_result_L, __LINE__); \
} \
} while (false)
#define R_SUCCEEDED(res) (static_cast<Result>(res).IsSuccess()) #define R_SUCCEEDED(res) (static_cast<Result>(res).IsSuccess())
#define R_FAILED(res) (static_cast<Result>(res).IsFailure()) #define R_FAILED(res) (static_cast<Result>(res).IsFailure())

View File

@ -765,15 +765,16 @@ Result Module::Interface::InitializeApplicationInfoBase() {
// TODO(ogniK): This should be changed to reflect the target process for when we have multiple // TODO(ogniK): This should be changed to reflect the target process for when we have multiple
// processes emulated. As we don't actually have pid support we should assume we're just using // processes emulated. As we don't actually have pid support we should assume we're just using
// our own process // our own process
const auto launch_property = Glue::ApplicationLaunchProperty launch_property{};
system.GetARPManager().GetLaunchProperty(system.GetApplicationProcessProgramID()); const auto result = system.GetARPManager().GetLaunchProperty(
&launch_property, system.GetApplicationProcessProgramID());
if (launch_property.Failed()) { if (result != ResultSuccess) {
LOG_ERROR(Service_ACC, "Failed to get launch property"); LOG_ERROR(Service_ACC, "Failed to get launch property");
return Account::ResultInvalidApplication; return Account::ResultInvalidApplication;
} }
switch (launch_property->base_game_storage_id) { switch (launch_property.base_game_storage_id) {
case FileSys::StorageId::GameCard: case FileSys::StorageId::GameCard:
application_info.application_type = ApplicationType::GameCard; application_info.application_type = ApplicationType::GameCard;
break; break;
@ -785,7 +786,7 @@ Result Module::Interface::InitializeApplicationInfoBase() {
break; break;
default: default:
LOG_ERROR(Service_ACC, "Invalid game storage ID! storage_id={}", LOG_ERROR(Service_ACC, "Invalid game storage ID! storage_id={}",
launch_property->base_game_storage_id); launch_property.base_game_storage_id);
return Account::ResultInvalidApplication; return Account::ResultInvalidApplication;
} }

View File

@ -1578,11 +1578,13 @@ void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
attribute.title_id = system.GetApplicationProcessProgramID(); attribute.title_id = system.GetApplicationProcessProgramID();
attribute.user_id = user_id; attribute.user_id = user_id;
attribute.type = FileSys::SaveDataType::SaveData; attribute.type = FileSys::SaveDataType::SaveData;
FileSys::VirtualDir save_data{};
const auto res = system.GetFileSystemController().CreateSaveData( const auto res = system.GetFileSystemController().CreateSaveData(
FileSys::SaveDataSpaceId::NandUser, attribute); &save_data, FileSys::SaveDataSpaceId::NandUser, attribute);
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res.Code()); rb.Push(res);
rb.Push<u64>(0); rb.Push<u64>(0);
} }
@ -1667,26 +1669,30 @@ void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) {
auto app_man = ns_am2->GetApplicationManagerInterface(); auto app_man = ns_am2->GetApplicationManagerInterface();
// Get desired application language // Get desired application language
const auto res_lang = app_man->GetApplicationDesiredLanguage(supported_languages); u8 desired_language{};
if (res_lang.Failed()) { const auto res_lang =
app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages);
if (res_lang != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res_lang.Code()); rb.Push(res_lang);
return; return;
} }
// Convert to settings language code. // Convert to settings language code.
const auto res_code = app_man->ConvertApplicationLanguageToLanguageCode(*res_lang); u64 language_code{};
if (res_code.Failed()) { const auto res_code =
app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language);
if (res_code != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res_code.Code()); rb.Push(res_code);
return; return;
} }
LOG_DEBUG(Service_AM, "got desired_language={:016X}", *res_code); LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code);
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(*res_code); rb.Push(language_code);
} }
void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) { void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) {

View File

@ -57,8 +57,8 @@ Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size
return FileSys::ERROR_PATH_NOT_FOUND; return FileSys::ERROR_PATH_NOT_FOUND;
} }
const auto entry_type = GetEntryType(path); FileSys::EntryType entry_type{};
if (entry_type.Code() == ResultSuccess) { if (GetEntryType(&entry_type, path) == ResultSuccess) {
return FileSys::ERROR_PATH_ALREADY_EXISTS; return FileSys::ERROR_PATH_ALREADY_EXISTS;
} }
@ -210,8 +210,8 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
return ResultUnknown; return ResultUnknown;
} }
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
FileSys::Mode mode) const { const std::string& path_, FileSys::Mode mode) const {
const std::string path(Common::FS::SanitizePath(path_)); const std::string path(Common::FS::SanitizePath(path_));
std::string_view npath = path; std::string_view npath = path;
while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
@ -224,50 +224,68 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
} }
if (mode == FileSys::Mode::Append) { if (mode == FileSys::Mode::Append) {
return std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()); *out_file = std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize());
} else {
*out_file = file;
} }
return file; return ResultSuccess;
} }
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) { Result VfsDirectoryServiceWrapper::OpenDirectory(FileSys::VirtualDir* out_directory,
const std::string& path_) {
std::string path(Common::FS::SanitizePath(path_)); std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, path); auto dir = GetDirectoryRelativeWrapped(backing, path);
if (dir == nullptr) { if (dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this // TODO(DarkLordZach): Find a better error code for this
return FileSys::ERROR_PATH_NOT_FOUND; return FileSys::ERROR_PATH_NOT_FOUND;
} }
return dir; *out_directory = dir;
return ResultSuccess;
} }
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( Result VfsDirectoryServiceWrapper::GetEntryType(FileSys::EntryType* out_entry_type,
const std::string& path_) const { const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_)); std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
auto filename = Common::FS::GetFilename(path);
// TODO(Subv): Some games use the '/' path, find out what this means.
if (filename.empty())
return FileSys::EntryType::Directory;
if (dir->GetFile(filename) != nullptr)
return FileSys::EntryType::File;
if (dir->GetSubdirectory(filename) != nullptr)
return FileSys::EntryType::Directory;
return FileSys::ERROR_PATH_NOT_FOUND;
}
ResultVal<FileSys::FileTimeStampRaw> VfsDirectoryServiceWrapper::GetFileTimeStampRaw(
const std::string& path) const {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr) { if (dir == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND; return FileSys::ERROR_PATH_NOT_FOUND;
} }
if (GetEntryType(path).Failed()) {
auto filename = Common::FS::GetFilename(path);
// TODO(Subv): Some games use the '/' path, find out what this means.
if (filename.empty()) {
*out_entry_type = FileSys::EntryType::Directory;
return ResultSuccess;
}
if (dir->GetFile(filename) != nullptr) {
*out_entry_type = FileSys::EntryType::File;
return ResultSuccess;
}
if (dir->GetSubdirectory(filename) != nullptr) {
*out_entry_type = FileSys::EntryType::Directory;
return ResultSuccess;
}
return FileSys::ERROR_PATH_NOT_FOUND;
}
Result VfsDirectoryServiceWrapper::GetFileTimeStampRaw(
FileSys::FileTimeStampRaw* out_file_time_stamp_raw, const std::string& path) const {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND; return FileSys::ERROR_PATH_NOT_FOUND;
} }
return dir->GetFileTimeStamp(Common::FS::GetFilename(path));
FileSys::EntryType entry_type;
if (GetEntryType(&entry_type, path) != ResultSuccess) {
return FileSys::ERROR_PATH_NOT_FOUND;
}
*out_file_time_stamp_raw = dir->GetFileTimeStamp(Common::FS::GetFilename(path));
return ResultSuccess;
} }
FileSystemController::FileSystemController(Core::System& system_) : system{system_} {} FileSystemController::FileSystemController(Core::System& system_) : system{system_} {}
@ -310,57 +328,54 @@ void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) {
romfs_factory->SetPackedUpdate(std::move(update_raw)); romfs_factory->SetPackedUpdate(std::move(update_raw));
} }
ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() const { FileSys::VirtualFile FileSystemController::OpenRomFSCurrentProcess() const {
LOG_TRACE(Service_FS, "Opening RomFS for current process"); LOG_TRACE(Service_FS, "Opening RomFS for current process");
if (romfs_factory == nullptr) { if (romfs_factory == nullptr) {
// TODO(bunnei): Find a better error code for this return nullptr;
return ResultUnknown;
} }
return romfs_factory->OpenCurrentProcess(system.GetApplicationProcessProgramID()); return romfs_factory->OpenCurrentProcess(system.GetApplicationProcessProgramID());
} }
ResultVal<FileSys::VirtualFile> FileSystemController::OpenPatchedRomFS( FileSys::VirtualFile FileSystemController::OpenPatchedRomFS(u64 title_id,
u64 title_id, FileSys::ContentRecordType type) const { FileSys::ContentRecordType type) const {
LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}", title_id); LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}", title_id);
if (romfs_factory == nullptr) { if (romfs_factory == nullptr) {
// TODO: Find a better error code for this return nullptr;
return ResultUnknown;
} }
return romfs_factory->OpenPatchedRomFS(title_id, type); return romfs_factory->OpenPatchedRomFS(title_id, type);
} }
ResultVal<FileSys::VirtualFile> FileSystemController::OpenPatchedRomFSWithProgramIndex( FileSys::VirtualFile FileSystemController::OpenPatchedRomFSWithProgramIndex(
u64 title_id, u8 program_index, FileSys::ContentRecordType type) const { u64 title_id, u8 program_index, FileSys::ContentRecordType type) const {
LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}, program_index={}", title_id, LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}, program_index={}", title_id,
program_index); program_index);
if (romfs_factory == nullptr) { if (romfs_factory == nullptr) {
// TODO: Find a better error code for this return nullptr;
return ResultUnknown;
} }
return romfs_factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type); return romfs_factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
} }
ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS( FileSys::VirtualFile FileSystemController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const { FileSys::ContentRecordType type) const {
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}", LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
title_id, storage_id, type); title_id, storage_id, type);
if (romfs_factory == nullptr) { if (romfs_factory == nullptr) {
// TODO(bunnei): Find a better error code for this return nullptr;
return ResultUnknown;
} }
return romfs_factory->Open(title_id, storage_id, type); return romfs_factory->Open(title_id, storage_id, type);
} }
ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( Result FileSystemController::CreateSaveData(FileSys::VirtualDir* out_save_data,
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const { FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& save_struct) const {
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space, LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
save_struct.DebugInfo()); save_struct.DebugInfo());
@ -368,11 +383,18 @@ ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
return FileSys::ERROR_ENTITY_NOT_FOUND; return FileSys::ERROR_ENTITY_NOT_FOUND;
} }
return save_data_factory->Create(space, save_struct); auto save_data = save_data_factory->Create(space, save_struct);
if (save_data == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data = save_data;
return ResultSuccess;
} }
ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData( Result FileSystemController::OpenSaveData(FileSys::VirtualDir* out_save_data,
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& attribute) const { FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& attribute) const {
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", space, LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", space,
attribute.DebugInfo()); attribute.DebugInfo());
@ -380,32 +402,50 @@ ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
return FileSys::ERROR_ENTITY_NOT_FOUND; return FileSys::ERROR_ENTITY_NOT_FOUND;
} }
return save_data_factory->Open(space, attribute); auto save_data = save_data_factory->Open(space, attribute);
if (save_data == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data = save_data;
return ResultSuccess;
} }
ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace( Result FileSystemController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
FileSys::SaveDataSpaceId space) const { FileSys::SaveDataSpaceId space) const {
LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", space); LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", space);
if (save_data_factory == nullptr) { if (save_data_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND; return FileSys::ERROR_ENTITY_NOT_FOUND;
} }
return save_data_factory->GetSaveDataSpaceDirectory(space); auto save_data_space = save_data_factory->GetSaveDataSpaceDirectory(space);
if (save_data_space == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data_space = save_data_space;
return ResultSuccess;
} }
ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() const { Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const {
LOG_TRACE(Service_FS, "Opening SDMC"); LOG_TRACE(Service_FS, "Opening SDMC");
if (sdmc_factory == nullptr) { if (sdmc_factory == nullptr) {
return FileSys::ERROR_SD_CARD_NOT_FOUND; return FileSys::ERROR_SD_CARD_NOT_FOUND;
} }
return sdmc_factory->Open(); auto sdmc = sdmc_factory->Open();
if (sdmc == nullptr) {
return FileSys::ERROR_SD_CARD_NOT_FOUND;
}
*out_sdmc = sdmc;
return ResultSuccess;
} }
ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition( Result FileSystemController::OpenBISPartition(FileSys::VirtualDir* out_bis_partition,
FileSys::BisPartitionId id) const { FileSys::BisPartitionId id) const {
LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", id); LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", id);
if (bis_factory == nullptr) { if (bis_factory == nullptr) {
@ -417,11 +457,12 @@ ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition(
return FileSys::ERROR_INVALID_ARGUMENT; return FileSys::ERROR_INVALID_ARGUMENT;
} }
return part; *out_bis_partition = part;
return ResultSuccess;
} }
ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage( Result FileSystemController::OpenBISPartitionStorage(
FileSys::BisPartitionId id) const { FileSys::VirtualFile* out_bis_partition_storage, FileSys::BisPartitionId id) const {
LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", id); LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", id);
if (bis_factory == nullptr) { if (bis_factory == nullptr) {
@ -433,7 +474,8 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
return FileSys::ERROR_INVALID_ARGUMENT; return FileSys::ERROR_INVALID_ARGUMENT;
} }
return part; *out_bis_partition_storage = part;
return ResultSuccess;
} }
u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const { u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const {

View File

@ -64,21 +64,24 @@ public:
Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
void SetPackedUpdate(FileSys::VirtualFile update_raw); void SetPackedUpdate(FileSys::VirtualFile update_raw);
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const; FileSys::VirtualFile OpenRomFSCurrentProcess() const;
ResultVal<FileSys::VirtualFile> OpenPatchedRomFS(u64 title_id, FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type) const;
FileSys::ContentRecordType type) const; FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
ResultVal<FileSys::VirtualFile> OpenPatchedRomFSWithProgramIndex( FileSys::ContentRecordType type) const;
u64 title_id, u8 program_index, FileSys::ContentRecordType type) const; FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const;
FileSys::ContentRecordType type) const;
ResultVal<FileSys::VirtualDir> CreateSaveData( Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const; const FileSys::SaveDataAttribute& save_struct) const;
ResultVal<FileSys::VirtualDir> OpenSaveData( Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const; const FileSys::SaveDataAttribute& save_struct) const;
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const; Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
ResultVal<FileSys::VirtualDir> OpenSDMC() const; FileSys::SaveDataSpaceId space) const;
ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const; Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const;
ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id) const; Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition,
FileSys::BisPartitionId id) const;
Result OpenBISPartitionStorage(FileSys::VirtualFile* out_bis_partition_storage,
FileSys::BisPartitionId id) const;
u64 GetFreeSpaceSize(FileSys::StorageId id) const; u64 GetFreeSpaceSize(FileSys::StorageId id) const;
u64 GetTotalSpaceSize(FileSys::StorageId id) const; u64 GetTotalSpaceSize(FileSys::StorageId id) const;
@ -224,26 +227,28 @@ public:
* @param mode Mode to open the file with * @param mode Mode to open the file with
* @return Opened file, or error code * @return Opened file, or error code
*/ */
ResultVal<FileSys::VirtualFile> OpenFile(const std::string& path, FileSys::Mode mode) const; Result OpenFile(FileSys::VirtualFile* out_file, const std::string& path,
FileSys::Mode mode) const;
/** /**
* Open a directory specified by its path * Open a directory specified by its path
* @param path Path relative to the archive * @param path Path relative to the archive
* @return Opened directory, or error code * @return Opened directory, or error code
*/ */
ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path); Result OpenDirectory(FileSys::VirtualDir* out_directory, const std::string& path);
/** /**
* Get the type of the specified path * Get the type of the specified path
* @return The type of the specified path or error code * @return The type of the specified path or error code
*/ */
ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const; Result GetEntryType(FileSys::EntryType* out_entry_type, const std::string& path) const;
/** /**
* Get the timestamp of the specified path * Get the timestamp of the specified path
* @return The timestamp of the specified path or error code * @return The timestamp of the specified path or error code
*/ */
ResultVal<FileSys::FileTimeStampRaw> GetFileTimeStampRaw(const std::string& path) const; Result GetFileTimeStampRaw(FileSys::FileTimeStampRaw* out_time_stamp_raw,
const std::string& path) const;
private: private:
FileSys::VirtualDir backing; FileSys::VirtualDir backing;

View File

@ -419,14 +419,15 @@ public:
LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode); LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode);
auto result = backend.OpenFile(name, mode); FileSys::VirtualFile vfs_file{};
if (result.Failed()) { auto result = backend.OpenFile(&vfs_file, name, mode);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code()); rb.Push(result);
return; return;
} }
auto file = std::make_shared<IFile>(system, result.Unwrap()); auto file = std::make_shared<IFile>(system, vfs_file);
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -444,14 +445,15 @@ public:
LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags); LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags);
auto result = backend.OpenDirectory(name); FileSys::VirtualDir vfs_dir{};
if (result.Failed()) { auto result = backend.OpenDirectory(&vfs_dir, name);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code()); rb.Push(result);
return; return;
} }
auto directory = std::make_shared<IDirectory>(system, result.Unwrap()); auto directory = std::make_shared<IDirectory>(system, vfs_dir);
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -464,16 +466,17 @@ public:
LOG_DEBUG(Service_FS, "called. file={}", name); LOG_DEBUG(Service_FS, "called. file={}", name);
auto result = backend.GetEntryType(name); FileSys::EntryType vfs_entry_type{};
if (result.Failed()) { auto result = backend.GetEntryType(&vfs_entry_type, name);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code()); rb.Push(result);
return; return;
} }
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(*result)); rb.Push<u32>(static_cast<u32>(vfs_entry_type));
} }
void Commit(HLERequestContext& ctx) { void Commit(HLERequestContext& ctx) {
@ -505,16 +508,17 @@ public:
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name); LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
auto result = backend.GetFileTimeStampRaw(name); FileSys::FileTimeStampRaw vfs_timestamp{};
if (result.Failed()) { auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code()); rb.Push(result);
return; return;
} }
IPC::ResponseBuilder rb{ctx, 10}; IPC::ResponseBuilder rb{ctx, 10};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw(*result); rb.PushRaw(vfs_timestamp);
} }
private: private:
@ -572,14 +576,15 @@ private:
} }
void FindAllSaves(FileSys::SaveDataSpaceId space) { void FindAllSaves(FileSys::SaveDataSpaceId space) {
const auto save_root = fsc.OpenSaveDataSpace(space); FileSys::VirtualDir save_root{};
const auto result = fsc.OpenSaveDataSpace(&save_root, space);
if (save_root.Failed() || *save_root == nullptr) { if (result != ResultSuccess || save_root == nullptr) {
LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space); LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space);
return; return;
} }
for (const auto& type : (*save_root)->GetSubdirectories()) { for (const auto& type : save_root->GetSubdirectories()) {
if (type->GetName() == "save") { if (type->GetName() == "save") {
for (const auto& save_id : type->GetSubdirectories()) { for (const auto& save_id : type->GetSubdirectories()) {
for (const auto& user_id : save_id->GetSubdirectories()) { for (const auto& user_id : save_id->GetSubdirectories()) {
@ -837,9 +842,11 @@ void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) {
void FSP_SRV::OpenSdCardFileSystem(HLERequestContext& ctx) { void FSP_SRV::OpenSdCardFileSystem(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called"); LOG_DEBUG(Service_FS, "called");
auto filesystem = FileSys::VirtualDir sdmc_dir{};
std::make_shared<IFileSystem>(system, fsc.OpenSDMC().Unwrap(), fsc.OpenSDMC(&sdmc_dir);
SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
auto filesystem = std::make_shared<IFileSystem>(
system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -856,7 +863,8 @@ void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(), LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
uid[1], uid[0]); uid[1], uid[0]);
fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, save_struct); FileSys::VirtualDir save_data_dir{};
fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, save_struct);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -874,8 +882,9 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
LOG_INFO(Service_FS, "called."); LOG_INFO(Service_FS, "called.");
auto dir = fsc.OpenSaveData(parameters.space_id, parameters.attribute); FileSys::VirtualDir dir{};
if (dir.Failed()) { auto result = fsc.OpenSaveData(&dir, parameters.space_id, parameters.attribute);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0}; IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
return; return;
@ -899,8 +908,8 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
ASSERT(false); ASSERT(false);
} }
auto filesystem = std::make_shared<IFileSystem>(system, std::move(dir.Unwrap()), auto filesystem =
SizeGetter::FromStorageId(fsc, id)); std::make_shared<IFileSystem>(system, std::move(dir), SizeGetter::FromStorageId(fsc, id));
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -970,7 +979,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) {
if (!romfs) { if (!romfs) {
auto current_romfs = fsc.OpenRomFSCurrentProcess(); auto current_romfs = fsc.OpenRomFSCurrentProcess();
if (current_romfs.Failed()) { if (!current_romfs) {
// TODO (bunnei): Find the right error code to use here // TODO (bunnei): Find the right error code to use here
LOG_CRITICAL(Service_FS, "no file system interface available!"); LOG_CRITICAL(Service_FS, "no file system interface available!");
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
@ -978,7 +987,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) {
return; return;
} }
romfs = current_romfs.Unwrap(); romfs = current_romfs;
} }
auto storage = std::make_shared<IStorage>(system, romfs); auto storage = std::make_shared<IStorage>(system, romfs);
@ -999,7 +1008,7 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
if (data.Failed()) { if (!data) {
const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
if (archive != nullptr) { if (archive != nullptr) {
@ -1021,7 +1030,7 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
const FileSys::PatchManager pm{title_id, fsc, content_provider}; const FileSys::PatchManager pm{title_id, fsc, content_provider};
auto storage = std::make_shared<IStorage>( auto storage = std::make_shared<IStorage>(
system, pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data)); system, pm.PatchRomFS(std::move(data), 0, FileSys::ContentRecordType::Data));
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -1051,7 +1060,7 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
fsc.OpenPatchedRomFSWithProgramIndex(system.GetApplicationProcessProgramID(), program_index, fsc.OpenPatchedRomFSWithProgramIndex(system.GetApplicationProcessProgramID(), program_index,
FileSys::ContentRecordType::Program); FileSys::ContentRecordType::Program);
if (patched_romfs.Failed()) { if (!patched_romfs) {
// TODO: Find the right error code to use here // TODO: Find the right error code to use here
LOG_ERROR(Service_FS, "could not open storage with program_index={}", program_index); LOG_ERROR(Service_FS, "could not open storage with program_index={}", program_index);
@ -1060,7 +1069,7 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
return; return;
} }
auto storage = std::make_shared<IStorage>(system, std::move(patched_romfs.Unwrap())); auto storage = std::make_shared<IStorage>(system, std::move(patched_romfs));
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);

View File

@ -65,18 +65,19 @@ void ARP_R::GetApplicationLaunchProperty(HLERequestContext& ctx) {
return; return;
} }
const auto res = manager.GetLaunchProperty(*title_id); ApplicationLaunchProperty launch_property{};
const auto res = manager.GetLaunchProperty(&launch_property, *title_id);
if (res.Failed()) { if (res != ResultSuccess) {
LOG_ERROR(Service_ARP, "Failed to get launch property!"); LOG_ERROR(Service_ARP, "Failed to get launch property!");
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code()); rb.Push(res);
return; return;
} }
IPC::ResponseBuilder rb{ctx, 6}; IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw(*res); rb.PushRaw(launch_property);
} }
void ARP_R::GetApplicationLaunchPropertyWithApplicationId(HLERequestContext& ctx) { void ARP_R::GetApplicationLaunchPropertyWithApplicationId(HLERequestContext& ctx) {
@ -85,18 +86,19 @@ void ARP_R::GetApplicationLaunchPropertyWithApplicationId(HLERequestContext& ctx
LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id); LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);
const auto res = manager.GetLaunchProperty(title_id); ApplicationLaunchProperty launch_property{};
const auto res = manager.GetLaunchProperty(&launch_property, title_id);
if (res.Failed()) { if (res != ResultSuccess) {
LOG_ERROR(Service_ARP, "Failed to get launch property!"); LOG_ERROR(Service_ARP, "Failed to get launch property!");
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code()); rb.Push(res);
return; return;
} }
IPC::ResponseBuilder rb{ctx, 6}; IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw(*res); rb.PushRaw(launch_property);
} }
void ARP_R::GetApplicationControlProperty(HLERequestContext& ctx) { void ARP_R::GetApplicationControlProperty(HLERequestContext& ctx) {
@ -113,16 +115,17 @@ void ARP_R::GetApplicationControlProperty(HLERequestContext& ctx) {
return; return;
} }
const auto res = manager.GetControlProperty(*title_id); std::vector<u8> nacp_data;
const auto res = manager.GetControlProperty(&nacp_data, *title_id);
if (res.Failed()) { if (res != ResultSuccess) {
LOG_ERROR(Service_ARP, "Failed to get control property!"); LOG_ERROR(Service_ARP, "Failed to get control property!");
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code()); rb.Push(res);
return; return;
} }
ctx.WriteBuffer(*res); ctx.WriteBuffer(nacp_data);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -134,16 +137,17 @@ void ARP_R::GetApplicationControlPropertyWithApplicationId(HLERequestContext& ct
LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id); LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);
const auto res = manager.GetControlProperty(title_id); std::vector<u8> nacp_data;
const auto res = manager.GetControlProperty(&nacp_data, title_id);
if (res.Failed()) { if (res != ResultSuccess) {
LOG_ERROR(Service_ARP, "Failed to get control property!"); LOG_ERROR(Service_ARP, "Failed to get control property!");
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code()); rb.Push(res);
return; return;
} }
ctx.WriteBuffer(*res); ctx.WriteBuffer(nacp_data);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);

View File

@ -15,7 +15,8 @@ ARPManager::ARPManager() = default;
ARPManager::~ARPManager() = default; ARPManager::~ARPManager() = default;
ResultVal<ApplicationLaunchProperty> ARPManager::GetLaunchProperty(u64 title_id) const { Result ARPManager::GetLaunchProperty(ApplicationLaunchProperty* out_launch_property,
u64 title_id) const {
if (title_id == 0) { if (title_id == 0) {
return Glue::ResultInvalidProcessId; return Glue::ResultInvalidProcessId;
} }
@ -25,10 +26,11 @@ ResultVal<ApplicationLaunchProperty> ARPManager::GetLaunchProperty(u64 title_id)
return Glue::ResultProcessIdNotRegistered; return Glue::ResultProcessIdNotRegistered;
} }
return iter->second.launch; *out_launch_property = iter->second.launch;
return ResultSuccess;
} }
ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const { Result ARPManager::GetControlProperty(std::vector<u8>* out_control_property, u64 title_id) const {
if (title_id == 0) { if (title_id == 0) {
return Glue::ResultInvalidProcessId; return Glue::ResultInvalidProcessId;
} }
@ -38,7 +40,8 @@ ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const {
return Glue::ResultProcessIdNotRegistered; return Glue::ResultProcessIdNotRegistered;
} }
return iter->second.control; *out_control_property = iter->second.control;
return ResultSuccess;
} }
Result ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch, Result ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,

View File

@ -32,12 +32,12 @@ public:
// Returns the ApplicationLaunchProperty corresponding to the provided title ID if it was // Returns the ApplicationLaunchProperty corresponding to the provided title ID if it was
// previously registered, otherwise ResultProcessIdNotRegistered if it was never registered or // previously registered, otherwise ResultProcessIdNotRegistered if it was never registered or
// ResultInvalidProcessId if the title ID is 0. // ResultInvalidProcessId if the title ID is 0.
ResultVal<ApplicationLaunchProperty> GetLaunchProperty(u64 title_id) const; Result GetLaunchProperty(ApplicationLaunchProperty* out_launch_property, u64 title_id) const;
// Returns a vector of the raw bytes of NACP data (necessarily 0x4000 in size) corresponding to // Returns a vector of the raw bytes of NACP data (necessarily 0x4000 in size) corresponding to
// the provided title ID if it was previously registered, otherwise ResultProcessIdNotRegistered // the provided title ID if it was previously registered, otherwise ResultProcessIdNotRegistered
// if it was never registered or ResultInvalidProcessId if the title ID is 0. // if it was never registered or ResultInvalidProcessId if the title ID is 0.
ResultVal<std::vector<u8>> GetControlProperty(u64 title_id) const; Result GetControlProperty(std::vector<u8>* out_control_property, u64 title_id) const;
// Adds a new entry to the internal database with the provided parameters, returning // Adds a new entry to the internal database with the provided parameters, returning
// ResultProcessIdNotRegistered if attempting to re-register a title ID without an intermediate // ResultProcessIdNotRegistered if attempting to re-register a title ID without an intermediate

View File

@ -16,22 +16,6 @@ class EmulatedConsole;
namespace Service::HID { namespace Service::HID {
class Controller_Touchscreen final : public ControllerBase { class Controller_Touchscreen final : public ControllerBase {
public: public:
// This is nn::hid::TouchScreenModeForNx
enum class TouchScreenModeForNx : u8 {
UseSystemSetting,
Finger,
Heat2,
};
// This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
INSERT_PADDING_BYTES_NOINIT(0x7);
INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved
};
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
"TouchScreenConfigurationForNx is an invalid size");
explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Touchscreen() override; ~Controller_Touchscreen() override;

View File

@ -2368,7 +2368,7 @@ void Hid::GetNpadCommunicationMode(HLERequestContext& ctx) {
void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) { void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto touchscreen_mode{rp.PopRaw<Controller_Touchscreen::TouchScreenConfigurationForNx>()}; const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
const auto applet_resource_user_id{rp.Pop<u64>()}; const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
@ -2543,7 +2543,8 @@ public:
class HidSys final : public ServiceFramework<HidSys> { class HidSys final : public ServiceFramework<HidSys> {
public: public:
explicit HidSys(Core::System& system_) : ServiceFramework{system_, "hid:sys"} { explicit HidSys(Core::System& system_)
: ServiceFramework{system_, "hid:sys"}, service_context{system_, "hid:sys"} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{31, nullptr, "SendKeyboardLockKeyEvent"}, {31, nullptr, "SendKeyboardLockKeyEvent"},
@ -2568,7 +2569,7 @@ public:
{303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
{304, nullptr, "EnableAssigningSingleOnSlSrPress"}, {304, nullptr, "EnableAssigningSingleOnSlSrPress"},
{305, nullptr, "DisableAssigningSingleOnSlSrPress"}, {305, nullptr, "DisableAssigningSingleOnSlSrPress"},
{306, nullptr, "GetLastActiveNpad"}, {306, &HidSys::GetLastActiveNpad, "GetLastActiveNpad"},
{307, nullptr, "GetNpadSystemExtStyle"}, {307, nullptr, "GetNpadSystemExtStyle"},
{308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, {308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
{309, nullptr, "GetNpadFullKeyGripColor"}, {309, nullptr, "GetNpadFullKeyGripColor"},
@ -2624,7 +2625,7 @@ public:
{700, nullptr, "ActivateUniquePad"}, {700, nullptr, "ActivateUniquePad"},
{702, nullptr, "AcquireUniquePadConnectionEventHandle"}, {702, nullptr, "AcquireUniquePadConnectionEventHandle"},
{703, nullptr, "GetUniquePadIds"}, {703, nullptr, "GetUniquePadIds"},
{751, nullptr, "AcquireJoyDetachOnBluetoothOffEventHandle"}, {751, &HidSys::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
{800, nullptr, "ListSixAxisSensorHandles"}, {800, nullptr, "ListSixAxisSensorHandles"},
{801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
{802, nullptr, "ResetSixAxisSensorCalibrationValues"}, {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
@ -2650,7 +2651,7 @@ public:
{830, nullptr, "SetNotificationLedPattern"}, {830, nullptr, "SetNotificationLedPattern"},
{831, nullptr, "SetNotificationLedPatternWithTimeout"}, {831, nullptr, "SetNotificationLedPatternWithTimeout"},
{832, nullptr, "PrepareHidsForNotificationWake"}, {832, nullptr, "PrepareHidsForNotificationWake"},
{850, nullptr, "IsUsbFullKeyControllerEnabled"}, {850, &HidSys::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
{851, nullptr, "EnableUsbFullKeyController"}, {851, nullptr, "EnableUsbFullKeyController"},
{852, nullptr, "IsUsbConnected"}, {852, nullptr, "IsUsbConnected"},
{870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
@ -2682,7 +2683,7 @@ public:
{1150, nullptr, "SetTouchScreenMagnification"}, {1150, nullptr, "SetTouchScreenMagnification"},
{1151, nullptr, "GetTouchScreenFirmwareVersion"}, {1151, nullptr, "GetTouchScreenFirmwareVersion"},
{1152, nullptr, "SetTouchScreenDefaultConfiguration"}, {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
{1153, nullptr, "GetTouchScreenDefaultConfiguration"}, {1153, &HidSys::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
{1154, nullptr, "IsFirmwareAvailableForNotification"}, {1154, nullptr, "IsFirmwareAvailableForNotification"},
{1155, nullptr, "SetForceHandheldStyleVibration"}, {1155, nullptr, "SetForceHandheldStyleVibration"},
{1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
@ -2749,6 +2750,8 @@ public:
// clang-format on // clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent");
} }
private: private:
@ -2760,17 +2763,66 @@ private:
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
} }
void GetLastActiveNpad(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(Core::HID::NpadIdType::Handheld);
}
void GetUniquePadsFromNpad(HLERequestContext& ctx) { void GetUniquePadsFromNpad(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
const s64 total_entries = 0;
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type); LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
const std::vector<Core::HID::UniquePadId> unique_pads{};
ctx.WriteBuffer(unique_pads);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(total_entries); rb.Push(static_cast<u32>(unique_pads.size()));
} }
void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
}
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
const bool is_enabled = false;
LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(is_enabled);
}
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
Core::HID::TouchScreenConfigurationForNx touchscreen_config{
.mode = Core::HID::TouchScreenModeForNx::Finger,
};
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(touchscreen_config);
}
Kernel::KEvent* joy_detach_event;
KernelHelpers::ServiceContext service_context;
}; };
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {

View File

@ -357,7 +357,8 @@ public:
return ResultSuccess; return ResultSuccess;
} }
ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) { Result MapProcessCodeMemory(VAddr* out_map_location, Kernel::KProcess* process, VAddr base_addr,
u64 size) {
auto& page_table{process->GetPageTable()}; auto& page_table{process->GetPageTable()};
VAddr addr{}; VAddr addr{};
@ -372,20 +373,21 @@ public:
R_TRY(result); R_TRY(result);
if (ValidateRegionForMap(page_table, addr, size)) { if (ValidateRegionForMap(page_table, addr, size)) {
return addr; *out_map_location = addr;
return ResultSuccess;
} }
} }
return ERROR_INSUFFICIENT_ADDRESS_SPACE; return ERROR_INSUFFICIENT_ADDRESS_SPACE;
} }
ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size, Result MapNro(VAddr* out_map_location, Kernel::KProcess* process, VAddr nro_addr,
VAddr bss_addr, std::size_t bss_size, std::size_t size) { std::size_t nro_size, VAddr bss_addr, std::size_t bss_size, std::size_t size) {
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
auto& page_table{process->GetPageTable()}; auto& page_table{process->GetPageTable()};
VAddr addr{}; VAddr addr{};
CASCADE_RESULT(addr, MapProcessCodeMemory(process, nro_addr, nro_size)); R_TRY(MapProcessCodeMemory(&addr, process, nro_addr, nro_size));
if (bss_size) { if (bss_size) {
auto block_guard = detail::ScopeExit([&] { auto block_guard = detail::ScopeExit([&] {
@ -411,7 +413,8 @@ public:
} }
if (ValidateRegionForMap(page_table, addr, size)) { if (ValidateRegionForMap(page_table, addr, size)) {
return addr; *out_map_location = addr;
return ResultSuccess;
} }
} }
@ -437,9 +440,9 @@ public:
CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start,
nro_header.segment_headers[DATA_INDEX].memory_size); nro_header.segment_headers[DATA_INDEX].memory_size);
CASCADE_CODE(process->GetPageTable().SetProcessMemoryPermission( R_TRY(process->GetPageTable().SetProcessMemoryPermission(
text_start, ro_start - text_start, Kernel::Svc::MemoryPermission::ReadExecute)); text_start, ro_start - text_start, Kernel::Svc::MemoryPermission::ReadExecute));
CASCADE_CODE(process->GetPageTable().SetProcessMemoryPermission( R_TRY(process->GetPageTable().SetProcessMemoryPermission(
ro_start, data_start - ro_start, Kernel::Svc::MemoryPermission::Read)); ro_start, data_start - ro_start, Kernel::Svc::MemoryPermission::Read));
return process->GetPageTable().SetProcessMemoryPermission( return process->GetPageTable().SetProcessMemoryPermission(
@ -542,31 +545,32 @@ public:
} }
// Map memory for the NRO // Map memory for the NRO
const auto map_result{MapNro(system.ApplicationProcess(), nro_address, nro_size, VAddr map_location{};
bss_address, bss_size, nro_size + bss_size)}; const auto map_result{MapNro(&map_location, system.ApplicationProcess(), nro_address,
if (map_result.Failed()) { nro_size, bss_address, bss_size, nro_size + bss_size)};
if (map_result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(map_result.Code()); rb.Push(map_result);
} }
// Load the NRO into the mapped memory // Load the NRO into the mapped memory
if (const auto result{ if (const auto result{
LoadNro(system.ApplicationProcess(), header, nro_address, *map_result)}; LoadNro(system.ApplicationProcess(), header, nro_address, map_location)};
result.IsError()) { result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(map_result.Code()); rb.Push(result);
} }
// Track the loaded NRO // Track the loaded NRO
nro.insert_or_assign(*map_result, nro.insert_or_assign(map_location,
NROInfo{hash, *map_result, nro_size, bss_address, bss_size, NROInfo{hash, map_location, nro_size, bss_address, bss_size,
header.segment_headers[TEXT_INDEX].memory_size, header.segment_headers[TEXT_INDEX].memory_size,
header.segment_headers[RO_INDEX].memory_size, header.segment_headers[RO_INDEX].memory_size,
header.segment_headers[DATA_INDEX].memory_size, nro_address}); header.segment_headers[DATA_INDEX].memory_size, nro_address});
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(*map_result); rb.Push(map_location);
} }
Result UnmapNro(const NROInfo& info) { Result UnmapNro(const NROInfo& info) {
@ -574,19 +578,19 @@ public:
auto& page_table{system.ApplicationProcess()->GetPageTable()}; auto& page_table{system.ApplicationProcess()->GetPageTable()};
if (info.bss_size != 0) { if (info.bss_size != 0) {
CASCADE_CODE(page_table.UnmapCodeMemory( R_TRY(page_table.UnmapCodeMemory(
info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address, info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address,
info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
} }
CASCADE_CODE(page_table.UnmapCodeMemory( R_TRY(page_table.UnmapCodeMemory(
info.nro_address + info.text_size + info.ro_size, info.nro_address + info.text_size + info.ro_size,
info.src_addr + info.text_size + info.ro_size, info.data_size, info.src_addr + info.text_size + info.ro_size, info.data_size,
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
CASCADE_CODE(page_table.UnmapCodeMemory( R_TRY(page_table.UnmapCodeMemory(
info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size, info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size,
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
CASCADE_CODE(page_table.UnmapCodeMemory( R_TRY(page_table.UnmapCodeMemory(
info.nro_address, info.src_addr, info.text_size, info.nro_address, info.src_addr, info.text_size,
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
return ResultSuccess; return ResultSuccess;

View File

@ -101,20 +101,14 @@ private:
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
const auto result{manager.GetDefault(source_flag)}; const auto default_miis{manager.GetDefault(source_flag)};
if (result.Failed()) { if (default_miis.size() > 0) {
IPC::ResponseBuilder rb{ctx, 2}; ctx.WriteBuffer(SerializeArray(default_miis));
rb.Push(result.Code());
return;
}
if (result->size() > 0) {
ctx.WriteBuffer(SerializeArray(*result));
} }
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(result->size())); rb.Push<u32>(static_cast<u32>(default_miis.size()));
} }
void Get1(HLERequestContext& ctx) { void Get1(HLERequestContext& ctx) {
@ -123,15 +117,10 @@ private:
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
const auto result{manager.GetDefault(source_flag)}; const auto default_miis{manager.GetDefault(source_flag)};
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
std::vector<CharInfo> values; std::vector<CharInfo> values;
for (const auto& element : *result) { for (const auto& element : default_miis) {
values.emplace_back(element.info); values.emplace_back(element.info);
} }
@ -139,7 +128,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(result->size())); rb.Push<u32>(static_cast<u32>(default_miis.size()));
} }
void UpdateLatest(HLERequestContext& ctx) { void UpdateLatest(HLERequestContext& ctx) {
@ -149,16 +138,17 @@ private:
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
const auto result{manager.UpdateLatest(info, source_flag)}; CharInfo new_char_info{};
if (result.Failed()) { const auto result{manager.UpdateLatest(&new_char_info, info, source_flag)};
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code()); rb.Push(result);
return; return;
} }
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw<CharInfo>(*result); rb.PushRaw<CharInfo>(new_char_info);
} }
void BuildRandom(HLERequestContext& ctx) { void BuildRandom(HLERequestContext& ctx) {

View File

@ -409,8 +409,7 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const {
return static_cast<u32>(count); return static_cast<u32>(count);
} }
ResultVal<CharInfo> MiiManager::UpdateLatest([[maybe_unused]] const CharInfo& info, Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag) {
SourceFlag source_flag) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) { if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return ERROR_CANNOT_FIND_ENTRY; return ERROR_CANNOT_FIND_ENTRY;
} }
@ -672,7 +671,7 @@ bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const {
return is_valid; return is_valid;
} }
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { std::vector<MiiInfoElement> MiiManager::GetDefault(SourceFlag source_flag) {
std::vector<MiiInfoElement> result; std::vector<MiiInfoElement> result;
if ((source_flag & SourceFlag::Default) == SourceFlag::None) { if ((source_flag & SourceFlag::Default) == SourceFlag::None) {

View File

@ -19,12 +19,12 @@ public:
bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter); bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
bool IsFullDatabase() const; bool IsFullDatabase() const;
u32 GetCount(SourceFlag source_flag) const; u32 GetCount(SourceFlag source_flag) const;
ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag); Result UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag);
CharInfo BuildRandom(Age age, Gender gender, Race race); CharInfo BuildRandom(Age age, Gender gender, Race race);
CharInfo BuildDefault(std::size_t index); CharInfo BuildDefault(std::size_t index);
CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
bool ValidateV3Info(const Ver3StoreData& mii_v3) const; bool ValidateV3Info(const Ver3StoreData& mii_v3) const;
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag); std::vector<MiiInfoElement> GetDefault(SourceFlag source_flag);
Result GetIndex(const CharInfo& info, u32& index); Result GetIndex(const CharInfo& info, u32& index);
// This is nn::mii::detail::Ver::StoreDataRaw::BuildFromStoreData // This is nn::mii::detail::Ver::StoreDataRaw::BuildFromStoreData

View File

@ -392,19 +392,20 @@ void IApplicationManagerInterface::GetApplicationDesiredLanguage(HLERequestConte
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto supported_languages = rp.Pop<u32>(); const auto supported_languages = rp.Pop<u32>();
const auto res = GetApplicationDesiredLanguage(supported_languages); u8 desired_language{};
if (res.Succeeded()) { const auto res = GetApplicationDesiredLanguage(&desired_language, supported_languages);
if (res == ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<u32>(*res); rb.Push<u32>(desired_language);
} else { } else {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code()); rb.Push(res);
} }
} }
ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage( Result IApplicationManagerInterface::GetApplicationDesiredLanguage(u8* out_desired_language,
const u32 supported_languages) { const u32 supported_languages) {
LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages); LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
// Get language code from settings // Get language code from settings
@ -430,7 +431,8 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
for (const auto lang : *priority_list) { for (const auto lang : *priority_list) {
const auto supported_flag = GetSupportedLanguageFlag(lang); const auto supported_flag = GetSupportedLanguageFlag(lang);
if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) { if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) {
return static_cast<u8>(lang); *out_desired_language = static_cast<u8>(lang);
return ResultSuccess;
} }
} }
@ -444,19 +446,20 @@ void IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto application_language = rp.Pop<u8>(); const auto application_language = rp.Pop<u8>();
const auto res = ConvertApplicationLanguageToLanguageCode(application_language); u64 language_code{};
if (res.Succeeded()) { const auto res = ConvertApplicationLanguageToLanguageCode(&language_code, application_language);
if (res == ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(*res); rb.Push(language_code);
} else { } else {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code()); rb.Push(res);
} }
} }
ResultVal<u64> IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode( Result IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
u8 application_language) { u64* out_language_code, u8 application_language) {
const auto language_code = const auto language_code =
ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language)); ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language));
if (language_code == std::nullopt) { if (language_code == std::nullopt) {
@ -464,7 +467,8 @@ ResultVal<u64> IApplicationManagerInterface::ConvertApplicationLanguageToLanguag
return Service::NS::ResultApplicationLanguageNotFound; return Service::NS::ResultApplicationLanguageNotFound;
} }
return static_cast<u64>(*language_code); *out_language_code = static_cast<u64>(*language_code);
return ResultSuccess;
} }
IApplicationVersionInterface::IApplicationVersionInterface(Core::System& system_) IApplicationVersionInterface::IApplicationVersionInterface(Core::System& system_)
@ -618,12 +622,13 @@ void IReadOnlyApplicationControlDataInterface::GetApplicationControlData(HLERequ
static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size."); static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size.");
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
std::vector<u8> nacp_data{};
const auto parameters{rp.PopRaw<RequestParameters>()}; const auto parameters{rp.PopRaw<RequestParameters>()};
const auto nacp_data{system.GetARPManager().GetControlProperty(parameters.application_id)}; const auto result =
const auto result = nacp_data ? ResultSuccess : ResultUnknown; system.GetARPManager().GetControlProperty(&nacp_data, parameters.application_id);
if (nacp_data) { if (result == ResultSuccess) {
ctx.WriteBuffer(nacp_data->data(), nacp_data->size()); ctx.WriteBuffer(nacp_data.data(), nacp_data.size());
} }
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};

View File

@ -28,8 +28,9 @@ public:
explicit IApplicationManagerInterface(Core::System& system_); explicit IApplicationManagerInterface(Core::System& system_);
~IApplicationManagerInterface() override; ~IApplicationManagerInterface() override;
ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages); Result GetApplicationDesiredLanguage(u8* out_desired_language, u32 supported_languages);
ResultVal<u64> ConvertApplicationLanguageToLanguageCode(u8 application_language); Result ConvertApplicationLanguageToLanguageCode(u64* out_language_code,
u8 application_language);
private: private:
void GetApplicationControlData(HLERequestContext& ctx); void GetApplicationControlData(HLERequestContext& ctx);

View File

@ -449,6 +449,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
case NativeWindowScalingMode::ScaleToWindow: case NativeWindowScalingMode::ScaleToWindow:
case NativeWindowScalingMode::ScaleCrop: case NativeWindowScalingMode::ScaleCrop:
case NativeWindowScalingMode::NoScaleCrop: case NativeWindowScalingMode::NoScaleCrop:
case NativeWindowScalingMode::PreserveAspectRatio:
break; break;
default: default:
LOG_ERROR(Service_Nvnflinger, "unknown scaling mode {}", scaling_mode); LOG_ERROR(Service_Nvnflinger, "unknown scaling mode {}", scaling_mode);

View File

@ -183,7 +183,7 @@ std::optional<u32> Nvnflinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
return layer->GetBinderId(); return layer->GetBinderId();
} }
ResultVal<Kernel::KReadableEvent*> Nvnflinger::FindVsyncEvent(u64 display_id) { Result Nvnflinger::FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64 display_id) {
const auto lock_guard = Lock(); const auto lock_guard = Lock();
auto* const display = FindDisplay(display_id); auto* const display = FindDisplay(display_id);
@ -191,7 +191,7 @@ ResultVal<Kernel::KReadableEvent*> Nvnflinger::FindVsyncEvent(u64 display_id) {
return VI::ResultNotFound; return VI::ResultNotFound;
} }
return display->GetVSyncEvent(); return display->GetVSyncEvent(out_vsync_event);
} }
VI::Display* Nvnflinger::FindDisplay(u64 display_id) { VI::Display* Nvnflinger::FindDisplay(u64 display_id) {

View File

@ -82,7 +82,7 @@ public:
/// ///
/// If an invalid display ID is provided, then VI::ResultNotFound is returned. /// If an invalid display ID is provided, then VI::ResultNotFound is returned.
/// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned. /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned.
[[nodiscard]] ResultVal<Kernel::KReadableEvent*> FindVsyncEvent(u64 display_id); [[nodiscard]] Result FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64 display_id);
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
/// finished. /// finished.

View File

@ -41,6 +41,7 @@ enum class NativeWindowScalingMode : s32 {
ScaleToWindow = 1, ScaleToWindow = 1,
ScaleCrop = 2, ScaleCrop = 2,
NoScaleCrop = 3, NoScaleCrop = 3,
PreserveAspectRatio = 4,
}; };
/// Transform parameter for QueueBuffer /// Transform parameter for QueueBuffer

View File

@ -152,7 +152,7 @@ private:
if (pin_code[0] == '\0') { if (pin_code[0] == '\0') {
return true; return true;
} }
if (!restriction_settings.is_free_communication_default_on) { if (!settings.is_free_communication_default_on) {
return true; return true;
} }
// TODO(ogniK): Check for blacklisted/exempted applications. Return false can happen here // TODO(ogniK): Check for blacklisted/exempted applications. Return false can happen here
@ -168,21 +168,21 @@ private:
if (pin_code[0] == '\0') { if (pin_code[0] == '\0') {
return true; return true;
} }
if (!restriction_settings.is_stero_vision_restricted) { if (!settings.is_stero_vision_restricted) {
return false; return false;
} }
return true; return true;
} }
void SetStereoVisionRestrictionImpl(bool is_restricted) { void SetStereoVisionRestrictionImpl(bool is_restricted) {
if (restriction_settings.disabled) { if (settings.disabled) {
return; return;
} }
if (pin_code[0] == '\0') { if (pin_code[0] == '\0') {
return; return;
} }
restriction_settings.is_stero_vision_restricted = is_restricted; settings.is_stero_vision_restricted = is_restricted;
} }
void Initialize(HLERequestContext& ctx) { void Initialize(HLERequestContext& ctx) {
@ -430,7 +430,7 @@ private:
} }
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(restriction_settings.is_stero_vision_restricted); rb.Push(settings.is_stero_vision_restricted);
} }
void ResetConfirmedStereoVisionPermission(HLERequestContext& ctx) { void ResetConfirmedStereoVisionPermission(HLERequestContext& ctx) {
@ -460,22 +460,28 @@ private:
bool stereo_vision{}; bool stereo_vision{};
}; };
// This is nn::pctl::RestrictionSettings struct ParentalControlSettings {
struct RestrictionSettings {
bool is_stero_vision_restricted{}; bool is_stero_vision_restricted{};
bool is_free_communication_default_on{}; bool is_free_communication_default_on{};
bool disabled{}; bool disabled{};
}; };
// This is nn::pctl::RestrictionSettings
struct RestrictionSettings {
u8 rating_age;
bool sns_post_restriction;
bool free_communication_restriction;
};
static_assert(sizeof(RestrictionSettings) == 0x3, "RestrictionSettings has incorrect size."); static_assert(sizeof(RestrictionSettings) == 0x3, "RestrictionSettings has incorrect size.");
// This is nn::pctl::PlayTimerSettings // This is nn::pctl::PlayTimerSettings
struct PlayTimerSettings { struct PlayTimerSettings {
// TODO: RE the actual contents of this struct std::array<u32, 13> settings;
std::array<u8, 0x34> settings;
}; };
static_assert(sizeof(PlayTimerSettings) == 0x34, "PlayTimerSettings has incorrect size."); static_assert(sizeof(PlayTimerSettings) == 0x34, "PlayTimerSettings has incorrect size.");
States states{}; States states{};
ParentalControlSettings settings{};
RestrictionSettings restriction_settings{}; RestrictionSettings restriction_settings{};
std::array<char, 8> pin_code{}; std::array<char, 8> pin_code{};
Capability capability{}; Capability capability{};

View File

@ -102,16 +102,17 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
m_system.ServiceManager().RegisterService(service_name, max_sessions, handler))); m_system.ServiceManager().RegisterService(service_name, max_sessions, handler)));
// Get the registered port. // Get the registered port.
auto port = m_system.ServiceManager().GetServicePort(service_name); Kernel::KPort* port{};
ASSERT(port.Succeeded()); ASSERT(
R_SUCCEEDED(m_system.ServiceManager().GetServicePort(std::addressof(port), service_name)));
// Open a new reference to the server port. // Open a new reference to the server port.
(*port)->GetServerPort().Open(); port->GetServerPort().Open();
// Begin tracking the server port. // Begin tracking the server port.
{ {
std::scoped_lock ll{m_list_mutex}; std::scoped_lock ll{m_list_mutex};
m_ports.emplace(std::addressof((*port)->GetServerPort()), std::move(handler)); m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler));
} }
// Signal the wakeup event. // Signal the wakeup event.

View File

@ -52,8 +52,7 @@ static Result ValidateServiceName(const std::string& name) {
Result ServiceManager::RegisterService(std::string name, u32 max_sessions, Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
SessionRequestHandlerPtr handler) { SessionRequestHandlerPtr handler) {
R_TRY(ValidateServiceName(name));
CASCADE_CODE(ValidateServiceName(name));
std::scoped_lock lk{lock}; std::scoped_lock lk{lock};
if (registered_services.find(name) != registered_services.end()) { if (registered_services.find(name) != registered_services.end()) {
@ -77,7 +76,7 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
} }
Result ServiceManager::UnregisterService(const std::string& name) { Result ServiceManager::UnregisterService(const std::string& name) {
CASCADE_CODE(ValidateServiceName(name)); R_TRY(ValidateServiceName(name));
std::scoped_lock lk{lock}; std::scoped_lock lk{lock};
const auto iter = registered_services.find(name); const auto iter = registered_services.find(name);
@ -92,8 +91,8 @@ Result ServiceManager::UnregisterService(const std::string& name) {
return ResultSuccess; return ResultSuccess;
} }
ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) { Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::string& name) {
CASCADE_CODE(ValidateServiceName(name)); R_TRY(ValidateServiceName(name));
std::scoped_lock lk{lock}; std::scoped_lock lk{lock};
auto it = service_ports.find(name); auto it = service_ports.find(name);
@ -102,7 +101,8 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
return Service::SM::ResultNotRegistered; return Service::SM::ResultNotRegistered;
} }
return it->second; *out_port = it->second;
return ResultSuccess;
} }
/** /**
@ -122,32 +122,34 @@ void SM::Initialize(HLERequestContext& ctx) {
} }
void SM::GetService(HLERequestContext& ctx) { void SM::GetService(HLERequestContext& ctx) {
auto result = GetServiceImpl(ctx); Kernel::KClientSession* client_session{};
auto result = GetServiceImpl(&client_session, ctx);
if (ctx.GetIsDeferred()) { if (ctx.GetIsDeferred()) {
// Don't overwrite the command buffer. // Don't overwrite the command buffer.
return; return;
} }
if (result.Succeeded()) { if (result == ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(result.Code()); rb.Push(result);
rb.PushMoveObjects(result.Unwrap()); rb.PushMoveObjects(client_session);
} else { } else {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code()); rb.Push(result);
} }
} }
void SM::GetServiceTipc(HLERequestContext& ctx) { void SM::GetServiceTipc(HLERequestContext& ctx) {
auto result = GetServiceImpl(ctx); Kernel::KClientSession* client_session{};
auto result = GetServiceImpl(&client_session, ctx);
if (ctx.GetIsDeferred()) { if (ctx.GetIsDeferred()) {
// Don't overwrite the command buffer. // Don't overwrite the command buffer.
return; return;
} }
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(result.Code()); rb.Push(result);
rb.PushMoveObjects(result.Succeeded() ? result.Unwrap() : nullptr); rb.PushMoveObjects(result == ResultSuccess ? client_session : nullptr);
} }
static std::string PopServiceName(IPC::RequestParser& rp) { static std::string PopServiceName(IPC::RequestParser& rp) {
@ -161,7 +163,7 @@ static std::string PopServiceName(IPC::RequestParser& rp) {
return result; return result;
} }
ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(HLERequestContext& ctx) { Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLERequestContext& ctx) {
if (!ctx.GetManager()->GetIsInitializedForSm()) { if (!ctx.GetManager()->GetIsInitializedForSm()) {
return Service::SM::ResultInvalidClient; return Service::SM::ResultInvalidClient;
} }
@ -170,18 +172,18 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(HLERequestContext& ctx) {
std::string name(PopServiceName(rp)); std::string name(PopServiceName(rp));
// Find the named port. // Find the named port.
auto port_result = service_manager.GetServicePort(name); Kernel::KPort* port{};
if (port_result.Code() == Service::SM::ResultInvalidServiceName) { auto port_result = service_manager.GetServicePort(&port, name);
if (port_result == Service::SM::ResultInvalidServiceName) {
LOG_ERROR(Service_SM, "Invalid service name '{}'", name); LOG_ERROR(Service_SM, "Invalid service name '{}'", name);
return Service::SM::ResultInvalidServiceName; return Service::SM::ResultInvalidServiceName;
} }
if (port_result.Failed()) { if (port_result != ResultSuccess) {
LOG_INFO(Service_SM, "Waiting for service {} to become available", name); LOG_INFO(Service_SM, "Waiting for service {} to become available", name);
ctx.SetIsDeferred(); ctx.SetIsDeferred();
return Service::SM::ResultNotRegistered; return Service::SM::ResultNotRegistered;
} }
auto& port = port_result.Unwrap();
// Create a new session. // Create a new session.
Kernel::KClientSession* session{}; Kernel::KClientSession* session{};
@ -192,7 +194,8 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(HLERequestContext& ctx) {
LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId()); LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
return session; *out_client_session = session;
return ResultSuccess;
} }
void SM::RegisterService(HLERequestContext& ctx) { void SM::RegisterService(HLERequestContext& ctx) {

View File

@ -42,7 +42,7 @@ private:
void RegisterService(HLERequestContext& ctx); void RegisterService(HLERequestContext& ctx);
void UnregisterService(HLERequestContext& ctx); void UnregisterService(HLERequestContext& ctx);
ResultVal<Kernel::KClientSession*> GetServiceImpl(HLERequestContext& ctx); Result GetServiceImpl(Kernel::KClientSession** out_client_session, HLERequestContext& ctx);
ServiceManager& service_manager; ServiceManager& service_manager;
Kernel::KernelCore& kernel; Kernel::KernelCore& kernel;
@ -55,7 +55,7 @@ public:
Result RegisterService(std::string name, u32 max_sessions, SessionRequestHandlerPtr handler); Result RegisterService(std::string name, u32 max_sessions, SessionRequestHandlerPtr handler);
Result UnregisterService(const std::string& name); Result UnregisterService(const std::string& name);
ResultVal<Kernel::KPort*> GetServicePort(const std::string& name); Result GetServicePort(Kernel::KPort** out_port, const std::string& name);
template <Common::DerivedFrom<SessionRequestHandler> T> template <Common::DerivedFrom<SessionRequestHandler> T>
std::shared_ptr<T> GetService(const std::string& service_name) const { std::shared_ptr<T> GetService(const std::string& service_name) const {

View File

@ -54,7 +54,7 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
RegisterHandlers(functions); RegisterHandlers(functions);
} }
static ResultVal<std::string> ResolveImpl(const std::string& fqdn_in) { static std::string ResolveImpl(const std::string& fqdn_in) {
// The real implementation makes various substitutions. // The real implementation makes various substitutions.
// For now we just return the string as-is, which is good enough when not // For now we just return the string as-is, which is good enough when not
// connecting to real Nintendo servers. // connecting to real Nintendo servers.
@ -64,13 +64,10 @@ static ResultVal<std::string> ResolveImpl(const std::string& fqdn_in) {
static Result ResolveCommon(const std::string& fqdn_in, std::array<char, 0x100>& fqdn_out) { static Result ResolveCommon(const std::string& fqdn_in, std::array<char, 0x100>& fqdn_out) {
const auto res = ResolveImpl(fqdn_in); const auto res = ResolveImpl(fqdn_in);
if (res.Failed()) { if (res.size() >= fqdn_out.size()) {
return res.Code();
}
if (res->size() >= fqdn_out.size()) {
return ResultOverflow; return ResultOverflow;
} }
std::memcpy(fqdn_out.data(), res->c_str(), res->size() + 1); std::memcpy(fqdn_out.data(), res.c_str(), res.size() + 1);
return ResultSuccess; return ResultSuccess;
} }

View File

@ -30,10 +30,10 @@ void Module::Interface::GetConfig(HLERequestContext& ctx) {
// This should call svcCallSecureMonitor with the appropriate args. // This should call svcCallSecureMonitor with the appropriate args.
// Since we do not have it implemented yet, we will use this for now. // Since we do not have it implemented yet, we will use this for now.
const auto smc_result = GetConfigImpl(config_item); u64 smc_result{};
const auto result_code = smc_result.Code(); const auto result_code = GetConfigImpl(&smc_result, config_item);
if (smc_result.Failed()) { if (result_code != ResultSuccess) {
LOG_ERROR(Service_SPL, "called, config_item={}, result_code={}", config_item, LOG_ERROR(Service_SPL, "called, config_item={}, result_code={}", config_item,
result_code.raw); result_code.raw);
@ -42,11 +42,11 @@ void Module::Interface::GetConfig(HLERequestContext& ctx) {
} }
LOG_DEBUG(Service_SPL, "called, config_item={}, result_code={}, smc_result={}", config_item, LOG_DEBUG(Service_SPL, "called, config_item={}, result_code={}, smc_result={}", config_item,
result_code.raw, *smc_result); result_code.raw, smc_result);
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(result_code); rb.Push(result_code);
rb.Push(*smc_result); rb.Push(smc_result);
} }
void Module::Interface::ModularExponentiate(HLERequestContext& ctx) { void Module::Interface::ModularExponentiate(HLERequestContext& ctx) {
@ -99,7 +99,7 @@ void Module::Interface::GetBootReason(HLERequestContext& ctx) {
rb.Push(ResultSecureMonitorNotImplemented); rb.Push(ResultSecureMonitorNotImplemented);
} }
ResultVal<u64> Module::Interface::GetConfigImpl(ConfigItem config_item) const { Result Module::Interface::GetConfigImpl(u64* out_config, ConfigItem config_item) const {
switch (config_item) { switch (config_item) {
case ConfigItem::DisableProgramVerification: case ConfigItem::DisableProgramVerification:
case ConfigItem::DramId: case ConfigItem::DramId:
@ -121,40 +121,50 @@ ResultVal<u64> Module::Interface::GetConfigImpl(ConfigItem config_item) const {
return ResultSecureMonitorNotImplemented; return ResultSecureMonitorNotImplemented;
case ConfigItem::ExosphereApiVersion: case ConfigItem::ExosphereApiVersion:
// Get information about the current exosphere version. // Get information about the current exosphere version.
return (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MAJOR} << 56) | *out_config = (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MAJOR} << 56) |
(u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MINOR} << 48) | (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MINOR} << 48) |
(u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MICRO} << 40) | (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MICRO} << 40) |
(static_cast<u64>(HLE::ApiVersion::GetTargetFirmware())); (static_cast<u64>(HLE::ApiVersion::GetTargetFirmware()));
return ResultSuccess;
case ConfigItem::ExosphereNeedsReboot: case ConfigItem::ExosphereNeedsReboot:
// We are executing, so we aren't in the process of rebooting. // We are executing, so we aren't in the process of rebooting.
return u64{0}; *out_config = u64{0};
return ResultSuccess;
case ConfigItem::ExosphereNeedsShutdown: case ConfigItem::ExosphereNeedsShutdown:
// We are executing, so we aren't in the process of shutting down. // We are executing, so we aren't in the process of shutting down.
return u64{0}; *out_config = u64{0};
return ResultSuccess;
case ConfigItem::ExosphereGitCommitHash: case ConfigItem::ExosphereGitCommitHash:
// Get information about the current exosphere git commit hash. // Get information about the current exosphere git commit hash.
return u64{0}; *out_config = u64{0};
return ResultSuccess;
case ConfigItem::ExosphereHasRcmBugPatch: case ConfigItem::ExosphereHasRcmBugPatch:
// Get information about whether this unit has the RCM bug patched. // Get information about whether this unit has the RCM bug patched.
return u64{0}; *out_config = u64{0};
return ResultSuccess;
case ConfigItem::ExosphereBlankProdInfo: case ConfigItem::ExosphereBlankProdInfo:
// Get whether this unit should simulate a "blanked" PRODINFO. // Get whether this unit should simulate a "blanked" PRODINFO.
return u64{0}; *out_config = u64{0};
return ResultSuccess;
case ConfigItem::ExosphereAllowCalWrites: case ConfigItem::ExosphereAllowCalWrites:
// Get whether this unit should allow writing to the calibration partition. // Get whether this unit should allow writing to the calibration partition.
return u64{0}; *out_config = u64{0};
return ResultSuccess;
case ConfigItem::ExosphereEmummcType: case ConfigItem::ExosphereEmummcType:
// Get what kind of emummc this unit has active. // Get what kind of emummc this unit has active.
return u64{0}; *out_config = u64{0};
return ResultSuccess;
case ConfigItem::ExospherePayloadAddress: case ConfigItem::ExospherePayloadAddress:
// Gets the physical address of the reboot payload buffer, if one exists. // Gets the physical address of the reboot payload buffer, if one exists.
return ResultSecureMonitorNotInitialized; return ResultSecureMonitorNotInitialized;
case ConfigItem::ExosphereLogConfiguration: case ConfigItem::ExosphereLogConfiguration:
// Get the log configuration. // Get the log configuration.
return u64{0}; *out_config = u64{0};
return ResultSuccess;
case ConfigItem::ExosphereForceEnableUsb30: case ConfigItem::ExosphereForceEnableUsb30:
// Get whether usb 3.0 should be force-enabled. // Get whether usb 3.0 should be force-enabled.
return u64{0}; *out_config = u64{0};
return ResultSuccess;
default: default:
return ResultSecureMonitorInvalidArgument; return ResultSecureMonitorInvalidArgument;
} }

View File

@ -35,7 +35,7 @@ public:
std::shared_ptr<Module> module; std::shared_ptr<Module> module;
private: private:
ResultVal<u64> GetConfigImpl(ConfigItem config_item) const; Result GetConfigImpl(u64* out_config, ConfigItem config_item) const;
std::mt19937 rng; std::mt19937 rng;
}; };

View File

@ -4,6 +4,7 @@
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/result.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@ -141,12 +142,12 @@ private:
bool did_set_host_name = false; bool did_set_host_name = false;
bool did_handshake = false; bool did_handshake = false;
ResultVal<s32> SetSocketDescriptorImpl(s32 fd) { Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
LOG_DEBUG(Service_SSL, "called, fd={}", fd); LOG_DEBUG(Service_SSL, "called, fd={}", fd);
ASSERT(!did_handshake); ASSERT(!did_handshake);
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u"); auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; }); ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
s32 ret_fd;
// Based on https://switchbrew.org/wiki/SSL_services#SetSocketDescriptor // Based on https://switchbrew.org/wiki/SSL_services#SetSocketDescriptor
if (do_not_close_socket) { if (do_not_close_socket) {
auto res = bsd->DuplicateSocketImpl(fd); auto res = bsd->DuplicateSocketImpl(fd);
@ -156,9 +157,9 @@ private:
} }
fd = *res; fd = *res;
fd_to_close = fd; fd_to_close = fd;
ret_fd = fd; *out_fd = fd;
} else { } else {
ret_fd = -1; *out_fd = -1;
} }
std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(fd); std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(fd);
if (!sock.has_value()) { if (!sock.has_value()) {
@ -167,7 +168,7 @@ private:
} }
socket = std::move(*sock); socket = std::move(*sock);
backend->SetSocket(socket); backend->SetSocket(socket);
return ret_fd; return ResultSuccess;
} }
Result SetHostNameImpl(const std::string& hostname) { Result SetHostNameImpl(const std::string& hostname) {
@ -247,34 +248,36 @@ private:
return ret; return ret;
} }
ResultVal<std::vector<u8>> ReadImpl(size_t size) { Result ReadImpl(std::vector<u8>* out_data, size_t size) {
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; }); ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
std::vector<u8> res(size); size_t actual_size{};
ResultVal<size_t> actual = backend->Read(res); Result res = backend->Read(&actual_size, *out_data);
if (actual.Failed()) { if (res != ResultSuccess) {
return actual.Code(); return res;
} }
res.resize(*actual); out_data->resize(actual_size);
return res; return res;
} }
ResultVal<size_t> WriteImpl(std::span<const u8> data) { Result WriteImpl(size_t* out_size, std::span<const u8> data) {
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; }); ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
return backend->Write(data); return backend->Write(out_size, data);
} }
ResultVal<s32> PendingImpl() { Result PendingImpl(s32* out_pending) {
LOG_WARNING(Service_SSL, "(STUBBED) called."); LOG_WARNING(Service_SSL, "(STUBBED) called.");
return 0; *out_pending = 0;
return ResultSuccess;
} }
void SetSocketDescriptor(HLERequestContext& ctx) { void SetSocketDescriptor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 in_fd = rp.Pop<s32>();
const ResultVal<s32> res = SetSocketDescriptorImpl(fd); s32 out_fd{-1};
const Result res = SetSocketDescriptorImpl(&out_fd, in_fd);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res.Code()); rb.Push(res);
rb.Push<s32>(res.ValueOr(-1)); rb.Push<s32>(out_fd);
} }
void SetHostName(HLERequestContext& ctx) { void SetHostName(HLERequestContext& ctx) {
@ -313,14 +316,15 @@ private:
}; };
static_assert(sizeof(OutputParameters) == 0x8); static_assert(sizeof(OutputParameters) == 0x8);
const Result res = DoHandshakeImpl(); Result res = DoHandshakeImpl();
OutputParameters out{}; OutputParameters out{};
if (res == ResultSuccess) { if (res == ResultSuccess) {
auto certs = backend->GetServerCerts(); std::vector<std::vector<u8>> certs;
if (certs.Succeeded()) { res = backend->GetServerCerts(&certs);
const std::vector<u8> certs_buf = SerializeServerCerts(*certs); if (res == ResultSuccess) {
const std::vector<u8> certs_buf = SerializeServerCerts(certs);
ctx.WriteBuffer(certs_buf); ctx.WriteBuffer(certs_buf);
out.certs_count = static_cast<u32>(certs->size()); out.certs_count = static_cast<u32>(certs.size());
out.certs_size = static_cast<u32>(certs_buf.size()); out.certs_size = static_cast<u32>(certs_buf.size());
} }
} }
@ -330,29 +334,32 @@ private:
} }
void Read(HLERequestContext& ctx) { void Read(HLERequestContext& ctx) {
const ResultVal<std::vector<u8>> res = ReadImpl(ctx.GetWriteBufferSize()); std::vector<u8> output_bytes;
const Result res = ReadImpl(&output_bytes, ctx.GetWriteBufferSize());
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res.Code()); rb.Push(res);
if (res.Succeeded()) { if (res == ResultSuccess) {
rb.Push(static_cast<u32>(res->size())); rb.Push(static_cast<u32>(output_bytes.size()));
ctx.WriteBuffer(*res); ctx.WriteBuffer(output_bytes);
} else { } else {
rb.Push(static_cast<u32>(0)); rb.Push(static_cast<u32>(0));
} }
} }
void Write(HLERequestContext& ctx) { void Write(HLERequestContext& ctx) {
const ResultVal<size_t> res = WriteImpl(ctx.ReadBuffer()); size_t write_size{0};
const Result res = WriteImpl(&write_size, ctx.ReadBuffer());
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res.Code()); rb.Push(res);
rb.Push(static_cast<u32>(res.ValueOr(0))); rb.Push(static_cast<u32>(write_size));
} }
void Pending(HLERequestContext& ctx) { void Pending(HLERequestContext& ctx) {
const ResultVal<s32> res = PendingImpl(); s32 pending_size{0};
const Result res = PendingImpl(&pending_size);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res.Code()); rb.Push(res);
rb.Push<s32>(res.ValueOr(0)); rb.Push<s32>(pending_size);
} }
void SetSessionCacheMode(HLERequestContext& ctx) { void SetSessionCacheMode(HLERequestContext& ctx) {
@ -438,13 +445,14 @@ private:
void CreateConnection(HLERequestContext& ctx) { void CreateConnection(HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "called"); LOG_WARNING(Service_SSL, "called");
auto backend_res = CreateSSLConnectionBackend(); std::unique_ptr<SSLConnectionBackend> backend;
const Result res = CreateSSLConnectionBackend(&backend);
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(backend_res.Code()); rb.Push(res);
if (backend_res.Succeeded()) { if (res == ResultSuccess) {
rb.PushIpcInterface<ISslConnection>(system, ssl_version, shared_data, rb.PushIpcInterface<ISslConnection>(system, ssl_version, shared_data,
std::move(*backend_res)); std::move(backend));
} }
} }

View File

@ -35,11 +35,11 @@ public:
virtual void SetSocket(std::shared_ptr<Network::SocketBase> socket) = 0; virtual void SetSocket(std::shared_ptr<Network::SocketBase> socket) = 0;
virtual Result SetHostName(const std::string& hostname) = 0; virtual Result SetHostName(const std::string& hostname) = 0;
virtual Result DoHandshake() = 0; virtual Result DoHandshake() = 0;
virtual ResultVal<size_t> Read(std::span<u8> data) = 0; virtual Result Read(size_t* out_size, std::span<u8> data) = 0;
virtual ResultVal<size_t> Write(std::span<const u8> data) = 0; virtual Result Write(size_t* out_size, std::span<const u8> data) = 0;
virtual ResultVal<std::vector<std::vector<u8>>> GetServerCerts() = 0; virtual Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) = 0;
}; };
ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend(); Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend);
} // namespace Service::SSL } // namespace Service::SSL

View File

@ -7,7 +7,7 @@
namespace Service::SSL { namespace Service::SSL {
ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() { Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend) {
LOG_ERROR(Service_SSL, LOG_ERROR(Service_SSL,
"Can't create SSL connection because no SSL backend is available on this platform"); "Can't create SSL connection because no SSL backend is available on this platform");
return ResultInternalError; return ResultInternalError;

View File

@ -105,31 +105,30 @@ public:
return ResultInternalError; return ResultInternalError;
} }
} }
return HandleReturn("SSL_do_handshake", 0, ret).Code(); return HandleReturn("SSL_do_handshake", 0, ret);
} }
ResultVal<size_t> Read(std::span<u8> data) override { Result Read(size_t* out_size, std::span<u8> data) override {
size_t actual; const int ret = SSL_read_ex(ssl, data.data(), data.size(), out_size);
const int ret = SSL_read_ex(ssl, data.data(), data.size(), &actual); return HandleReturn("SSL_read_ex", out_size, ret);
return HandleReturn("SSL_read_ex", actual, ret);
} }
ResultVal<size_t> Write(std::span<const u8> data) override { Result Write(size_t* out_size, std::span<const u8> data) override {
size_t actual; const int ret = SSL_write_ex(ssl, data.data(), data.size(), out_size);
const int ret = SSL_write_ex(ssl, data.data(), data.size(), &actual); return HandleReturn("SSL_write_ex", out_size, ret);
return HandleReturn("SSL_write_ex", actual, ret);
} }
ResultVal<size_t> HandleReturn(const char* what, size_t actual, int ret) { Result HandleReturn(const char* what, size_t* actual, int ret) {
const int ssl_err = SSL_get_error(ssl, ret); const int ssl_err = SSL_get_error(ssl, ret);
CheckOpenSSLErrors(); CheckOpenSSLErrors();
switch (ssl_err) { switch (ssl_err) {
case SSL_ERROR_NONE: case SSL_ERROR_NONE:
return actual; return ResultSuccess;
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_ZERO_RETURN", what); LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_ZERO_RETURN", what);
// DoHandshake special-cases this, but for Read and Write: // DoHandshake special-cases this, but for Read and Write:
return size_t(0); *actual = 0;
return ResultSuccess;
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_WANT_READ", what); LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_WANT_READ", what);
return ResultWouldBlock; return ResultWouldBlock;
@ -139,20 +138,20 @@ public:
default: default:
if (ssl_err == SSL_ERROR_SYSCALL && got_read_eof) { if (ssl_err == SSL_ERROR_SYSCALL && got_read_eof) {
LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_SYSCALL because server hung up", what); LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_SYSCALL because server hung up", what);
return size_t(0); *actual = 0;
return ResultSuccess;
} }
LOG_ERROR(Service_SSL, "{} => other SSL_get_error return value {}", what, ssl_err); LOG_ERROR(Service_SSL, "{} => other SSL_get_error return value {}", what, ssl_err);
return ResultInternalError; return ResultInternalError;
} }
} }
ResultVal<std::vector<std::vector<u8>>> GetServerCerts() override { Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) override {
STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl); STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
if (!chain) { if (!chain) {
LOG_ERROR(Service_SSL, "SSL_get_peer_cert_chain returned nullptr"); LOG_ERROR(Service_SSL, "SSL_get_peer_cert_chain returned nullptr");
return ResultInternalError; return ResultInternalError;
} }
std::vector<std::vector<u8>> ret;
int count = sk_X509_num(chain); int count = sk_X509_num(chain);
ASSERT(count >= 0); ASSERT(count >= 0);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@ -161,10 +160,10 @@ public:
unsigned char* buf = nullptr; unsigned char* buf = nullptr;
int len = i2d_X509(x509, &buf); int len = i2d_X509(x509, &buf);
ASSERT_OR_EXECUTE(len >= 0 && buf, { continue; }); ASSERT_OR_EXECUTE(len >= 0 && buf, { continue; });
ret.emplace_back(buf, buf + len); out_certs->emplace_back(buf, buf + len);
OPENSSL_free(buf); OPENSSL_free(buf);
} }
return ret; return ResultSuccess;
} }
~SSLConnectionBackendOpenSSL() { ~SSLConnectionBackendOpenSSL() {
@ -253,13 +252,13 @@ public:
std::shared_ptr<Network::SocketBase> socket; std::shared_ptr<Network::SocketBase> socket;
}; };
ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() { Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend) {
auto conn = std::make_unique<SSLConnectionBackendOpenSSL>(); auto conn = std::make_unique<SSLConnectionBackendOpenSSL>();
const Result res = conn->Init();
if (res.IsFailure()) { R_TRY(conn->Init());
return res;
} *out_backend = std::move(conn);
return conn; return ResultSuccess;
} }
namespace { namespace {

View File

@ -299,21 +299,22 @@ public:
return ResultSuccess; return ResultSuccess;
} }
ResultVal<size_t> Read(std::span<u8> data) override { Result Read(size_t* out_size, std::span<u8> data) override {
*out_size = 0;
if (handshake_state != HandshakeState::Connected) { if (handshake_state != HandshakeState::Connected) {
LOG_ERROR(Service_SSL, "Called Read but we did not successfully handshake"); LOG_ERROR(Service_SSL, "Called Read but we did not successfully handshake");
return ResultInternalError; return ResultInternalError;
} }
if (data.size() == 0 || got_read_eof) { if (data.size() == 0 || got_read_eof) {
return size_t(0); return ResultSuccess;
} }
while (1) { while (1) {
if (!cleartext_read_buf.empty()) { if (!cleartext_read_buf.empty()) {
const size_t read_size = std::min(cleartext_read_buf.size(), data.size()); *out_size = std::min(cleartext_read_buf.size(), data.size());
std::memcpy(data.data(), cleartext_read_buf.data(), read_size); std::memcpy(data.data(), cleartext_read_buf.data(), *out_size);
cleartext_read_buf.erase(cleartext_read_buf.begin(), cleartext_read_buf.erase(cleartext_read_buf.begin(),
cleartext_read_buf.begin() + read_size); cleartext_read_buf.begin() + *out_size);
return read_size; return ResultSuccess;
} }
if (!ciphertext_read_buf.empty()) { if (!ciphertext_read_buf.empty()) {
SecBuffer empty{ SecBuffer empty{
@ -366,7 +367,8 @@ public:
case SEC_I_CONTEXT_EXPIRED: case SEC_I_CONTEXT_EXPIRED:
// Server hung up by sending close_notify. // Server hung up by sending close_notify.
got_read_eof = true; got_read_eof = true;
return size_t(0); *out_size = 0;
return ResultSuccess;
default: default:
LOG_ERROR(Service_SSL, "DecryptMessage failed: {}", LOG_ERROR(Service_SSL, "DecryptMessage failed: {}",
Common::NativeErrorToString(ret)); Common::NativeErrorToString(ret));
@ -379,18 +381,21 @@ public:
} }
if (ciphertext_read_buf.empty()) { if (ciphertext_read_buf.empty()) {
got_read_eof = true; got_read_eof = true;
return size_t(0); *out_size = 0;
return ResultSuccess;
} }
} }
} }
ResultVal<size_t> Write(std::span<const u8> data) override { Result Write(size_t* out_size, std::span<const u8> data) override {
*out_size = 0;
if (handshake_state != HandshakeState::Connected) { if (handshake_state != HandshakeState::Connected) {
LOG_ERROR(Service_SSL, "Called Write but we did not successfully handshake"); LOG_ERROR(Service_SSL, "Called Write but we did not successfully handshake");
return ResultInternalError; return ResultInternalError;
} }
if (data.size() == 0) { if (data.size() == 0) {
return size_t(0); return ResultSuccess;
} }
data = data.subspan(0, std::min<size_t>(data.size(), stream_sizes.cbMaximumMessage)); data = data.subspan(0, std::min<size_t>(data.size(), stream_sizes.cbMaximumMessage));
if (!cleartext_write_buf.empty()) { if (!cleartext_write_buf.empty()) {
@ -402,7 +407,7 @@ public:
LOG_ERROR(Service_SSL, "Called Write but buffer does not match previous buffer"); LOG_ERROR(Service_SSL, "Called Write but buffer does not match previous buffer");
return ResultInternalError; return ResultInternalError;
} }
return WriteAlreadyEncryptedData(); return WriteAlreadyEncryptedData(out_size);
} else { } else {
cleartext_write_buf.assign(data.begin(), data.end()); cleartext_write_buf.assign(data.begin(), data.end());
} }
@ -448,21 +453,21 @@ public:
tmp_data_buf.end()); tmp_data_buf.end());
ciphertext_write_buf.insert(ciphertext_write_buf.end(), trailer_buf.begin(), ciphertext_write_buf.insert(ciphertext_write_buf.end(), trailer_buf.begin(),
trailer_buf.end()); trailer_buf.end());
return WriteAlreadyEncryptedData(); return WriteAlreadyEncryptedData(out_size);
} }
ResultVal<size_t> WriteAlreadyEncryptedData() { Result WriteAlreadyEncryptedData(size_t* out_size) {
const Result r = FlushCiphertextWriteBuf(); const Result r = FlushCiphertextWriteBuf();
if (r != ResultSuccess) { if (r != ResultSuccess) {
return r; return r;
} }
// write buf is empty // write buf is empty
const size_t cleartext_bytes_written = cleartext_write_buf.size(); *out_size = cleartext_write_buf.size();
cleartext_write_buf.clear(); cleartext_write_buf.clear();
return cleartext_bytes_written; return ResultSuccess;
} }
ResultVal<std::vector<std::vector<u8>>> GetServerCerts() override { Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) override {
PCCERT_CONTEXT returned_cert = nullptr; PCCERT_CONTEXT returned_cert = nullptr;
const SECURITY_STATUS ret = const SECURITY_STATUS ret =
QueryContextAttributes(&ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &returned_cert); QueryContextAttributes(&ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &returned_cert);
@ -473,16 +478,15 @@ public:
return ResultInternalError; return ResultInternalError;
} }
PCCERT_CONTEXT some_cert = nullptr; PCCERT_CONTEXT some_cert = nullptr;
std::vector<std::vector<u8>> certs;
while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert))) { while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert))) {
certs.emplace_back(static_cast<u8*>(some_cert->pbCertEncoded), out_certs->emplace_back(static_cast<u8*>(some_cert->pbCertEncoded),
static_cast<u8*>(some_cert->pbCertEncoded) + static_cast<u8*>(some_cert->pbCertEncoded) +
some_cert->cbCertEncoded); some_cert->cbCertEncoded);
} }
std::reverse(certs.begin(), std::reverse(out_certs->begin(),
certs.end()); // Windows returns certs in reverse order from what we want out_certs->end()); // Windows returns certs in reverse order from what we want
CertFreeCertificateContext(returned_cert); CertFreeCertificateContext(returned_cert);
return certs; return ResultSuccess;
} }
~SSLConnectionBackendSchannel() { ~SSLConnectionBackendSchannel() {
@ -532,13 +536,13 @@ public:
size_t read_buf_fill_size = 0; size_t read_buf_fill_size = 0;
}; };
ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() { Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend) {
auto conn = std::make_unique<SSLConnectionBackendSchannel>(); auto conn = std::make_unique<SSLConnectionBackendSchannel>();
const Result res = conn->Init();
if (res.IsFailure()) { R_TRY(conn->Init());
return res;
} *out_backend = std::move(conn);
return conn; return ResultSuccess;
} }
} // namespace Service::SSL } // namespace Service::SSL

View File

@ -100,27 +100,23 @@ public:
Result DoHandshake() override { Result DoHandshake() override {
OSStatus status = SSLHandshake(context); OSStatus status = SSLHandshake(context);
return HandleReturn("SSLHandshake", 0, status).Code(); return HandleReturn("SSLHandshake", 0, status);
} }
ResultVal<size_t> Read(std::span<u8> data) override { Result Read(size_t* out_size, std::span<u8> data) override {
size_t actual; OSStatus status = SSLRead(context, data.data(), data.size(), out_size);
OSStatus status = SSLRead(context, data.data(), data.size(), &actual); return HandleReturn("SSLRead", out_size, status);
;
return HandleReturn("SSLRead", actual, status);
} }
ResultVal<size_t> Write(std::span<const u8> data) override { Result Write(size_t* out_size, std::span<const u8> data) override {
size_t actual; OSStatus status = SSLWrite(context, data.data(), data.size(), out_size);
OSStatus status = SSLWrite(context, data.data(), data.size(), &actual); return HandleReturn("SSLWrite", out_size, status);
;
return HandleReturn("SSLWrite", actual, status);
} }
ResultVal<size_t> HandleReturn(const char* what, size_t actual, OSStatus status) { Result HandleReturn(const char* what, size_t* actual, OSStatus status) {
switch (status) { switch (status) {
case 0: case 0:
return actual; return ResultSuccess;
case errSSLWouldBlock: case errSSLWouldBlock:
return ResultWouldBlock; return ResultWouldBlock;
default: { default: {
@ -136,22 +132,21 @@ public:
} }
} }
ResultVal<std::vector<std::vector<u8>>> GetServerCerts() override { Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) override {
CFReleaser<SecTrustRef> trust; CFReleaser<SecTrustRef> trust;
OSStatus status = SSLCopyPeerTrust(context, &trust.ptr); OSStatus status = SSLCopyPeerTrust(context, &trust.ptr);
if (status) { if (status) {
LOG_ERROR(Service_SSL, "SSLCopyPeerTrust failed: {}", OSStatusToString(status)); LOG_ERROR(Service_SSL, "SSLCopyPeerTrust failed: {}", OSStatusToString(status));
return ResultInternalError; return ResultInternalError;
} }
std::vector<std::vector<u8>> ret;
for (CFIndex i = 0, count = SecTrustGetCertificateCount(trust); i < count; i++) { for (CFIndex i = 0, count = SecTrustGetCertificateCount(trust); i < count; i++) {
SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i); SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i);
CFReleaser<CFDataRef> data(SecCertificateCopyData(cert)); CFReleaser<CFDataRef> data(SecCertificateCopyData(cert));
ASSERT_OR_EXECUTE(data, { return ResultInternalError; }); ASSERT_OR_EXECUTE(data, { return ResultInternalError; });
const u8* ptr = CFDataGetBytePtr(data); const u8* ptr = CFDataGetBytePtr(data);
ret.emplace_back(ptr, ptr + CFDataGetLength(data)); out_certs->emplace_back(ptr, ptr + CFDataGetLength(data));
} }
return ret; return ResultSuccess;
} }
static OSStatus ReadCallback(SSLConnectionRef connection, void* data, size_t* dataLength) { static OSStatus ReadCallback(SSLConnectionRef connection, void* data, size_t* dataLength) {
@ -210,13 +205,13 @@ private:
std::shared_ptr<Network::SocketBase> socket; std::shared_ptr<Network::SocketBase> socket;
}; };
ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() { Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend) {
auto conn = std::make_unique<SSLConnectionBackendSecureTransport>(); auto conn = std::make_unique<SSLConnectionBackendSecureTransport>();
const Result res = conn->Init();
if (res.IsFailure()) { R_TRY(conn->Init());
return res;
} *out_backend = std::move(conn);
return conn; return ResultSuccess;
} }
} // namespace Service::SSL } // namespace Service::SSL

View File

@ -58,14 +58,15 @@ const Layer& Display::GetLayer(std::size_t index) const {
return *layers.at(index); return *layers.at(index);
} }
ResultVal<Kernel::KReadableEvent*> Display::GetVSyncEvent() { Result Display::GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event) {
if (got_vsync_event) { if (got_vsync_event) {
return ResultPermissionDenied; return ResultPermissionDenied;
} }
got_vsync_event = true; got_vsync_event = true;
return GetVSyncEventUnchecked(); *out_vsync_event = GetVSyncEventUnchecked();
return ResultSuccess;
} }
Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() {

View File

@ -85,7 +85,7 @@ public:
* @returns The internal Vsync event if it has not yet been retrieved, * @returns The internal Vsync event if it has not yet been retrieved,
* VI::ResultPermissionDenied otherwise. * VI::ResultPermissionDenied otherwise.
*/ */
[[nodiscard]] ResultVal<Kernel::KReadableEvent*> GetVSyncEvent(); [[nodiscard]] Result GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event);
/// Gets the internal vsync event. /// Gets the internal vsync event.
Kernel::KReadableEvent* GetVSyncEventUnchecked(); Kernel::KReadableEvent* GetVSyncEventUnchecked();

View File

@ -683,9 +683,9 @@ private:
LOG_DEBUG(Service_VI, "called. display_id={}", display_id); LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); Kernel::KReadableEvent* vsync_event{};
if (vsync_event.Failed()) { const auto result = nv_flinger.FindVsyncEvent(&vsync_event, display_id);
const auto result = vsync_event.Code(); if (result != ResultSuccess) {
if (result == ResultNotFound) { if (result == ResultNotFound) {
LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
} }
@ -697,7 +697,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(*vsync_event); rb.PushCopyObjects(vsync_event);
} }
void ConvertScalingMode(HLERequestContext& ctx) { void ConvertScalingMode(HLERequestContext& ctx) {
@ -705,15 +705,16 @@ private:
const auto mode = rp.PopEnum<NintendoScaleMode>(); const auto mode = rp.PopEnum<NintendoScaleMode>();
LOG_DEBUG(Service_VI, "called mode={}", mode); LOG_DEBUG(Service_VI, "called mode={}", mode);
const auto converted_mode = ConvertScalingModeImpl(mode); ConvertedScaleMode converted_mode{};
const auto result = ConvertScalingModeImpl(&converted_mode, mode);
if (converted_mode.Succeeded()) { if (result == ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushEnum(*converted_mode); rb.PushEnum(converted_mode);
} else { } else {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(converted_mode.Code()); rb.Push(result);
} }
} }
@ -760,18 +761,24 @@ private:
rb.Push(alignment); rb.Push(alignment);
} }
static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) { static Result ConvertScalingModeImpl(ConvertedScaleMode* out_scaling_mode,
NintendoScaleMode mode) {
switch (mode) { switch (mode) {
case NintendoScaleMode::None: case NintendoScaleMode::None:
return ConvertedScaleMode::None; *out_scaling_mode = ConvertedScaleMode::None;
return ResultSuccess;
case NintendoScaleMode::Freeze: case NintendoScaleMode::Freeze:
return ConvertedScaleMode::Freeze; *out_scaling_mode = ConvertedScaleMode::Freeze;
return ResultSuccess;
case NintendoScaleMode::ScaleToWindow: case NintendoScaleMode::ScaleToWindow:
return ConvertedScaleMode::ScaleToWindow; *out_scaling_mode = ConvertedScaleMode::ScaleToWindow;
return ResultSuccess;
case NintendoScaleMode::ScaleAndCrop: case NintendoScaleMode::ScaleAndCrop:
return ConvertedScaleMode::ScaleAndCrop; *out_scaling_mode = ConvertedScaleMode::ScaleAndCrop;
return ResultSuccess;
case NintendoScaleMode::PreserveAspectRatio: case NintendoScaleMode::PreserveAspectRatio:
return ConvertedScaleMode::PreserveAspectRatio; *out_scaling_mode = ConvertedScaleMode::PreserveAspectRatio;
return ResultSuccess;
default: default:
LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode);
return ResultOperationFailed; return ResultOperationFailed;

View File

@ -476,7 +476,13 @@ NetworkInstance::~NetworkInstance() {
std::optional<IPv4Address> GetHostIPv4Address() { std::optional<IPv4Address> GetHostIPv4Address() {
const auto network_interface = Network::GetSelectedNetworkInterface(); const auto network_interface = Network::GetSelectedNetworkInterface();
if (!network_interface.has_value()) { if (!network_interface.has_value()) {
LOG_DEBUG(Network, "GetSelectedNetworkInterface returned no interface"); // Only print the error once to avoid log spam
static bool print_error = true;
if (print_error) {
LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
print_error = false;
}
return {}; return {};
} }

View File

@ -200,7 +200,14 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
}); });
if (res == network_interfaces.end()) { if (res == network_interfaces.end()) {
LOG_DEBUG(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); // Only print the error once to avoid log spam
static bool print_error = true;
if (print_error) {
LOG_ERROR(Network, "Couldn't find selected interface \"{}\"",
selected_network_interface);
print_error = false;
}
return std::nullopt; return std::nullopt;
} }

View File

@ -509,9 +509,9 @@ class GuestMemory {
public: public:
GuestMemory() = delete; GuestMemory() = delete;
explicit GuestMemory(M& memory_, u64 addr_, std::size_t size_, explicit GuestMemory(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) Common::ScratchBuffer<T>* backup = nullptr)
: memory{memory_}, addr{addr_}, size{size_} { : m_memory{memory}, m_addr{addr}, m_size{size} {
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write); static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
if constexpr (FLAGS & GuestMemoryFlags::Read) { if constexpr (FLAGS & GuestMemoryFlags::Read) {
Read(addr, size, backup); Read(addr, size, backup);
@ -521,89 +521,97 @@ public:
~GuestMemory() = default; ~GuestMemory() = default;
T* data() noexcept { T* data() noexcept {
return data_span.data(); return m_data_span.data();
} }
const T* data() const noexcept { const T* data() const noexcept {
return data_span.data(); return m_data_span.data();
}
size_t size() const noexcept {
return m_size;
}
size_t size_bytes() const noexcept {
return this->size() * sizeof(T);
} }
[[nodiscard]] T* begin() noexcept { [[nodiscard]] T* begin() noexcept {
return data(); return this->data();
} }
[[nodiscard]] const T* begin() const noexcept { [[nodiscard]] const T* begin() const noexcept {
return data(); return this->data();
} }
[[nodiscard]] T* end() noexcept { [[nodiscard]] T* end() noexcept {
return data() + size; return this->data() + this->size();
} }
[[nodiscard]] const T* end() const noexcept { [[nodiscard]] const T* end() const noexcept {
return data() + size; return this->data() + this->size();
} }
T& operator[](size_t index) noexcept { T& operator[](size_t index) noexcept {
return data_span[index]; return m_data_span[index];
} }
const T& operator[](size_t index) const noexcept { const T& operator[](size_t index) const noexcept {
return data_span[index]; return m_data_span[index];
} }
void SetAddressAndSize(u64 addr_, std::size_t size_) noexcept { void SetAddressAndSize(u64 addr, std::size_t size) noexcept {
addr = addr_; m_addr = addr;
size = size_; m_size = size;
addr_changed = true; m_addr_changed = true;
} }
std::span<T> Read(u64 addr_, std::size_t size_, std::span<T> Read(u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) noexcept { Common::ScratchBuffer<T>* backup = nullptr) noexcept {
addr = addr_; m_addr = addr;
size = size_; m_size = size;
if (size == 0) { if (m_size == 0) {
is_data_copy = true; m_is_data_copy = true;
return {}; return {};
} }
if (TrySetSpan()) { if (this->TrySetSpan()) {
if constexpr (FLAGS & GuestMemoryFlags::Safe) { if constexpr (FLAGS & GuestMemoryFlags::Safe) {
memory.FlushRegion(addr, size * sizeof(T)); m_memory.FlushRegion(m_addr, this->size_bytes());
} }
} else { } else {
if (backup) { if (backup) {
backup->resize_destructive(size); backup->resize_destructive(this->size());
data_span = *backup; m_data_span = *backup;
} else { } else {
data_copy.resize(size); m_data_copy.resize(this->size());
data_span = std::span(data_copy); m_data_span = std::span(m_data_copy);
} }
is_data_copy = true; m_is_data_copy = true;
span_valid = true; m_span_valid = true;
if constexpr (FLAGS & GuestMemoryFlags::Safe) { if constexpr (FLAGS & GuestMemoryFlags::Safe) {
memory.ReadBlock(addr, data_span.data(), size * sizeof(T)); m_memory.ReadBlock(m_addr, this->data(), this->size_bytes());
} else { } else {
memory.ReadBlockUnsafe(addr, data_span.data(), size * sizeof(T)); m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
} }
} }
return data_span; return m_data_span;
} }
void Write(std::span<T> write_data) noexcept { void Write(std::span<T> write_data) noexcept {
if constexpr (FLAGS & GuestMemoryFlags::Cached) { if constexpr (FLAGS & GuestMemoryFlags::Cached) {
memory.WriteBlockCached(addr, write_data.data(), size * sizeof(T)); m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) { } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
memory.WriteBlock(addr, write_data.data(), size * sizeof(T)); m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes());
} else { } else {
memory.WriteBlockUnsafe(addr, write_data.data(), size * sizeof(T)); m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
} }
} }
bool TrySetSpan() noexcept { bool TrySetSpan() noexcept {
if (u8* ptr = memory.GetSpan(addr, size * sizeof(T)); ptr) { if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) {
data_span = {reinterpret_cast<T*>(ptr), size}; m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
span_valid = true; m_span_valid = true;
return true; return true;
} }
return false; return false;
@ -611,36 +619,36 @@ public:
protected: protected:
bool IsDataCopy() const noexcept { bool IsDataCopy() const noexcept {
return is_data_copy; return m_is_data_copy;
} }
bool AddressChanged() const noexcept { bool AddressChanged() const noexcept {
return addr_changed; return m_addr_changed;
} }
M& memory; M& m_memory;
u64 addr; u64 m_addr{};
size_t size; size_t m_size{};
std::span<T> data_span{}; std::span<T> m_data_span{};
std::vector<T> data_copy; std::vector<T> m_data_copy{};
bool span_valid{false}; bool m_span_valid{false};
bool is_data_copy{false}; bool m_is_data_copy{false};
bool addr_changed{false}; bool m_addr_changed{false};
}; };
template <typename M, typename T, GuestMemoryFlags FLAGS> template <typename M, typename T, GuestMemoryFlags FLAGS>
class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> { class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> {
public: public:
GuestMemoryScoped() = delete; GuestMemoryScoped() = delete;
explicit GuestMemoryScoped(M& memory_, u64 addr_, std::size_t size_, explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) Common::ScratchBuffer<T>* backup = nullptr)
: GuestMemory<M, T, FLAGS>(memory_, addr_, size_, backup) { : GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) { if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
if (!this->TrySetSpan()) { if (!this->TrySetSpan()) {
if (backup) { if (backup) {
this->data_span = *backup; this->m_data_span = *backup;
this->span_valid = true; this->m_span_valid = true;
this->is_data_copy = true; this->m_is_data_copy = true;
} }
} }
} }
@ -648,24 +656,21 @@ public:
~GuestMemoryScoped() { ~GuestMemoryScoped() {
if constexpr (FLAGS & GuestMemoryFlags::Write) { if constexpr (FLAGS & GuestMemoryFlags::Write) {
if (this->size == 0) [[unlikely]] { if (this->size() == 0) [[unlikely]] {
return; return;
} }
if (this->AddressChanged() || this->IsDataCopy()) { if (this->AddressChanged() || this->IsDataCopy()) {
ASSERT(this->span_valid); ASSERT(this->m_span_valid);
if constexpr (FLAGS & GuestMemoryFlags::Cached) { if constexpr (FLAGS & GuestMemoryFlags::Cached) {
this->memory.WriteBlockCached(this->addr, this->data_span.data(), this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes());
this->size * sizeof(T));
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) { } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
this->memory.WriteBlock(this->addr, this->data_span.data(), this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes());
this->size * sizeof(T));
} else { } else {
this->memory.WriteBlockUnsafe(this->addr, this->data_span.data(), this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
this->size * sizeof(T));
} }
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) { } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
this->memory.InvalidateRegion(this->addr, this->size * sizeof(T)); this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
} }
} }
} }

View File

@ -5,6 +5,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/polyfill_ranges.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "core/memory.h" #include "core/memory.h"

View File

@ -220,7 +220,8 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
ASSERT(num_textures <= MAX_TEXTURES); ASSERT(num_textures <= MAX_TEXTURES);
ASSERT(num_images <= MAX_IMAGES); ASSERT(num_images <= MAX_IMAGES);
const bool assembly_shaders{assembly_programs[0].handle != 0}; const auto backend = device.GetShaderBackend();
const bool assembly_shaders{backend == Settings::ShaderBackend::Glasm};
use_storage_buffers = use_storage_buffers =
!assembly_shaders || num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks(); !assembly_shaders || num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
writes_global_memory &= !use_storage_buffers; writes_global_memory &= !use_storage_buffers;
@ -230,7 +231,6 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
GenerateTransformFeedbackState(); GenerateTransformFeedbackState();
} }
const bool in_parallel = thread_worker != nullptr; const bool in_parallel = thread_worker != nullptr;
const auto backend = device.GetShaderBackend();
auto func{[this, sources_ = std::move(sources), sources_spirv_ = std::move(sources_spirv), auto func{[this, sources_ = std::move(sources), sources_spirv_ = std::move(sources_spirv),
shader_notify, backend, in_parallel, shader_notify, backend, in_parallel,
force_context_flush](ShaderContext::Context*) mutable { force_context_flush](ShaderContext::Context*) mutable {
@ -559,15 +559,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
} }
void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { void GraphicsPipeline::ConfigureTransformFeedbackImpl() const {
glTransformFeedbackStreamAttribsNV(num_xfb_attribs, xfb_attribs.data(), num_xfb_strides, glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), GL_SEPARATE_ATTRIBS);
xfb_streams.data(), GL_INTERLEAVED_ATTRIBS);
} }
void GraphicsPipeline::GenerateTransformFeedbackState() { void GraphicsPipeline::GenerateTransformFeedbackState() {
// TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal // TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
// when this is required. // when this is required.
GLint* cursor{xfb_attribs.data()}; GLint* cursor{xfb_attribs.data()};
GLint* current_stream{xfb_streams.data()};
for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) { for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
const auto& layout = key.xfb_state.layouts[feedback]; const auto& layout = key.xfb_state.layouts[feedback];
@ -575,15 +573,6 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
if (layout.varying_count == 0) { if (layout.varying_count == 0) {
continue; continue;
} }
*current_stream = static_cast<GLint>(feedback);
if (current_stream != xfb_streams.data()) {
// When stepping one stream, push the expected token
cursor[0] = GL_NEXT_BUFFER_NV;
cursor[1] = 0;
cursor[2] = 0;
cursor += XFB_ENTRY_STRIDE;
}
++current_stream;
const auto& locations = key.xfb_state.varyings[feedback]; const auto& locations = key.xfb_state.varyings[feedback];
std::optional<u32> current_index; std::optional<u32> current_index;
@ -619,7 +608,6 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
} }
} }
num_xfb_attribs = static_cast<GLsizei>((cursor - xfb_attribs.data()) / XFB_ENTRY_STRIDE); num_xfb_attribs = static_cast<GLsizei>((cursor - xfb_attribs.data()) / XFB_ENTRY_STRIDE);
num_xfb_strides = static_cast<GLsizei>(current_stream - xfb_streams.data());
} }
void GraphicsPipeline::WaitForBuild() { void GraphicsPipeline::WaitForBuild() {

View File

@ -154,9 +154,7 @@ private:
static constexpr std::size_t XFB_ENTRY_STRIDE = 3; static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
GLsizei num_xfb_attribs{}; GLsizei num_xfb_attribs{};
GLsizei num_xfb_strides{};
std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{}; std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{};
std::array<GLint, Maxwell::NumTransformFeedbackBuffers> xfb_streams{};
std::mutex built_mutex; std::mutex built_mutex;
std::condition_variable built_condvar; std::condition_variable built_condvar;

View File

@ -326,6 +326,43 @@ std::vector<const char*> ExtensionListForVulkan(
} // Anonymous namespace } // Anonymous namespace
void Device::RemoveExtension(bool& extension, const std::string& extension_name) {
extension = false;
loaded_extensions.erase(extension_name);
}
void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) {
if (loaded_extensions.contains(extension_name) && !is_suitable) {
LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name);
this->RemoveExtension(is_suitable, extension_name);
}
}
template <typename Feature>
void Device::RemoveExtensionFeature(bool& extension, Feature& feature,
const std::string& extension_name) {
// Unload extension.
this->RemoveExtension(extension, extension_name);
// Save sType and pNext for chain.
VkStructureType sType = feature.sType;
void* pNext = feature.pNext;
// Clear feature struct and restore chain.
feature = {};
feature.sType = sType;
feature.pNext = pNext;
}
template <typename Feature>
void Device::RemoveExtensionFeatureIfUnsuitable(bool is_suitable, Feature& feature,
const std::string& extension_name) {
if (loaded_extensions.contains(extension_name) && !is_suitable) {
LOG_WARNING(Render_Vulkan, "Removing features for unsuitable extension {}", extension_name);
this->RemoveExtensionFeature(is_suitable, feature, extension_name);
}
}
Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
const vk::InstanceDispatch& dld_) const vk::InstanceDispatch& dld_)
: instance{instance_}, dld{dld_}, physical{physical_}, : instance{instance_}, dld{dld_}, physical{physical_},
@ -397,21 +434,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (is_qualcomm || is_turnip) { if (is_qualcomm || is_turnip) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color"); "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
extensions.custom_border_color = false; RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
loaded_extensions.erase(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
} }
if (is_qualcomm) { if (is_qualcomm) {
must_emulate_scaled_formats = true; must_emulate_scaled_formats = true;
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state"); LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state");
extensions.extended_dynamic_state = false; RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"Qualcomm drivers have a slow VK_KHR_push_descriptor implementation"); "Qualcomm drivers have a slow VK_KHR_push_descriptor implementation");
extensions.push_descriptor = false; RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
#if defined(ANDROID) && defined(ARCHITECTURE_arm64) #if defined(ANDROID) && defined(ARCHITECTURE_arm64)
// Patch the driver to enable BCn textures. // Patch the driver to enable BCn textures.
@ -440,15 +476,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
must_emulate_scaled_formats = true; must_emulate_scaled_formats = true;
LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state"); LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
extensions.extended_dynamic_state = false; RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2"); LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2");
features.extended_dynamic_state2.extendedDynamicState2 = false; RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
extensions.extended_dynamic_state2 = false;
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
} }
if (is_nvidia) { if (is_nvidia) {
@ -464,8 +497,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
case NvidiaArchitecture::VoltaOrOlder: case NvidiaArchitecture::VoltaOrOlder:
if (nv_major_version < 527) { if (nv_major_version < 527) {
LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor"); LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor");
extensions.push_descriptor = false; RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
} }
break; break;
} }
@ -480,8 +512,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state"); "RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state");
extensions.extended_dynamic_state = false; RemoveExtensionFeature(extensions.extended_dynamic_state,
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); features.extended_dynamic_state,
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
if (extensions.extended_dynamic_state2 && is_radv) { if (extensions.extended_dynamic_state2 && is_radv) {
@ -490,11 +523,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_WARNING( LOG_WARNING(
Render_Vulkan, Render_Vulkan,
"RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2"); "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
features.extended_dynamic_state2.extendedDynamicState2 = false; RemoveExtensionFeature(extensions.extended_dynamic_state2,
features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; features.extended_dynamic_state2,
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
extensions.extended_dynamic_state2 = false;
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
} }
} }
if (extensions.extended_dynamic_state2 && is_qualcomm) { if (extensions.extended_dynamic_state2 && is_qualcomm) {
@ -504,11 +535,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
// Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2. // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2.
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2"); "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2");
features.extended_dynamic_state2.extendedDynamicState2 = false; RemoveExtensionFeature(extensions.extended_dynamic_state2,
features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; features.extended_dynamic_state2,
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
extensions.extended_dynamic_state2 = false;
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
} }
} }
if (extensions.extended_dynamic_state3 && is_radv) { if (extensions.extended_dynamic_state3 && is_radv) {
@ -540,9 +569,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (is_rdna2) { if (is_rdna2) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware"); "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
features.vertex_input_dynamic_state.vertexInputDynamicState = false; RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
extensions.vertex_input_dynamic_state = false; features.vertex_input_dynamic_state,
loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
if (extensions.vertex_input_dynamic_state && is_qualcomm) { if (extensions.vertex_input_dynamic_state && is_qualcomm) {
@ -553,9 +582,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_WARNING( LOG_WARNING(
Render_Vulkan, Render_Vulkan,
"Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state"); "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state");
features.vertex_input_dynamic_state.vertexInputDynamicState = false; RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
extensions.vertex_input_dynamic_state = false; features.vertex_input_dynamic_state,
loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
@ -575,8 +604,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (!features.shader_float16_int8.shaderFloat16) { if (!features.shader_float16_int8.shaderFloat16) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax"); "AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax");
extensions.sampler_filter_minmax = false; RemoveExtension(extensions.sampler_filter_minmax,
loaded_extensions.erase(VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME); VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME);
} }
} }
@ -584,8 +613,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const u32 version = (properties.properties.driverVersion << 3) >> 3; const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) { if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state"); LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state");
extensions.vertex_input_dynamic_state = false; RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) { if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
@ -612,8 +642,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
// mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc // mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor"); "ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor");
extensions.push_descriptor = false; RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
} }
} }
if (is_mvk) { if (is_mvk) {
@ -1007,34 +1036,29 @@ bool Device::GetSuitability(bool requires_swapchain) {
return suitable; return suitable;
} }
void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) {
if (loaded_extensions.contains(extension_name) && !is_suitable) {
LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name);
loaded_extensions.erase(extension_name);
}
}
void Device::RemoveUnsuitableExtensions() { void Device::RemoveUnsuitableExtensions() {
// VK_EXT_custom_border_color // VK_EXT_custom_border_color
extensions.custom_border_color = features.custom_border_color.customBorderColors && extensions.custom_border_color = features.custom_border_color.customBorderColors &&
features.custom_border_color.customBorderColorWithoutFormat; features.custom_border_color.customBorderColorWithoutFormat;
RemoveExtensionIfUnsuitable(extensions.custom_border_color, RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color,
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
// VK_EXT_depth_clip_control // VK_EXT_depth_clip_control
extensions.depth_clip_control = features.depth_clip_control.depthClipControl; extensions.depth_clip_control = features.depth_clip_control.depthClipControl;
RemoveExtensionIfUnsuitable(extensions.depth_clip_control, RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control,
VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
// VK_EXT_extended_dynamic_state // VK_EXT_extended_dynamic_state
extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState; extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state, RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state,
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); features.extended_dynamic_state,
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
// VK_EXT_extended_dynamic_state2 // VK_EXT_extended_dynamic_state2
extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2; extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2;
RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state2, RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state2,
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); features.extended_dynamic_state2,
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
// VK_EXT_extended_dynamic_state3 // VK_EXT_extended_dynamic_state3
dynamic_state3_blending = dynamic_state3_blending =
@ -1048,35 +1072,38 @@ void Device::RemoveUnsuitableExtensions() {
extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables; extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables;
dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3; dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3;
dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3; dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3;
RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state3, RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state3,
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); features.extended_dynamic_state3,
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
// VK_EXT_provoking_vertex // VK_EXT_provoking_vertex
extensions.provoking_vertex = extensions.provoking_vertex =
features.provoking_vertex.provokingVertexLast && features.provoking_vertex.provokingVertexLast &&
features.provoking_vertex.transformFeedbackPreservesProvokingVertex; features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
RemoveExtensionIfUnsuitable(extensions.provoking_vertex, RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex, features.provoking_vertex,
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
// VK_KHR_shader_atomic_int64 // VK_KHR_shader_atomic_int64
extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics && extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics &&
features.shader_atomic_int64.shaderSharedInt64Atomics; features.shader_atomic_int64.shaderSharedInt64Atomics;
RemoveExtensionIfUnsuitable(extensions.shader_atomic_int64, RemoveExtensionFeatureIfUnsuitable(extensions.shader_atomic_int64, features.shader_atomic_int64,
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
// VK_EXT_shader_demote_to_helper_invocation // VK_EXT_shader_demote_to_helper_invocation
extensions.shader_demote_to_helper_invocation = extensions.shader_demote_to_helper_invocation =
features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation; features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation;
RemoveExtensionIfUnsuitable(extensions.shader_demote_to_helper_invocation, RemoveExtensionFeatureIfUnsuitable(extensions.shader_demote_to_helper_invocation,
VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); features.shader_demote_to_helper_invocation,
VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME);
// VK_EXT_subgroup_size_control // VK_EXT_subgroup_size_control
extensions.subgroup_size_control = extensions.subgroup_size_control =
features.subgroup_size_control.subgroupSizeControl && features.subgroup_size_control.subgroupSizeControl &&
properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize && properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize &&
properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize; properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize;
RemoveExtensionIfUnsuitable(extensions.subgroup_size_control, RemoveExtensionFeatureIfUnsuitable(extensions.subgroup_size_control,
VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); features.subgroup_size_control,
VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
// VK_EXT_transform_feedback // VK_EXT_transform_feedback
extensions.transform_feedback = extensions.transform_feedback =
@ -1086,24 +1113,27 @@ void Device::RemoveUnsuitableExtensions() {
properties.transform_feedback.maxTransformFeedbackBuffers > 0 && properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
properties.transform_feedback.transformFeedbackQueries && properties.transform_feedback.transformFeedbackQueries &&
properties.transform_feedback.transformFeedbackDraw; properties.transform_feedback.transformFeedbackDraw;
RemoveExtensionIfUnsuitable(extensions.transform_feedback, RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback,
VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
// VK_EXT_vertex_input_dynamic_state // VK_EXT_vertex_input_dynamic_state
extensions.vertex_input_dynamic_state = extensions.vertex_input_dynamic_state =
features.vertex_input_dynamic_state.vertexInputDynamicState; features.vertex_input_dynamic_state.vertexInputDynamicState;
RemoveExtensionIfUnsuitable(extensions.vertex_input_dynamic_state, RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
// VK_KHR_pipeline_executable_properties // VK_KHR_pipeline_executable_properties
if (Settings::values.renderer_shader_feedback.GetValue()) { if (Settings::values.renderer_shader_feedback.GetValue()) {
extensions.pipeline_executable_properties = extensions.pipeline_executable_properties =
features.pipeline_executable_properties.pipelineExecutableInfo; features.pipeline_executable_properties.pipelineExecutableInfo;
RemoveExtensionIfUnsuitable(extensions.pipeline_executable_properties, RemoveExtensionFeatureIfUnsuitable(extensions.pipeline_executable_properties,
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); features.pipeline_executable_properties,
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
} else { } else {
extensions.pipeline_executable_properties = false; RemoveExtensionFeature(extensions.pipeline_executable_properties,
loaded_extensions.erase(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); features.pipeline_executable_properties,
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
} }
// VK_KHR_workgroup_memory_explicit_layout // VK_KHR_workgroup_memory_explicit_layout
@ -1113,8 +1143,9 @@ void Device::RemoveUnsuitableExtensions() {
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess && features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess &&
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess && features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess &&
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout; features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout;
RemoveExtensionIfUnsuitable(extensions.workgroup_memory_explicit_layout, RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout,
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); features.workgroup_memory_explicit_layout,
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
} }
void Device::SetupFamilies(VkSurfaceKHR surface) { void Device::SetupFamilies(VkSurfaceKHR surface) {

View File

@ -639,8 +639,17 @@ private:
// Remove extensions which have incomplete feature support. // Remove extensions which have incomplete feature support.
void RemoveUnsuitableExtensions(); void RemoveUnsuitableExtensions();
void RemoveExtension(bool& extension, const std::string& extension_name);
void RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name); void RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name);
template <typename Feature>
void RemoveExtensionFeature(bool& extension, Feature& feature,
const std::string& extension_name);
template <typename Feature>
void RemoveExtensionFeatureIfUnsuitable(bool is_suitable, Feature& feature,
const std::string& extension_name);
/// Sets up queue families. /// Sets up queue families.
void SetupFamilies(VkSurfaceKHR surface); void SetupFamilies(VkSurfaceKHR surface);

View File

@ -214,13 +214,17 @@ void GameList::OnTextChanged(const QString& new_text) {
const int children_count = folder->rowCount(); const int children_count = folder->rowCount();
for (int j = 0; j < children_count; ++j) { for (int j = 0; j < children_count; ++j) {
++children_total; ++children_total;
const QStandardItem* child = folder->child(j, 0); const QStandardItem* child = folder->child(j, 0);
const auto program_id = child->data(GameListItemPath::ProgramIdRole).toULongLong();
const QString file_path = const QString file_path =
child->data(GameListItemPath::FullPathRole).toString().toLower(); child->data(GameListItemPath::FullPathRole).toString().toLower();
const QString file_title = const QString file_title =
child->data(GameListItemPath::TitleRole).toString().toLower(); child->data(GameListItemPath::TitleRole).toString().toLower();
const QString file_program_id = const QString file_program_id =
child->data(GameListItemPath::ProgramIdRole).toString().toLower(); QStringLiteral("%1").arg(program_id, 16, 16, QLatin1Char{'0'});
// Only items which filename in combination with its title contains all words // Only items which filename in combination with its title contains all words
// that are in the searchfield will be visible in the gamelist // that are in the searchfield will be visible in the gamelist
@ -231,7 +235,7 @@ void GameList::OnTextChanged(const QString& new_text) {
file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} +
file_title; file_title;
if (ContainsAllWords(file_name, edit_filter_text) || if (ContainsAllWords(file_name, edit_filter_text) ||
(file_program_id.count() == 16 && edit_filter_text.contains(file_program_id))) { (file_program_id.count() == 16 && file_program_id.contains(edit_filter_text))) {
tree_view->setRowHidden(j, folder_index, false); tree_view->setRowHidden(j, folder_index, false);
++result_count; ++result_count;
} else { } else {