Compare commits
41 Commits
android-34
...
android-43
Author | SHA1 | Date | |
---|---|---|---|
42c8c5a059 | |||
ddedaa8875 | |||
0e3a995bf4 | |||
755bcc459b | |||
a8c4f01f6c | |||
6d665a94ea | |||
bbc6b08fc7 | |||
fbda084acb | |||
2694f81462 | |||
d5adaeafdf | |||
58a4c86797 | |||
35a77c3bb2 | |||
c1016b68ae | |||
b30df50076 | |||
5afe1367ba | |||
24700af3c2 | |||
f9ef721ca6 | |||
c34ed4bbd8 | |||
7351884588 | |||
242ce2a0b3 | |||
8ab3685a39 | |||
8bd0521b58 | |||
64ea5522d3 | |||
798a439eb1 | |||
786b609151 | |||
89a2d308c3 | |||
0d4bf53ad9 | |||
8b98c4e5a0 | |||
26ff214719 | |||
640f7cd945 | |||
7d8f748696 | |||
bdd96118d1 | |||
1ed9e8812b | |||
9d3a293a4e | |||
3d6ce9dd2b | |||
023b9b38cc | |||
6a43aff745 | |||
1e394c6cdf | |||
37b278a9a8 | |||
83eee1d226 | |||
84cb20bc72 |
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
@ -134,7 +134,7 @@ else()
|
||||
endif()
|
||||
|
||||
# GCC bugs
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# These diagnostics would be great if they worked, but are just completely broken
|
||||
# and produce bogus errors on external libraries like fmt.
|
||||
add_compile_options(
|
||||
|
@ -49,6 +49,7 @@ class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List
|
||||
val context = YuzuApplication.appContext
|
||||
binding.textSettingName.text = context.getString(license.titleId)
|
||||
binding.textSettingDescription.text = context.getString(license.descriptionId)
|
||||
binding.textSettingValue.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,13 +5,19 @@ package org.yuzu.yuzu_emu.adapters
|
||||
|
||||
import android.text.Html
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.button.MaterialButton
|
||||
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.StepState
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils
|
||||
|
||||
class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
|
||||
RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
|
||||
@ -26,7 +32,7 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
|
||||
holder.bind(pages[position])
|
||||
|
||||
inner class SetupPageViewHolder(val binding: PageSetupBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
RecyclerView.ViewHolder(binding.root), SetupCallback {
|
||||
lateinit var page: SetupPage
|
||||
|
||||
init {
|
||||
@ -35,6 +41,12 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
|
||||
|
||||
fun bind(page: SetupPage) {
|
||||
this.page = page
|
||||
|
||||
if (page.stepCompleted.invoke() == StepState.COMPLETE) {
|
||||
binding.buttonAction.visibility = View.INVISIBLE
|
||||
binding.textConfirmation.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
binding.icon.setImageDrawable(
|
||||
ResourcesCompat.getDrawable(
|
||||
activity.resources,
|
||||
@ -62,9 +74,15 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
|
||||
MaterialButton.ICON_GRAVITY_END
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -207,8 +207,11 @@ class SettingsAdapter(
|
||||
val sliderBinding = DialogSliderBinding.inflate(inflater)
|
||||
|
||||
textSliderValue = sliderBinding.textValue
|
||||
textSliderValue!!.text = sliderProgress.toString()
|
||||
sliderBinding.textUnits.text = item.units
|
||||
textSliderValue!!.text = String.format(
|
||||
context.getString(R.string.value_with_units),
|
||||
sliderProgress.toString(),
|
||||
item.units
|
||||
)
|
||||
|
||||
sliderBinding.slider.apply {
|
||||
valueFrom = item.min.toFloat()
|
||||
@ -216,7 +219,11 @@ class SettingsAdapter(
|
||||
value = sliderProgress.toFloat()
|
||||
addOnChangeListener { _: Slider, value: Float, _: Boolean ->
|
||||
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)
|
||||
.setPositiveButton(android.R.string.ok, this)
|
||||
.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()
|
||||
}
|
||||
|
||||
|
@ -25,12 +25,17 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
|
||||
binding.textSettingDescription.setText(item.descriptionId)
|
||||
binding.textSettingDescription.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.textSettingDescription.visibility = View.GONE
|
||||
}
|
||||
|
||||
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.textSettingDescription.text = dateFormatter.format(zonedTime)
|
||||
}
|
||||
binding.textSettingValue.text = dateFormatter.format(zonedTime)
|
||||
|
||||
setStyle(setting.isEditable, binding)
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
|
@ -23,6 +23,9 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
|
||||
} else {
|
||||
binding.textSettingDescription.visibility = View.GONE
|
||||
}
|
||||
binding.textSettingValue.visibility = View.GONE
|
||||
|
||||
setStyle(setting.isEditable, binding)
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
|
@ -5,6 +5,8 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.View
|
||||
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.ui.SettingsAdapter
|
||||
|
||||
@ -33,4 +35,18 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings
|
||||
abstract override fun onClick(clicked: View)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -17,28 +17,33 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
|
||||
override fun bind(item: SettingsItem) {
|
||||
setting = item
|
||||
binding.textSettingName.setText(item.nameId)
|
||||
binding.textSettingDescription.visibility = View.VISIBLE
|
||||
if (item.descriptionId != 0) {
|
||||
binding.textSettingDescription.setText(item.descriptionId)
|
||||
} else if (item is SingleChoiceSetting) {
|
||||
val resMgr = binding.textSettingDescription.context.resources
|
||||
binding.textSettingDescription.visibility = View.VISIBLE
|
||||
} 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)
|
||||
for (i in values.indices) {
|
||||
if (values[i] == item.selectedValue) {
|
||||
binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i]
|
||||
return
|
||||
binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if (item is StringSingleChoiceSetting) {
|
||||
for (i in item.values!!.indices) {
|
||||
if (item.values[i] == item.selectedValue) {
|
||||
binding.textSettingDescription.text = item.choices[i]
|
||||
return
|
||||
binding.textSettingValue.text = item.choices[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.textSettingDescription.visibility = View.GONE
|
||||
}
|
||||
|
||||
setStyle(setting.isEditable, binding)
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.View
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
|
||||
@ -22,6 +23,14 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
|
||||
} else {
|
||||
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) {
|
||||
|
@ -22,6 +22,7 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
|
||||
} else {
|
||||
binding.textSettingDescription.visibility = View.GONE
|
||||
}
|
||||
binding.textSettingValue.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
|
@ -25,12 +25,12 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
|
||||
binding.textSettingDescription.text = ""
|
||||
binding.textSettingDescription.visibility = View.GONE
|
||||
}
|
||||
binding.switchWidget.isChecked = setting.isChecked
|
||||
binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
|
||||
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) {
|
||||
|
@ -19,6 +19,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
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.features.settings.model.Settings
|
||||
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.StepState
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.GameHelper
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils
|
||||
|
||||
class SetupFragment : Fragment() {
|
||||
private var _binding: FragmentSetupBinding? = null
|
||||
@ -112,14 +116,22 @@ class SetupFragment : Fragment() {
|
||||
0,
|
||||
false,
|
||||
R.string.give_permission,
|
||||
{ permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) },
|
||||
{
|
||||
notificationCallback = it
|
||||
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
},
|
||||
true,
|
||||
R.string.notification_warning,
|
||||
R.string.notification_warning_description,
|
||||
0,
|
||||
{
|
||||
NotificationManagerCompat.from(requireContext())
|
||||
if (NotificationManagerCompat.from(requireContext())
|
||||
.areNotificationsEnabled()
|
||||
) {
|
||||
StepState.COMPLETE
|
||||
} else {
|
||||
StepState.INCOMPLETE
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
@ -133,12 +145,22 @@ class SetupFragment : Fragment() {
|
||||
R.drawable.ic_add,
|
||||
true,
|
||||
R.string.select_keys,
|
||||
{ mainActivity.getProdKey.launch(arrayOf("*/*")) },
|
||||
{
|
||||
keyCallback = it
|
||||
getProdKey.launch(arrayOf("*/*"))
|
||||
},
|
||||
true,
|
||||
R.string.install_prod_keys_warning,
|
||||
R.string.install_prod_keys_warning_description,
|
||||
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(
|
||||
@ -150,9 +172,8 @@ class SetupFragment : Fragment() {
|
||||
true,
|
||||
R.string.add_games,
|
||||
{
|
||||
mainActivity.getGamesDirectory.launch(
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
|
||||
)
|
||||
gamesDirCallback = it
|
||||
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
|
||||
},
|
||||
true,
|
||||
R.string.add_games_warning,
|
||||
@ -163,7 +184,11 @@ class SetupFragment : Fragment() {
|
||||
PreferenceManager.getDefaultSharedPreferences(
|
||||
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 {
|
||||
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
|
||||
offscreenPageLimit = 2
|
||||
@ -194,15 +226,15 @@ class SetupFragment : Fragment() {
|
||||
super.onPageSelected(position)
|
||||
|
||||
if (position == 1 && previousPosition == 0) {
|
||||
showView(binding.buttonNext)
|
||||
showView(binding.buttonBack)
|
||||
ViewUtils.showView(binding.buttonNext)
|
||||
ViewUtils.showView(binding.buttonBack)
|
||||
} else if (position == 0 && previousPosition == 1) {
|
||||
hideView(binding.buttonBack)
|
||||
hideView(binding.buttonNext)
|
||||
ViewUtils.hideView(binding.buttonBack)
|
||||
ViewUtils.hideView(binding.buttonNext)
|
||||
} 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) {
|
||||
showView(binding.buttonNext)
|
||||
ViewUtils.showView(binding.buttonNext)
|
||||
}
|
||||
|
||||
previousPosition = position
|
||||
@ -215,7 +247,8 @@ class SetupFragment : Fragment() {
|
||||
|
||||
// Checks if the user has completed the task on the current page
|
||||
if (currentPage.hasWarning) {
|
||||
if (currentPage.taskCompleted.invoke()) {
|
||||
val stepState = currentPage.stepCompleted.invoke()
|
||||
if (stepState != StepState.INCOMPLETE) {
|
||||
pageForward()
|
||||
return@setOnClickListener
|
||||
}
|
||||
@ -264,9 +297,15 @@ class SetupFragment : Fragment() {
|
||||
_binding = null
|
||||
}
|
||||
|
||||
private lateinit var notificationCallback: SetupCallback
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
private val permissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||
if (it) {
|
||||
notificationCallback.onStepCompleted()
|
||||
}
|
||||
|
||||
if (!it &&
|
||||
!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() {
|
||||
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
|
||||
.putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
|
||||
@ -284,33 +344,6 @@ class SetupFragment : Fragment() {
|
||||
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() {
|
||||
binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1
|
||||
}
|
||||
@ -326,15 +359,29 @@ class SetupFragment : Fragment() {
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { view: View, windowInsets: WindowInsetsCompat ->
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
view.setPadding(
|
||||
barInsets.left + cutoutInsets.left,
|
||||
barInsets.top + cutoutInsets.top,
|
||||
barInsets.right + cutoutInsets.right,
|
||||
barInsets.bottom + cutoutInsets.bottom
|
||||
|
||||
val leftPadding = barInsets.left + cutoutInsets.left
|
||||
val topPadding = barInsets.top + cutoutInsets.top
|
||||
val rightPadding = barInsets.right + cutoutInsets.right
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,9 @@ class HomeViewModel : ViewModel() {
|
||||
private val _statusBarShadeVisible = MutableLiveData(true)
|
||||
val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible
|
||||
|
||||
private val _shouldPageForward = MutableLiveData(false)
|
||||
val shouldPageForward: LiveData<Boolean> get() = _shouldPageForward
|
||||
|
||||
var navigatedToSetup = false
|
||||
|
||||
init {
|
||||
@ -33,4 +36,8 @@ class HomeViewModel : ViewModel() {
|
||||
}
|
||||
_statusBarShadeVisible.value = visible
|
||||
}
|
||||
|
||||
fun setShouldPageForward(pageForward: Boolean) {
|
||||
_shouldPageForward.value = pageForward
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,20 @@ data class SetupPage(
|
||||
val buttonIconId: Int,
|
||||
val leftAlignedIcon: Boolean,
|
||||
val buttonTextId: Int,
|
||||
val buttonAction: () -> Unit,
|
||||
val buttonAction: (callback: SetupCallback) -> Unit,
|
||||
val hasWarning: Boolean,
|
||||
val warningTitleId: Int = 0,
|
||||
val warningDescriptionId: 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
|
||||
}
|
||||
|
@ -266,10 +266,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
|
||||
val getGamesDirectory =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||
if (result == null) {
|
||||
return@registerForActivityResult
|
||||
if (result != null) {
|
||||
processGamesDir(result)
|
||||
}
|
||||
}
|
||||
|
||||
fun processGamesDir(result: Uri) {
|
||||
contentResolver.takePersistableUriPermission(
|
||||
result,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
@ -292,16 +294,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
|
||||
val getProdKey =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
if (result == null) {
|
||||
return@registerForActivityResult
|
||||
if (result != null) {
|
||||
processKey(result)
|
||||
}
|
||||
}
|
||||
|
||||
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@registerForActivityResult
|
||||
return false
|
||||
}
|
||||
|
||||
contentResolver.takePersistableUriPermission(
|
||||
@ -324,14 +328,17 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
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 =
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/setup_root"
|
||||
@ -8,19 +8,24 @@
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/viewPager2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/constraint_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_margin="8dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:id="@+id/button_next"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/next"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
@ -31,10 +36,11 @@
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/back"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
@ -21,45 +21,76 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
android:layout_weight="1">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.DisplaySmall"
|
||||
android:id="@+id/text_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
style="@style/TextAppearance.Material3.DisplaySmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
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" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.TitleLarge"
|
||||
android:id="@+id/text_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:paddingHorizontal="32dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="26sp"
|
||||
app:lineHeight="40sp"
|
||||
style="@style/TextAppearance.Material3.TitleLarge"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center"
|
||||
android:textSize="20sp"
|
||||
android:paddingHorizontal="16dp"
|
||||
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" />
|
||||
|
||||
<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
|
||||
android:id="@+id/button_action"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:textSize="20sp"
|
||||
app:iconSize="24sp"
|
||||
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" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -5,23 +5,16 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/text_value"
|
||||
style="@style/TextAppearance.Material3.LabelMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginBottom="@dimen/spacing_medlarge"
|
||||
android:layout_marginTop="@dimen/spacing_medlarge"
|
||||
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="%" />
|
||||
tools:text="75%" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/setup_root"
|
||||
@ -8,35 +8,39 @@
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/viewPager2"
|
||||
android:layout_width="0dp"
|
||||
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" />
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@+id/constraint_buttons"
|
||||
android:layout_alignParentTop="true"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/constraint_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_alignParentBottom="true">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:id="@+id/button_next"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp"
|
||||
android:text="@string/next"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:id="@+id/button_back"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp"
|
||||
android:text="@string/back"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
@ -1,9 +1,10 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
@ -11,31 +12,40 @@
|
||||
android:minHeight="72dp"
|
||||
android:padding="@dimen/spacing_large">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.HeadlineMedium"
|
||||
android:id="@+id/text_setting_name"
|
||||
android:layout_width="0dp"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/text_setting_name"
|
||||
style="@style/TextAppearance.Material3.HeadlineMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:textSize="16sp"
|
||||
android:textAlignment="viewStart"
|
||||
app:lineHeight="28dp"
|
||||
android:textSize="16sp"
|
||||
app:lineHeight="22dp"
|
||||
tools:text="Setting Name" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/text_setting_description"
|
||||
android:layout_width="wrap_content"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignStart="@+id/text_setting_name"
|
||||
android:layout_below="@+id/text_setting_name"
|
||||
android:layout_marginTop="@dimen/spacing_small"
|
||||
android:visibility="visible"
|
||||
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>
|
||||
|
@ -21,11 +21,12 @@
|
||||
app:layout_constraintVertical_chainStyle="spread"
|
||||
app:layout_constraintWidth_max="220dp"
|
||||
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
|
||||
android:id="@+id/text_title"
|
||||
style="@style/TextAppearance.Material3.DisplayMedium"
|
||||
style="@style/TextAppearance.Material3.DisplaySmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:textAlignment="center"
|
||||
@ -44,23 +45,42 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="26sp"
|
||||
android:textSize="20sp"
|
||||
android:paddingHorizontal="16dp"
|
||||
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="40sp"
|
||||
app:lineHeight="30sp"
|
||||
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
|
||||
android:id="@+id/button_action"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="56dp"
|
||||
android:textSize="20sp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:textSize="20sp"
|
||||
app:iconGravity="end"
|
||||
app:iconSize="24sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
@ -29,6 +29,7 @@
|
||||
<string name="back">Back</string>
|
||||
<string name="add_games">Add Games</string>
|
||||
<string name="add_games_description">Select your games folder</string>
|
||||
<string name="step_complete">Complete!</string>
|
||||
|
||||
<!-- Home strings -->
|
||||
<string name="home_games">Games</string>
|
||||
@ -149,6 +150,7 @@
|
||||
<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="cpu_accuracy">CPU accuracy</string>
|
||||
<string name="value_with_units">%1$s%2$s</string>
|
||||
|
||||
<!-- System settings strings -->
|
||||
<string name="use_docked_mode">Docked Mode</string>
|
||||
|
@ -1,7 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/settings_common.h"
|
||||
|
||||
namespace Settings {
|
||||
|
@ -12,8 +12,8 @@ namespace Settings {
|
||||
|
||||
template <typename T>
|
||||
struct EnumMetadata {
|
||||
static constexpr std::vector<std::pair<std::string, T>> Canonicalizations();
|
||||
static constexpr u32 Index();
|
||||
static std::vector<std::pair<std::string, T>> Canonicalizations();
|
||||
static u32 Index();
|
||||
};
|
||||
|
||||
#define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__))
|
||||
@ -66,11 +66,11 @@ struct EnumMetadata {
|
||||
#define ENUM(NAME, ...) \
|
||||
enum class NAME : u32 { __VA_ARGS__ }; \
|
||||
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__)}; \
|
||||
} \
|
||||
template <> \
|
||||
constexpr u32 EnumMetadata<NAME>::Index() { \
|
||||
inline u32 EnumMetadata<NAME>::Index() { \
|
||||
return __COUNTER__; \
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ enum class AudioEngine : u32 {
|
||||
};
|
||||
|
||||
template <>
|
||||
constexpr std::vector<std::pair<std::string, AudioEngine>>
|
||||
inline std::vector<std::pair<std::string, AudioEngine>>
|
||||
EnumMetadata<AudioEngine>::Canonicalizations() {
|
||||
return {
|
||||
{"auto", AudioEngine::Auto},
|
||||
@ -96,7 +96,7 @@ EnumMetadata<AudioEngine>::Canonicalizations() {
|
||||
}
|
||||
|
||||
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
|
||||
// here
|
||||
return 100;
|
||||
@ -147,7 +147,7 @@ ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
|
||||
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
|
||||
|
||||
template <typename Type>
|
||||
constexpr std::string CanonicalizeEnum(Type id) {
|
||||
inline std::string CanonicalizeEnum(Type id) {
|
||||
const auto group = EnumMetadata<Type>::Canonicalizations();
|
||||
for (auto& [name, value] : group) {
|
||||
if (value == id) {
|
||||
@ -158,7 +158,7 @@ constexpr std::string CanonicalizeEnum(Type id) {
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
constexpr Type ToEnum(const std::string& canonicalization) {
|
||||
inline Type ToEnum(const std::string& canonicalization) {
|
||||
const auto group = EnumMetadata<Type>::Canonicalizations();
|
||||
for (auto& [name, value] : group) {
|
||||
if (name == canonicalization) {
|
||||
|
@ -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>) {
|
||||
return CanonicalizeEnum(this->GetValue());
|
||||
} else {
|
||||
@ -256,11 +256,11 @@ public:
|
||||
* @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
|
||||
*/
|
||||
template <typename T = BasicSetting>
|
||||
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
|
||||
Category category_, u32 specialization_ = Specialization::Default,
|
||||
bool save_ = true, bool runtime_modifiable_ = false,
|
||||
BasicSetting* other_setting_ = nullptr)
|
||||
requires(!ranged)
|
||||
typename std::enable_if<!ranged, T*>::type other_setting_ = nullptr)
|
||||
: Setting<Type, false>{
|
||||
linkage, default_val, name, category_, specialization_,
|
||||
save_, runtime_modifiable_, other_setting_} {
|
||||
@ -282,12 +282,12 @@ public:
|
||||
* @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
|
||||
*/
|
||||
template <typename T = BasicSetting>
|
||||
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
|
||||
const Type& max_val, const std::string& name, Category category_,
|
||||
u32 specialization_ = Specialization::Default, bool save_ = true,
|
||||
bool runtime_modifiable_ = false,
|
||||
BasicSetting* other_setting_ = nullptr)
|
||||
requires(ranged)
|
||||
typename std::enable_if<ranged, T*>::type other_setting_ = nullptr)
|
||||
: Setting<Type, true>{linkage, default_val, min_val,
|
||||
max_val, name, category_,
|
||||
specialization_, save_, runtime_modifiable_,
|
||||
|
@ -263,6 +263,23 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
|
||||
|
||||
std::vector<u8> mem(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));
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_ERR);
|
||||
|
@ -35,7 +35,7 @@ void RomFSFactory::SetPackedUpdate(VirtualFile 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (nca == nullptr) {
|
||||
// TODO: Find the right error code to use here
|
||||
return ResultUnknown;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
ResultVal<VirtualFile> RomFSFactory::OpenPatchedRomFSWithProgramIndex(
|
||||
u64 title_id, u8 program_index, ContentRecordType type) const {
|
||||
VirtualFile RomFSFactory::OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
|
||||
ContentRecordType type) const {
|
||||
const auto res_title_id = GetBaseTitleIDWithProgramIndex(title_id, program_index);
|
||||
|
||||
return OpenPatchedRomFS(res_title_id, type);
|
||||
}
|
||||
|
||||
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
|
||||
ContentRecordType type) const {
|
||||
VirtualFile RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) const {
|
||||
const std::shared_ptr<NCA> res = GetEntry(title_id, storage, type);
|
||||
if (res == nullptr) {
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
return ResultUnknown;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto romfs = res->GetRomFS();
|
||||
if (romfs == nullptr) {
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
return romfs;
|
||||
return res->GetRomFS();
|
||||
}
|
||||
|
||||
std::shared_ptr<NCA> RomFSFactory::GetEntry(u64 title_id, StorageId storage,
|
||||
|
@ -41,13 +41,11 @@ public:
|
||||
~RomFSFactory();
|
||||
|
||||
void SetPackedUpdate(VirtualFile update_raw_file);
|
||||
[[nodiscard]] ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const;
|
||||
[[nodiscard]] ResultVal<VirtualFile> OpenPatchedRomFS(u64 title_id,
|
||||
ContentRecordType type) const;
|
||||
[[nodiscard]] ResultVal<VirtualFile> OpenPatchedRomFSWithProgramIndex(
|
||||
u64 title_id, u8 program_index, ContentRecordType type) const;
|
||||
[[nodiscard]] ResultVal<VirtualFile> Open(u64 title_id, StorageId storage,
|
||||
[[nodiscard]] VirtualFile OpenCurrentProcess(u64 current_process_title_id) const;
|
||||
[[nodiscard]] VirtualFile OpenPatchedRomFS(u64 title_id, ContentRecordType type) const;
|
||||
[[nodiscard]] VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
|
||||
ContentRecordType type) const;
|
||||
[[nodiscard]] VirtualFile Open(u64 title_id, StorageId storage, ContentRecordType type) const;
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::shared_ptr<NCA> GetEntry(u64 title_id, StorageId storage,
|
||||
|
@ -108,26 +108,16 @@ SaveDataFactory::SaveDataFactory(Core::System& system_, VirtualDir save_director
|
||||
|
||||
SaveDataFactory::~SaveDataFactory() = default;
|
||||
|
||||
ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
|
||||
const SaveDataAttribute& meta) const {
|
||||
VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
|
||||
PrintSaveDataAttributeWarnings(meta);
|
||||
|
||||
const auto save_directory =
|
||||
GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
||||
|
||||
auto out = 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;
|
||||
return dir->CreateDirectoryRelative(save_directory);
|
||||
}
|
||||
|
||||
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
|
||||
const SaveDataAttribute& meta) const {
|
||||
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
|
||||
|
||||
const auto save_directory =
|
||||
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 an error if the save data doesn't actually exist.
|
||||
if (out == nullptr) {
|
||||
// TODO(Subv): Find out correct error code.
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -89,8 +89,8 @@ public:
|
||||
explicit SaveDataFactory(Core::System& system_, VirtualDir save_directory_);
|
||||
~SaveDataFactory();
|
||||
|
||||
ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
|
||||
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
|
||||
VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
|
||||
VirtualDir Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
|
||||
|
||||
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
|
||||
|
||||
|
@ -23,7 +23,7 @@ SDMCFactory::SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_)
|
||||
|
||||
SDMCFactory::~SDMCFactory() = default;
|
||||
|
||||
ResultVal<VirtualDir> SDMCFactory::Open() const {
|
||||
VirtualDir SDMCFactory::Open() const {
|
||||
return sd_dir;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ public:
|
||||
explicit SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_);
|
||||
~SDMCFactory();
|
||||
|
||||
ResultVal<VirtualDir> Open() const;
|
||||
VirtualDir Open() const;
|
||||
|
||||
VirtualDir GetSDMCModificationLoadRoot(u64 title_id) const;
|
||||
VirtualDir GetSDMCContentDirectory() const;
|
||||
|
@ -289,6 +289,19 @@ enum class GyroscopeZeroDriftMode : u32 {
|
||||
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
|
||||
struct NpadStyleTag {
|
||||
union {
|
||||
@ -334,6 +347,14 @@ struct TouchState {
|
||||
};
|
||||
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 {
|
||||
u8 r{};
|
||||
u8 g{};
|
||||
@ -662,6 +683,11 @@ struct MouseState {
|
||||
};
|
||||
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.
|
||||
constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
|
||||
switch (npad_id_type) {
|
||||
|
@ -768,7 +768,7 @@ Result KPageTable::UnmapProcessMemory(KProcessAddress dst_addr, size_t size,
|
||||
m_memory_block_slab_manager, num_allocator_blocks);
|
||||
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.
|
||||
m_memory_block_manager.Update(std::addressof(allocator), dst_addr, num_pages,
|
||||
|
@ -283,159 +283,6 @@ private:
|
||||
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_FAILED(res) (static_cast<Result>(res).IsFailure())
|
||||
|
||||
|
@ -765,15 +765,16 @@ Result Module::Interface::InitializeApplicationInfoBase() {
|
||||
// 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
|
||||
// our own process
|
||||
const auto launch_property =
|
||||
system.GetARPManager().GetLaunchProperty(system.GetApplicationProcessProgramID());
|
||||
Glue::ApplicationLaunchProperty launch_property{};
|
||||
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");
|
||||
return Account::ResultInvalidApplication;
|
||||
}
|
||||
|
||||
switch (launch_property->base_game_storage_id) {
|
||||
switch (launch_property.base_game_storage_id) {
|
||||
case FileSys::StorageId::GameCard:
|
||||
application_info.application_type = ApplicationType::GameCard;
|
||||
break;
|
||||
@ -785,7 +786,7 @@ Result Module::Interface::InitializeApplicationInfoBase() {
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1578,11 +1578,13 @@ void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
|
||||
attribute.title_id = system.GetApplicationProcessProgramID();
|
||||
attribute.user_id = user_id;
|
||||
attribute.type = FileSys::SaveDataType::SaveData;
|
||||
|
||||
FileSys::VirtualDir save_data{};
|
||||
const auto res = system.GetFileSystemController().CreateSaveData(
|
||||
FileSys::SaveDataSpaceId::NandUser, attribute);
|
||||
&save_data, FileSys::SaveDataSpaceId::NandUser, attribute);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(res.Code());
|
||||
rb.Push(res);
|
||||
rb.Push<u64>(0);
|
||||
}
|
||||
|
||||
@ -1667,26 +1669,30 @@ void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) {
|
||||
auto app_man = ns_am2->GetApplicationManagerInterface();
|
||||
|
||||
// Get desired application language
|
||||
const auto res_lang = app_man->GetApplicationDesiredLanguage(supported_languages);
|
||||
if (res_lang.Failed()) {
|
||||
u8 desired_language{};
|
||||
const auto res_lang =
|
||||
app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages);
|
||||
if (res_lang != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res_lang.Code());
|
||||
rb.Push(res_lang);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert to settings language code.
|
||||
const auto res_code = app_man->ConvertApplicationLanguageToLanguageCode(*res_lang);
|
||||
if (res_code.Failed()) {
|
||||
u64 language_code{};
|
||||
const auto res_code =
|
||||
app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language);
|
||||
if (res_code != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res_code.Code());
|
||||
rb.Push(res_code);
|
||||
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};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(*res_code);
|
||||
rb.Push(language_code);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) {
|
||||
|
@ -57,8 +57,8 @@ Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
const auto entry_type = GetEntryType(path);
|
||||
if (entry_type.Code() == ResultSuccess) {
|
||||
FileSys::EntryType entry_type{};
|
||||
if (GetEntryType(&entry_type, path) == ResultSuccess) {
|
||||
return FileSys::ERROR_PATH_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
@ -210,8 +210,8 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
|
||||
FileSys::Mode mode) const {
|
||||
Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
|
||||
const std::string& path_, FileSys::Mode mode) const {
|
||||
const std::string path(Common::FS::SanitizePath(path_));
|
||||
std::string_view npath = path;
|
||||
while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
|
||||
@ -224,50 +224,68 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
|
||||
}
|
||||
|
||||
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_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, path);
|
||||
if (dir == nullptr) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
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 {
|
||||
std::string path(Common::FS::SanitizePath(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) {
|
||||
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 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_} {}
|
||||
@ -310,57 +328,54 @@ void FileSystemController::SetPackedUpdate(FileSys::VirtualFile 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");
|
||||
|
||||
if (romfs_factory == nullptr) {
|
||||
// TODO(bunnei): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return romfs_factory->OpenCurrentProcess(system.GetApplicationProcessProgramID());
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> FileSystemController::OpenPatchedRomFS(
|
||||
u64 title_id, FileSys::ContentRecordType type) const {
|
||||
FileSys::VirtualFile FileSystemController::OpenPatchedRomFS(u64 title_id,
|
||||
FileSys::ContentRecordType type) const {
|
||||
LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}", title_id);
|
||||
|
||||
if (romfs_factory == nullptr) {
|
||||
// TODO: Find a better error code for this
|
||||
return ResultUnknown;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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 {
|
||||
LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}, program_index={}", title_id,
|
||||
program_index);
|
||||
|
||||
if (romfs_factory == nullptr) {
|
||||
// TODO: Find a better error code for this
|
||||
return ResultUnknown;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return romfs_factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
|
||||
u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const {
|
||||
FileSys::VirtualFile FileSystemController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
|
||||
FileSys::ContentRecordType type) const {
|
||||
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
|
||||
title_id, storage_id, type);
|
||||
|
||||
if (romfs_factory == nullptr) {
|
||||
// TODO(bunnei): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return romfs_factory->Open(title_id, storage_id, type);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
|
||||
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const {
|
||||
Result FileSystemController::CreateSaveData(FileSys::VirtualDir* out_save_data,
|
||||
FileSys::SaveDataSpaceId space,
|
||||
const FileSys::SaveDataAttribute& save_struct) const {
|
||||
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
|
||||
save_struct.DebugInfo());
|
||||
|
||||
@ -368,11 +383,18 @@ ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
|
||||
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(
|
||||
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& attribute) const {
|
||||
Result FileSystemController::OpenSaveData(FileSys::VirtualDir* out_save_data,
|
||||
FileSys::SaveDataSpaceId space,
|
||||
const FileSys::SaveDataAttribute& attribute) const {
|
||||
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", space,
|
||||
attribute.DebugInfo());
|
||||
|
||||
@ -380,10 +402,16 @@ ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
|
||||
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 {
|
||||
LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", space);
|
||||
|
||||
@ -391,20 +419,32 @@ ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(
|
||||
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");
|
||||
|
||||
if (sdmc_factory == nullptr) {
|
||||
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 {
|
||||
LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", id);
|
||||
|
||||
@ -417,11 +457,12 @@ ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition(
|
||||
return FileSys::ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return part;
|
||||
*out_bis_partition = part;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
|
||||
FileSys::BisPartitionId id) const {
|
||||
Result FileSystemController::OpenBISPartitionStorage(
|
||||
FileSys::VirtualFile* out_bis_partition_storage, FileSys::BisPartitionId id) const {
|
||||
LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", id);
|
||||
|
||||
if (bis_factory == nullptr) {
|
||||
@ -433,7 +474,8 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
|
||||
return FileSys::ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return part;
|
||||
*out_bis_partition_storage = part;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const {
|
||||
|
@ -64,21 +64,24 @@ public:
|
||||
Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
|
||||
|
||||
void SetPackedUpdate(FileSys::VirtualFile update_raw);
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const;
|
||||
ResultVal<FileSys::VirtualFile> OpenPatchedRomFS(u64 title_id,
|
||||
FileSys::VirtualFile OpenRomFSCurrentProcess() const;
|
||||
FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type) const;
|
||||
FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
|
||||
FileSys::ContentRecordType type) const;
|
||||
ResultVal<FileSys::VirtualFile> OpenPatchedRomFSWithProgramIndex(
|
||||
u64 title_id, u8 program_index, FileSys::ContentRecordType type) const;
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
|
||||
FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
|
||||
FileSys::ContentRecordType type) const;
|
||||
ResultVal<FileSys::VirtualDir> CreateSaveData(
|
||||
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const;
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveData(
|
||||
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const;
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const;
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC() const;
|
||||
ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const;
|
||||
ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id) const;
|
||||
|
||||
Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
|
||||
const FileSys::SaveDataAttribute& save_struct) const;
|
||||
Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
|
||||
const FileSys::SaveDataAttribute& save_struct) const;
|
||||
Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
|
||||
FileSys::SaveDataSpaceId space) const;
|
||||
Result OpenSDMC(FileSys::VirtualDir* out_sdmc) 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 GetTotalSpaceSize(FileSys::StorageId id) const;
|
||||
@ -224,26 +227,28 @@ public:
|
||||
* @param mode Mode to open the file with
|
||||
* @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
|
||||
* @param path Path relative to the archive
|
||||
* @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
|
||||
* @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
|
||||
* @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:
|
||||
FileSys::VirtualDir backing;
|
||||
|
@ -419,14 +419,15 @@ public:
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode);
|
||||
|
||||
auto result = backend.OpenFile(name, mode);
|
||||
if (result.Failed()) {
|
||||
FileSys::VirtualFile vfs_file{};
|
||||
auto result = backend.OpenFile(&vfs_file, name, mode);
|
||||
if (result != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
rb.Push(result);
|
||||
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};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -444,14 +445,15 @@ public:
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags);
|
||||
|
||||
auto result = backend.OpenDirectory(name);
|
||||
if (result.Failed()) {
|
||||
FileSys::VirtualDir vfs_dir{};
|
||||
auto result = backend.OpenDirectory(&vfs_dir, name);
|
||||
if (result != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
rb.Push(result);
|
||||
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};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -464,16 +466,17 @@ public:
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. file={}", name);
|
||||
|
||||
auto result = backend.GetEntryType(name);
|
||||
if (result.Failed()) {
|
||||
FileSys::EntryType vfs_entry_type{};
|
||||
auto result = backend.GetEntryType(&vfs_entry_type, name);
|
||||
if (result != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(*result));
|
||||
rb.Push<u32>(static_cast<u32>(vfs_entry_type));
|
||||
}
|
||||
|
||||
void Commit(HLERequestContext& ctx) {
|
||||
@ -505,16 +508,17 @@ public:
|
||||
|
||||
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
|
||||
|
||||
auto result = backend.GetFileTimeStampRaw(name);
|
||||
if (result.Failed()) {
|
||||
FileSys::FileTimeStampRaw vfs_timestamp{};
|
||||
auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name);
|
||||
if (result != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(*result);
|
||||
rb.PushRaw(vfs_timestamp);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -572,14 +576,15 @@ private:
|
||||
}
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& type : (*save_root)->GetSubdirectories()) {
|
||||
for (const auto& type : save_root->GetSubdirectories()) {
|
||||
if (type->GetName() == "save") {
|
||||
for (const auto& save_id : type->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) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
auto filesystem =
|
||||
std::make_shared<IFileSystem>(system, fsc.OpenSDMC().Unwrap(),
|
||||
SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
|
||||
FileSys::VirtualDir sdmc_dir{};
|
||||
fsc.OpenSDMC(&sdmc_dir);
|
||||
|
||||
auto filesystem = std::make_shared<IFileSystem>(
|
||||
system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
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(),
|
||||
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};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -874,8 +882,9 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
|
||||
|
||||
LOG_INFO(Service_FS, "called.");
|
||||
|
||||
auto dir = fsc.OpenSaveData(parameters.space_id, parameters.attribute);
|
||||
if (dir.Failed()) {
|
||||
FileSys::VirtualDir dir{};
|
||||
auto result = fsc.OpenSaveData(&dir, parameters.space_id, parameters.attribute);
|
||||
if (result != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
|
||||
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
|
||||
return;
|
||||
@ -899,8 +908,8 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
auto filesystem = std::make_shared<IFileSystem>(system, std::move(dir.Unwrap()),
|
||||
SizeGetter::FromStorageId(fsc, id));
|
||||
auto filesystem =
|
||||
std::make_shared<IFileSystem>(system, std::move(dir), SizeGetter::FromStorageId(fsc, id));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -970,7 +979,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) {
|
||||
|
||||
if (!romfs) {
|
||||
auto current_romfs = fsc.OpenRomFSCurrentProcess();
|
||||
if (current_romfs.Failed()) {
|
||||
if (!current_romfs) {
|
||||
// TODO (bunnei): Find the right error code to use here
|
||||
LOG_CRITICAL(Service_FS, "no file system interface available!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@ -978,7 +987,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
romfs = current_romfs.Unwrap();
|
||||
romfs = current_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);
|
||||
|
||||
if (data.Failed()) {
|
||||
if (!data) {
|
||||
const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
|
||||
|
||||
if (archive != nullptr) {
|
||||
@ -1021,7 +1030,7 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
|
||||
const FileSys::PatchManager pm{title_id, fsc, content_provider};
|
||||
|
||||
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};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -1051,7 +1060,7 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
|
||||
fsc.OpenPatchedRomFSWithProgramIndex(system.GetApplicationProcessProgramID(), program_index,
|
||||
FileSys::ContentRecordType::Program);
|
||||
|
||||
if (patched_romfs.Failed()) {
|
||||
if (!patched_romfs) {
|
||||
// TODO: Find the right error code to use here
|
||||
LOG_ERROR(Service_FS, "could not open storage with program_index={}", program_index);
|
||||
|
||||
@ -1060,7 +1069,7 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
|
||||
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};
|
||||
rb.Push(ResultSuccess);
|
||||
|
@ -65,18 +65,19 @@ void ARP_R::GetApplicationLaunchProperty(HLERequestContext& ctx) {
|
||||
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!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
rb.Push(res);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(*res);
|
||||
rb.PushRaw(launch_property);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
rb.Push(res);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(*res);
|
||||
rb.PushRaw(launch_property);
|
||||
}
|
||||
|
||||
void ARP_R::GetApplicationControlProperty(HLERequestContext& ctx) {
|
||||
@ -113,16 +115,17 @@ void ARP_R::GetApplicationControlProperty(HLERequestContext& ctx) {
|
||||
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!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
rb.Push(res);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(*res);
|
||||
ctx.WriteBuffer(nacp_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -134,16 +137,17 @@ void ARP_R::GetApplicationControlPropertyWithApplicationId(HLERequestContext& ct
|
||||
|
||||
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!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
rb.Push(res);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(*res);
|
||||
ctx.WriteBuffer(nacp_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
|
@ -15,7 +15,8 @@ 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) {
|
||||
return Glue::ResultInvalidProcessId;
|
||||
}
|
||||
@ -25,10 +26,11 @@ ResultVal<ApplicationLaunchProperty> ARPManager::GetLaunchProperty(u64 title_id)
|
||||
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) {
|
||||
return Glue::ResultInvalidProcessId;
|
||||
}
|
||||
@ -38,7 +40,8 @@ ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const {
|
||||
return Glue::ResultProcessIdNotRegistered;
|
||||
}
|
||||
|
||||
return iter->second.control;
|
||||
*out_control_property = iter->second.control;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
|
||||
|
@ -32,12 +32,12 @@ public:
|
||||
// Returns the ApplicationLaunchProperty corresponding to the provided title ID if it was
|
||||
// previously registered, otherwise ResultProcessIdNotRegistered if it was never registered or
|
||||
// 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
|
||||
// the provided title ID if it was previously registered, otherwise ResultProcessIdNotRegistered
|
||||
// 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
|
||||
// ResultProcessIdNotRegistered if attempting to re-register a title ID without an intermediate
|
||||
|
@ -16,22 +16,6 @@ class EmulatedConsole;
|
||||
namespace Service::HID {
|
||||
class Controller_Touchscreen final : public ControllerBase {
|
||||
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_);
|
||||
~Controller_Touchscreen() override;
|
||||
|
||||
|
@ -2368,7 +2368,7 @@ void Hid::GetNpadCommunicationMode(HLERequestContext& ctx) {
|
||||
|
||||
void Hid::SetTouchScreenConfiguration(HLERequestContext& 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>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
|
||||
@ -2543,7 +2543,8 @@ public:
|
||||
|
||||
class HidSys final : public ServiceFramework<HidSys> {
|
||||
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
|
||||
static const FunctionInfo functions[] = {
|
||||
{31, nullptr, "SendKeyboardLockKeyEvent"},
|
||||
@ -2568,7 +2569,7 @@ public:
|
||||
{303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
|
||||
{304, nullptr, "EnableAssigningSingleOnSlSrPress"},
|
||||
{305, nullptr, "DisableAssigningSingleOnSlSrPress"},
|
||||
{306, nullptr, "GetLastActiveNpad"},
|
||||
{306, &HidSys::GetLastActiveNpad, "GetLastActiveNpad"},
|
||||
{307, nullptr, "GetNpadSystemExtStyle"},
|
||||
{308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
|
||||
{309, nullptr, "GetNpadFullKeyGripColor"},
|
||||
@ -2624,7 +2625,7 @@ public:
|
||||
{700, nullptr, "ActivateUniquePad"},
|
||||
{702, nullptr, "AcquireUniquePadConnectionEventHandle"},
|
||||
{703, nullptr, "GetUniquePadIds"},
|
||||
{751, nullptr, "AcquireJoyDetachOnBluetoothOffEventHandle"},
|
||||
{751, &HidSys::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
|
||||
{800, nullptr, "ListSixAxisSensorHandles"},
|
||||
{801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
|
||||
{802, nullptr, "ResetSixAxisSensorCalibrationValues"},
|
||||
@ -2650,7 +2651,7 @@ public:
|
||||
{830, nullptr, "SetNotificationLedPattern"},
|
||||
{831, nullptr, "SetNotificationLedPatternWithTimeout"},
|
||||
{832, nullptr, "PrepareHidsForNotificationWake"},
|
||||
{850, nullptr, "IsUsbFullKeyControllerEnabled"},
|
||||
{850, &HidSys::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
|
||||
{851, nullptr, "EnableUsbFullKeyController"},
|
||||
{852, nullptr, "IsUsbConnected"},
|
||||
{870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
|
||||
@ -2682,7 +2683,7 @@ public:
|
||||
{1150, nullptr, "SetTouchScreenMagnification"},
|
||||
{1151, nullptr, "GetTouchScreenFirmwareVersion"},
|
||||
{1152, nullptr, "SetTouchScreenDefaultConfiguration"},
|
||||
{1153, nullptr, "GetTouchScreenDefaultConfiguration"},
|
||||
{1153, &HidSys::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
|
||||
{1154, nullptr, "IsFirmwareAvailableForNotification"},
|
||||
{1155, nullptr, "SetForceHandheldStyleVibration"},
|
||||
{1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
|
||||
@ -2749,6 +2750,8 @@ public:
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
@ -2760,17 +2763,66 @@ private:
|
||||
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) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
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);
|
||||
|
||||
const std::vector<Core::HID::UniquePadId> unique_pads{};
|
||||
|
||||
ctx.WriteBuffer(unique_pads);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
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) {
|
||||
|
@ -357,7 +357,8 @@ public:
|
||||
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()};
|
||||
VAddr addr{};
|
||||
|
||||
@ -372,20 +373,21 @@ public:
|
||||
R_TRY(result);
|
||||
|
||||
if (ValidateRegionForMap(page_table, addr, size)) {
|
||||
return addr;
|
||||
*out_map_location = addr;
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_INSUFFICIENT_ADDRESS_SPACE;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size,
|
||||
VAddr bss_addr, std::size_t bss_size, std::size_t size) {
|
||||
Result MapNro(VAddr* out_map_location, Kernel::KProcess* process, VAddr nro_addr,
|
||||
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++) {
|
||||
auto& page_table{process->GetPageTable()};
|
||||
VAddr addr{};
|
||||
|
||||
CASCADE_RESULT(addr, MapProcessCodeMemory(process, nro_addr, nro_size));
|
||||
R_TRY(MapProcessCodeMemory(&addr, process, nro_addr, nro_size));
|
||||
|
||||
if (bss_size) {
|
||||
auto block_guard = detail::ScopeExit([&] {
|
||||
@ -411,7 +413,8 @@ public:
|
||||
}
|
||||
|
||||
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,
|
||||
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));
|
||||
CASCADE_CODE(process->GetPageTable().SetProcessMemoryPermission(
|
||||
R_TRY(process->GetPageTable().SetProcessMemoryPermission(
|
||||
ro_start, data_start - ro_start, Kernel::Svc::MemoryPermission::Read));
|
||||
|
||||
return process->GetPageTable().SetProcessMemoryPermission(
|
||||
@ -542,31 +545,32 @@ public:
|
||||
}
|
||||
|
||||
// Map memory for the NRO
|
||||
const auto map_result{MapNro(system.ApplicationProcess(), nro_address, nro_size,
|
||||
bss_address, bss_size, nro_size + bss_size)};
|
||||
if (map_result.Failed()) {
|
||||
VAddr map_location{};
|
||||
const auto map_result{MapNro(&map_location, system.ApplicationProcess(), nro_address,
|
||||
nro_size, bss_address, bss_size, nro_size + bss_size)};
|
||||
if (map_result != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(map_result.Code());
|
||||
rb.Push(map_result);
|
||||
}
|
||||
|
||||
// Load the NRO into the mapped memory
|
||||
if (const auto result{
|
||||
LoadNro(system.ApplicationProcess(), header, nro_address, *map_result)};
|
||||
LoadNro(system.ApplicationProcess(), header, nro_address, map_location)};
|
||||
result.IsError()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(map_result.Code());
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
// Track the loaded NRO
|
||||
nro.insert_or_assign(*map_result,
|
||||
NROInfo{hash, *map_result, nro_size, bss_address, bss_size,
|
||||
nro.insert_or_assign(map_location,
|
||||
NROInfo{hash, map_location, nro_size, bss_address, bss_size,
|
||||
header.segment_headers[TEXT_INDEX].memory_size,
|
||||
header.segment_headers[RO_INDEX].memory_size,
|
||||
header.segment_headers[DATA_INDEX].memory_size, nro_address});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(*map_result);
|
||||
rb.Push(map_location);
|
||||
}
|
||||
|
||||
Result UnmapNro(const NROInfo& info) {
|
||||
@ -574,19 +578,19 @@ public:
|
||||
auto& page_table{system.ApplicationProcess()->GetPageTable()};
|
||||
|
||||
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.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.src_addr + info.text_size + info.ro_size, info.data_size,
|
||||
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,
|
||||
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
|
||||
CASCADE_CODE(page_table.UnmapCodeMemory(
|
||||
R_TRY(page_table.UnmapCodeMemory(
|
||||
info.nro_address, info.src_addr, info.text_size,
|
||||
Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
|
||||
return ResultSuccess;
|
||||
|
@ -101,20 +101,14 @@ private:
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const auto result{manager.GetDefault(source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
if (result->size() > 0) {
|
||||
ctx.WriteBuffer(SerializeArray(*result));
|
||||
const auto default_miis{manager.GetDefault(source_flag)};
|
||||
if (default_miis.size() > 0) {
|
||||
ctx.WriteBuffer(SerializeArray(default_miis));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(result->size()));
|
||||
rb.Push<u32>(static_cast<u32>(default_miis.size()));
|
||||
}
|
||||
|
||||
void Get1(HLERequestContext& ctx) {
|
||||
@ -123,15 +117,10 @@ private:
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const auto result{manager.GetDefault(source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
const auto default_miis{manager.GetDefault(source_flag)};
|
||||
|
||||
std::vector<CharInfo> values;
|
||||
for (const auto& element : *result) {
|
||||
for (const auto& element : default_miis) {
|
||||
values.emplace_back(element.info);
|
||||
}
|
||||
|
||||
@ -139,7 +128,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(result->size()));
|
||||
rb.Push<u32>(static_cast<u32>(default_miis.size()));
|
||||
}
|
||||
|
||||
void UpdateLatest(HLERequestContext& ctx) {
|
||||
@ -149,16 +138,17 @@ private:
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const auto result{manager.UpdateLatest(info, source_flag)};
|
||||
if (result.Failed()) {
|
||||
CharInfo new_char_info{};
|
||||
const auto result{manager.UpdateLatest(&new_char_info, info, source_flag)};
|
||||
if (result != ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<CharInfo>(*result);
|
||||
rb.PushRaw<CharInfo>(new_char_info);
|
||||
}
|
||||
|
||||
void BuildRandom(HLERequestContext& ctx) {
|
||||
|
@ -409,8 +409,7 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const {
|
||||
return static_cast<u32>(count);
|
||||
}
|
||||
|
||||
ResultVal<CharInfo> MiiManager::UpdateLatest([[maybe_unused]] const CharInfo& info,
|
||||
SourceFlag source_flag) {
|
||||
Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag) {
|
||||
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
|
||||
return ERROR_CANNOT_FIND_ENTRY;
|
||||
}
|
||||
@ -672,7 +671,7 @@ bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const {
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
|
||||
std::vector<MiiInfoElement> MiiManager::GetDefault(SourceFlag source_flag) {
|
||||
std::vector<MiiInfoElement> result;
|
||||
|
||||
if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
|
||||
|
@ -19,12 +19,12 @@ public:
|
||||
bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
|
||||
bool IsFullDatabase() 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 BuildDefault(std::size_t index);
|
||||
CharInfo ConvertV3ToCharInfo(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);
|
||||
|
||||
// This is nn::mii::detail::Ver::StoreDataRaw::BuildFromStoreData
|
||||
|
@ -392,18 +392,19 @@ void IApplicationManagerInterface::GetApplicationDesiredLanguage(HLERequestConte
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto supported_languages = rp.Pop<u32>();
|
||||
|
||||
const auto res = GetApplicationDesiredLanguage(supported_languages);
|
||||
if (res.Succeeded()) {
|
||||
u8 desired_language{};
|
||||
const auto res = GetApplicationDesiredLanguage(&desired_language, supported_languages);
|
||||
if (res == ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(*res);
|
||||
rb.Push<u32>(desired_language);
|
||||
} else {
|
||||
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) {
|
||||
LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
|
||||
|
||||
@ -430,7 +431,8 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
|
||||
for (const auto lang : *priority_list) {
|
||||
const auto supported_flag = GetSupportedLanguageFlag(lang);
|
||||
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};
|
||||
const auto application_language = rp.Pop<u8>();
|
||||
|
||||
const auto res = ConvertApplicationLanguageToLanguageCode(application_language);
|
||||
if (res.Succeeded()) {
|
||||
u64 language_code{};
|
||||
const auto res = ConvertApplicationLanguageToLanguageCode(&language_code, application_language);
|
||||
if (res == ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(*res);
|
||||
rb.Push(language_code);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
rb.Push(res);
|
||||
}
|
||||
}
|
||||
|
||||
ResultVal<u64> IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
|
||||
u8 application_language) {
|
||||
Result IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
|
||||
u64* out_language_code, u8 application_language) {
|
||||
const auto language_code =
|
||||
ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language));
|
||||
if (language_code == std::nullopt) {
|
||||
@ -464,7 +467,8 @@ ResultVal<u64> IApplicationManagerInterface::ConvertApplicationLanguageToLanguag
|
||||
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_)
|
||||
@ -618,12 +622,13 @@ void IReadOnlyApplicationControlDataInterface::GetApplicationControlData(HLERequ
|
||||
static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size.");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
std::vector<u8> nacp_data{};
|
||||
const auto parameters{rp.PopRaw<RequestParameters>()};
|
||||
const auto nacp_data{system.GetARPManager().GetControlProperty(parameters.application_id)};
|
||||
const auto result = nacp_data ? ResultSuccess : ResultUnknown;
|
||||
const auto result =
|
||||
system.GetARPManager().GetControlProperty(&nacp_data, parameters.application_id);
|
||||
|
||||
if (nacp_data) {
|
||||
ctx.WriteBuffer(nacp_data->data(), nacp_data->size());
|
||||
if (result == ResultSuccess) {
|
||||
ctx.WriteBuffer(nacp_data.data(), nacp_data.size());
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
@ -28,8 +28,9 @@ public:
|
||||
explicit IApplicationManagerInterface(Core::System& system_);
|
||||
~IApplicationManagerInterface() override;
|
||||
|
||||
ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages);
|
||||
ResultVal<u64> ConvertApplicationLanguageToLanguageCode(u8 application_language);
|
||||
Result GetApplicationDesiredLanguage(u8* out_desired_language, u32 supported_languages);
|
||||
Result ConvertApplicationLanguageToLanguageCode(u64* out_language_code,
|
||||
u8 application_language);
|
||||
|
||||
private:
|
||||
void GetApplicationControlData(HLERequestContext& ctx);
|
||||
|
@ -449,6 +449,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
|
||||
case NativeWindowScalingMode::ScaleToWindow:
|
||||
case NativeWindowScalingMode::ScaleCrop:
|
||||
case NativeWindowScalingMode::NoScaleCrop:
|
||||
case NativeWindowScalingMode::PreserveAspectRatio:
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_Nvnflinger, "unknown scaling mode {}", scaling_mode);
|
||||
|
@ -183,7 +183,7 @@ std::optional<u32> Nvnflinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
|
||||
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();
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
@ -191,7 +191,7 @@ ResultVal<Kernel::KReadableEvent*> Nvnflinger::FindVsyncEvent(u64 display_id) {
|
||||
return VI::ResultNotFound;
|
||||
}
|
||||
|
||||
return display->GetVSyncEvent();
|
||||
return display->GetVSyncEvent(out_vsync_event);
|
||||
}
|
||||
|
||||
VI::Display* Nvnflinger::FindDisplay(u64 display_id) {
|
||||
|
@ -82,7 +82,7 @@ public:
|
||||
///
|
||||
/// 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.
|
||||
[[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
|
||||
/// finished.
|
||||
|
@ -41,6 +41,7 @@ enum class NativeWindowScalingMode : s32 {
|
||||
ScaleToWindow = 1,
|
||||
ScaleCrop = 2,
|
||||
NoScaleCrop = 3,
|
||||
PreserveAspectRatio = 4,
|
||||
};
|
||||
|
||||
/// Transform parameter for QueueBuffer
|
||||
|
@ -152,7 +152,7 @@ private:
|
||||
if (pin_code[0] == '\0') {
|
||||
return true;
|
||||
}
|
||||
if (!restriction_settings.is_free_communication_default_on) {
|
||||
if (!settings.is_free_communication_default_on) {
|
||||
return true;
|
||||
}
|
||||
// TODO(ogniK): Check for blacklisted/exempted applications. Return false can happen here
|
||||
@ -168,21 +168,21 @@ private:
|
||||
if (pin_code[0] == '\0') {
|
||||
return true;
|
||||
}
|
||||
if (!restriction_settings.is_stero_vision_restricted) {
|
||||
if (!settings.is_stero_vision_restricted) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetStereoVisionRestrictionImpl(bool is_restricted) {
|
||||
if (restriction_settings.disabled) {
|
||||
if (settings.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pin_code[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
restriction_settings.is_stero_vision_restricted = is_restricted;
|
||||
settings.is_stero_vision_restricted = is_restricted;
|
||||
}
|
||||
|
||||
void Initialize(HLERequestContext& ctx) {
|
||||
@ -430,7 +430,7 @@ private:
|
||||
}
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(restriction_settings.is_stero_vision_restricted);
|
||||
rb.Push(settings.is_stero_vision_restricted);
|
||||
}
|
||||
|
||||
void ResetConfirmedStereoVisionPermission(HLERequestContext& ctx) {
|
||||
@ -460,22 +460,28 @@ private:
|
||||
bool stereo_vision{};
|
||||
};
|
||||
|
||||
// This is nn::pctl::RestrictionSettings
|
||||
struct RestrictionSettings {
|
||||
struct ParentalControlSettings {
|
||||
bool is_stero_vision_restricted{};
|
||||
bool is_free_communication_default_on{};
|
||||
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.");
|
||||
|
||||
// This is nn::pctl::PlayTimerSettings
|
||||
struct PlayTimerSettings {
|
||||
// TODO: RE the actual contents of this struct
|
||||
std::array<u8, 0x34> settings;
|
||||
std::array<u32, 13> settings;
|
||||
};
|
||||
static_assert(sizeof(PlayTimerSettings) == 0x34, "PlayTimerSettings has incorrect size.");
|
||||
|
||||
States states{};
|
||||
ParentalControlSettings settings{};
|
||||
RestrictionSettings restriction_settings{};
|
||||
std::array<char, 8> pin_code{};
|
||||
Capability capability{};
|
||||
|
@ -102,16 +102,17 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
|
||||
m_system.ServiceManager().RegisterService(service_name, max_sessions, handler)));
|
||||
|
||||
// Get the registered port.
|
||||
auto port = m_system.ServiceManager().GetServicePort(service_name);
|
||||
ASSERT(port.Succeeded());
|
||||
Kernel::KPort* port{};
|
||||
ASSERT(
|
||||
R_SUCCEEDED(m_system.ServiceManager().GetServicePort(std::addressof(port), service_name)));
|
||||
|
||||
// Open a new reference to the server port.
|
||||
(*port)->GetServerPort().Open();
|
||||
port->GetServerPort().Open();
|
||||
|
||||
// Begin tracking the server port.
|
||||
{
|
||||
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.
|
||||
|
@ -52,8 +52,7 @@ static Result ValidateServiceName(const std::string& name) {
|
||||
|
||||
Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
|
||||
SessionRequestHandlerPtr handler) {
|
||||
|
||||
CASCADE_CODE(ValidateServiceName(name));
|
||||
R_TRY(ValidateServiceName(name));
|
||||
|
||||
std::scoped_lock lk{lock};
|
||||
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) {
|
||||
CASCADE_CODE(ValidateServiceName(name));
|
||||
R_TRY(ValidateServiceName(name));
|
||||
|
||||
std::scoped_lock lk{lock};
|
||||
const auto iter = registered_services.find(name);
|
||||
@ -92,8 +91,8 @@ Result ServiceManager::UnregisterService(const std::string& name) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) {
|
||||
CASCADE_CODE(ValidateServiceName(name));
|
||||
Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::string& name) {
|
||||
R_TRY(ValidateServiceName(name));
|
||||
|
||||
std::scoped_lock lk{lock};
|
||||
auto it = service_ports.find(name);
|
||||
@ -102,7 +101,8 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
|
||||
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) {
|
||||
auto result = GetServiceImpl(ctx);
|
||||
Kernel::KClientSession* client_session{};
|
||||
auto result = GetServiceImpl(&client_session, ctx);
|
||||
if (ctx.GetIsDeferred()) {
|
||||
// Don't overwrite the command buffer.
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.Succeeded()) {
|
||||
if (result == ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(result.Code());
|
||||
rb.PushMoveObjects(result.Unwrap());
|
||||
rb.Push(result);
|
||||
rb.PushMoveObjects(client_session);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
rb.Push(result);
|
||||
}
|
||||
}
|
||||
|
||||
void SM::GetServiceTipc(HLERequestContext& ctx) {
|
||||
auto result = GetServiceImpl(ctx);
|
||||
Kernel::KClientSession* client_session{};
|
||||
auto result = GetServiceImpl(&client_session, ctx);
|
||||
if (ctx.GetIsDeferred()) {
|
||||
// Don't overwrite the command buffer.
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(result.Code());
|
||||
rb.PushMoveObjects(result.Succeeded() ? result.Unwrap() : nullptr);
|
||||
rb.Push(result);
|
||||
rb.PushMoveObjects(result == ResultSuccess ? client_session : nullptr);
|
||||
}
|
||||
|
||||
static std::string PopServiceName(IPC::RequestParser& rp) {
|
||||
@ -161,7 +163,7 @@ static std::string PopServiceName(IPC::RequestParser& rp) {
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(HLERequestContext& ctx) {
|
||||
Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLERequestContext& ctx) {
|
||||
if (!ctx.GetManager()->GetIsInitializedForSm()) {
|
||||
return Service::SM::ResultInvalidClient;
|
||||
}
|
||||
@ -170,18 +172,18 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(HLERequestContext& ctx) {
|
||||
std::string name(PopServiceName(rp));
|
||||
|
||||
// Find the named port.
|
||||
auto port_result = service_manager.GetServicePort(name);
|
||||
if (port_result.Code() == Service::SM::ResultInvalidServiceName) {
|
||||
Kernel::KPort* port{};
|
||||
auto port_result = service_manager.GetServicePort(&port, name);
|
||||
if (port_result == Service::SM::ResultInvalidServiceName) {
|
||||
LOG_ERROR(Service_SM, "Invalid service name '{}'", name);
|
||||
return Service::SM::ResultInvalidServiceName;
|
||||
}
|
||||
|
||||
if (port_result.Failed()) {
|
||||
if (port_result != ResultSuccess) {
|
||||
LOG_INFO(Service_SM, "Waiting for service {} to become available", name);
|
||||
ctx.SetIsDeferred();
|
||||
return Service::SM::ResultNotRegistered;
|
||||
}
|
||||
auto& port = port_result.Unwrap();
|
||||
|
||||
// Create a new 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());
|
||||
|
||||
return session;
|
||||
*out_client_session = session;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void SM::RegisterService(HLERequestContext& ctx) {
|
||||
|
@ -42,7 +42,7 @@ private:
|
||||
void RegisterService(HLERequestContext& ctx);
|
||||
void UnregisterService(HLERequestContext& ctx);
|
||||
|
||||
ResultVal<Kernel::KClientSession*> GetServiceImpl(HLERequestContext& ctx);
|
||||
Result GetServiceImpl(Kernel::KClientSession** out_client_session, HLERequestContext& ctx);
|
||||
|
||||
ServiceManager& service_manager;
|
||||
Kernel::KernelCore& kernel;
|
||||
@ -55,7 +55,7 @@ public:
|
||||
|
||||
Result RegisterService(std::string name, u32 max_sessions, SessionRequestHandlerPtr handler);
|
||||
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>
|
||||
std::shared_ptr<T> GetService(const std::string& service_name) const {
|
||||
|
@ -54,7 +54,7 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
|
||||
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.
|
||||
// For now we just return the string as-is, which is good enough when not
|
||||
// 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) {
|
||||
const auto res = ResolveImpl(fqdn_in);
|
||||
if (res.Failed()) {
|
||||
return res.Code();
|
||||
}
|
||||
if (res->size() >= fqdn_out.size()) {
|
||||
if (res.size() >= fqdn_out.size()) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,9 @@ enum class Errno : u32 {
|
||||
AGAIN = 11,
|
||||
INVAL = 22,
|
||||
MFILE = 24,
|
||||
PIPE = 32,
|
||||
MSGSIZE = 90,
|
||||
CONNABORTED = 103,
|
||||
CONNRESET = 104,
|
||||
NOTCONN = 107,
|
||||
TIMEDOUT = 110,
|
||||
|
@ -23,10 +23,14 @@ Errno Translate(Network::Errno value) {
|
||||
return Errno::INVAL;
|
||||
case Network::Errno::MFILE:
|
||||
return Errno::MFILE;
|
||||
case Network::Errno::PIPE:
|
||||
return Errno::PIPE;
|
||||
case Network::Errno::NOTCONN:
|
||||
return Errno::NOTCONN;
|
||||
case Network::Errno::TIMEDOUT:
|
||||
return Errno::TIMEDOUT;
|
||||
case Network::Errno::CONNABORTED:
|
||||
return Errno::CONNABORTED;
|
||||
case Network::Errno::CONNRESET:
|
||||
return Errno::CONNRESET;
|
||||
case Network::Errno::INPROGRESS:
|
||||
|
@ -30,10 +30,10 @@ void Module::Interface::GetConfig(HLERequestContext& ctx) {
|
||||
|
||||
// This should call svcCallSecureMonitor with the appropriate args.
|
||||
// Since we do not have it implemented yet, we will use this for now.
|
||||
const auto smc_result = GetConfigImpl(config_item);
|
||||
const auto result_code = smc_result.Code();
|
||||
u64 smc_result{};
|
||||
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,
|
||||
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,
|
||||
result_code.raw, *smc_result);
|
||||
result_code.raw, smc_result);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(result_code);
|
||||
rb.Push(*smc_result);
|
||||
rb.Push(smc_result);
|
||||
}
|
||||
|
||||
void Module::Interface::ModularExponentiate(HLERequestContext& ctx) {
|
||||
@ -99,7 +99,7 @@ void Module::Interface::GetBootReason(HLERequestContext& ctx) {
|
||||
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) {
|
||||
case ConfigItem::DisableProgramVerification:
|
||||
case ConfigItem::DramId:
|
||||
@ -121,40 +121,50 @@ ResultVal<u64> Module::Interface::GetConfigImpl(ConfigItem config_item) const {
|
||||
return ResultSecureMonitorNotImplemented;
|
||||
case ConfigItem::ExosphereApiVersion:
|
||||
// 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_MICRO} << 40) |
|
||||
(static_cast<u64>(HLE::ApiVersion::GetTargetFirmware()));
|
||||
return ResultSuccess;
|
||||
case ConfigItem::ExosphereNeedsReboot:
|
||||
// We are executing, so we aren't in the process of rebooting.
|
||||
return u64{0};
|
||||
*out_config = u64{0};
|
||||
return ResultSuccess;
|
||||
case ConfigItem::ExosphereNeedsShutdown:
|
||||
// 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:
|
||||
// Get information about the current exosphere git commit hash.
|
||||
return u64{0};
|
||||
*out_config = u64{0};
|
||||
return ResultSuccess;
|
||||
case ConfigItem::ExosphereHasRcmBugPatch:
|
||||
// Get information about whether this unit has the RCM bug patched.
|
||||
return u64{0};
|
||||
*out_config = u64{0};
|
||||
return ResultSuccess;
|
||||
case ConfigItem::ExosphereBlankProdInfo:
|
||||
// Get whether this unit should simulate a "blanked" PRODINFO.
|
||||
return u64{0};
|
||||
*out_config = u64{0};
|
||||
return ResultSuccess;
|
||||
case ConfigItem::ExosphereAllowCalWrites:
|
||||
// Get whether this unit should allow writing to the calibration partition.
|
||||
return u64{0};
|
||||
*out_config = u64{0};
|
||||
return ResultSuccess;
|
||||
case ConfigItem::ExosphereEmummcType:
|
||||
// Get what kind of emummc this unit has active.
|
||||
return u64{0};
|
||||
*out_config = u64{0};
|
||||
return ResultSuccess;
|
||||
case ConfigItem::ExospherePayloadAddress:
|
||||
// Gets the physical address of the reboot payload buffer, if one exists.
|
||||
return ResultSecureMonitorNotInitialized;
|
||||
case ConfigItem::ExosphereLogConfiguration:
|
||||
// Get the log configuration.
|
||||
return u64{0};
|
||||
*out_config = u64{0};
|
||||
return ResultSuccess;
|
||||
case ConfigItem::ExosphereForceEnableUsb30:
|
||||
// Get whether usb 3.0 should be force-enabled.
|
||||
return u64{0};
|
||||
*out_config = u64{0};
|
||||
return ResultSuccess;
|
||||
default:
|
||||
return ResultSecureMonitorInvalidArgument;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ public:
|
||||
std::shared_ptr<Module> module;
|
||||
|
||||
private:
|
||||
ResultVal<u64> GetConfigImpl(ConfigItem config_item) const;
|
||||
Result GetConfigImpl(u64* out_config, ConfigItem config_item) const;
|
||||
|
||||
std::mt19937 rng;
|
||||
};
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
@ -141,12 +142,12 @@ private:
|
||||
bool did_set_host_name = false;
|
||||
bool did_handshake = false;
|
||||
|
||||
ResultVal<s32> SetSocketDescriptorImpl(s32 fd) {
|
||||
Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
|
||||
LOG_DEBUG(Service_SSL, "called, fd={}", fd);
|
||||
ASSERT(!did_handshake);
|
||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
|
||||
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
|
||||
s32 ret_fd;
|
||||
|
||||
// Based on https://switchbrew.org/wiki/SSL_services#SetSocketDescriptor
|
||||
if (do_not_close_socket) {
|
||||
auto res = bsd->DuplicateSocketImpl(fd);
|
||||
@ -156,9 +157,9 @@ private:
|
||||
}
|
||||
fd = *res;
|
||||
fd_to_close = fd;
|
||||
ret_fd = fd;
|
||||
*out_fd = fd;
|
||||
} else {
|
||||
ret_fd = -1;
|
||||
*out_fd = -1;
|
||||
}
|
||||
std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(fd);
|
||||
if (!sock.has_value()) {
|
||||
@ -167,7 +168,7 @@ private:
|
||||
}
|
||||
socket = std::move(*sock);
|
||||
backend->SetSocket(socket);
|
||||
return ret_fd;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SetHostNameImpl(const std::string& hostname) {
|
||||
@ -247,34 +248,36 @@ private:
|
||||
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; });
|
||||
std::vector<u8> res(size);
|
||||
ResultVal<size_t> actual = backend->Read(res);
|
||||
if (actual.Failed()) {
|
||||
return actual.Code();
|
||||
size_t actual_size{};
|
||||
Result res = backend->Read(&actual_size, *out_data);
|
||||
if (res != ResultSuccess) {
|
||||
return res;
|
||||
}
|
||||
res.resize(*actual);
|
||||
out_data->resize(actual_size);
|
||||
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; });
|
||||
return backend->Write(data);
|
||||
return backend->Write(out_size, data);
|
||||
}
|
||||
|
||||
ResultVal<s32> PendingImpl() {
|
||||
Result PendingImpl(s32* out_pending) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called.");
|
||||
return 0;
|
||||
*out_pending = 0;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void SetSocketDescriptor(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
const ResultVal<s32> res = SetSocketDescriptorImpl(fd);
|
||||
const s32 in_fd = rp.Pop<s32>();
|
||||
s32 out_fd{-1};
|
||||
const Result res = SetSocketDescriptorImpl(&out_fd, in_fd);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res.Code());
|
||||
rb.Push<s32>(res.ValueOr(-1));
|
||||
rb.Push(res);
|
||||
rb.Push<s32>(out_fd);
|
||||
}
|
||||
|
||||
void SetHostName(HLERequestContext& ctx) {
|
||||
@ -313,14 +316,15 @@ private:
|
||||
};
|
||||
static_assert(sizeof(OutputParameters) == 0x8);
|
||||
|
||||
const Result res = DoHandshakeImpl();
|
||||
Result res = DoHandshakeImpl();
|
||||
OutputParameters out{};
|
||||
if (res == ResultSuccess) {
|
||||
auto certs = backend->GetServerCerts();
|
||||
if (certs.Succeeded()) {
|
||||
const std::vector<u8> certs_buf = SerializeServerCerts(*certs);
|
||||
std::vector<std::vector<u8>> certs;
|
||||
res = backend->GetServerCerts(&certs);
|
||||
if (res == ResultSuccess) {
|
||||
const std::vector<u8> certs_buf = SerializeServerCerts(certs);
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -330,29 +334,32 @@ private:
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(res.Code());
|
||||
if (res.Succeeded()) {
|
||||
rb.Push(static_cast<u32>(res->size()));
|
||||
ctx.WriteBuffer(*res);
|
||||
rb.Push(res);
|
||||
if (res == ResultSuccess) {
|
||||
rb.Push(static_cast<u32>(output_bytes.size()));
|
||||
ctx.WriteBuffer(output_bytes);
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(0));
|
||||
}
|
||||
}
|
||||
|
||||
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};
|
||||
rb.Push(res.Code());
|
||||
rb.Push(static_cast<u32>(res.ValueOr(0)));
|
||||
rb.Push(res);
|
||||
rb.Push(static_cast<u32>(write_size));
|
||||
}
|
||||
|
||||
void Pending(HLERequestContext& ctx) {
|
||||
const ResultVal<s32> res = PendingImpl();
|
||||
s32 pending_size{0};
|
||||
const Result res = PendingImpl(&pending_size);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res.Code());
|
||||
rb.Push<s32>(res.ValueOr(0));
|
||||
rb.Push(res);
|
||||
rb.Push<s32>(pending_size);
|
||||
}
|
||||
|
||||
void SetSessionCacheMode(HLERequestContext& ctx) {
|
||||
@ -438,13 +445,14 @@ private:
|
||||
void CreateConnection(HLERequestContext& ctx) {
|
||||
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};
|
||||
rb.Push(backend_res.Code());
|
||||
if (backend_res.Succeeded()) {
|
||||
rb.Push(res);
|
||||
if (res == ResultSuccess) {
|
||||
rb.PushIpcInterface<ISslConnection>(system, ssl_version, shared_data,
|
||||
std::move(*backend_res));
|
||||
std::move(backend));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,11 +35,11 @@ public:
|
||||
virtual void SetSocket(std::shared_ptr<Network::SocketBase> socket) = 0;
|
||||
virtual Result SetHostName(const std::string& hostname) = 0;
|
||||
virtual Result DoHandshake() = 0;
|
||||
virtual ResultVal<size_t> Read(std::span<u8> data) = 0;
|
||||
virtual ResultVal<size_t> Write(std::span<const u8> data) = 0;
|
||||
virtual ResultVal<std::vector<std::vector<u8>>> GetServerCerts() = 0;
|
||||
virtual Result Read(size_t* out_size, std::span<u8> data) = 0;
|
||||
virtual Result Write(size_t* out_size, std::span<const u8> data) = 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
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
namespace Service::SSL {
|
||||
|
||||
ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() {
|
||||
Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend) {
|
||||
LOG_ERROR(Service_SSL,
|
||||
"Can't create SSL connection because no SSL backend is available on this platform");
|
||||
return ResultInternalError;
|
||||
|
@ -105,31 +105,30 @@ public:
|
||||
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 {
|
||||
size_t actual;
|
||||
const int ret = SSL_read_ex(ssl, data.data(), data.size(), &actual);
|
||||
return HandleReturn("SSL_read_ex", actual, ret);
|
||||
Result Read(size_t* out_size, std::span<u8> data) override {
|
||||
const int ret = SSL_read_ex(ssl, data.data(), data.size(), out_size);
|
||||
return HandleReturn("SSL_read_ex", out_size, ret);
|
||||
}
|
||||
|
||||
ResultVal<size_t> Write(std::span<const u8> data) override {
|
||||
size_t actual;
|
||||
const int ret = SSL_write_ex(ssl, data.data(), data.size(), &actual);
|
||||
return HandleReturn("SSL_write_ex", actual, ret);
|
||||
Result Write(size_t* out_size, std::span<const u8> data) override {
|
||||
const int ret = SSL_write_ex(ssl, data.data(), data.size(), out_size);
|
||||
return HandleReturn("SSL_write_ex", out_size, 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);
|
||||
CheckOpenSSLErrors();
|
||||
switch (ssl_err) {
|
||||
case SSL_ERROR_NONE:
|
||||
return actual;
|
||||
return ResultSuccess;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_ZERO_RETURN", what);
|
||||
// DoHandshake special-cases this, but for Read and Write:
|
||||
return size_t(0);
|
||||
*actual = 0;
|
||||
return ResultSuccess;
|
||||
case SSL_ERROR_WANT_READ:
|
||||
LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_WANT_READ", what);
|
||||
return ResultWouldBlock;
|
||||
@ -139,20 +138,20 @@ public:
|
||||
default:
|
||||
if (ssl_err == SSL_ERROR_SYSCALL && got_read_eof) {
|
||||
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);
|
||||
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);
|
||||
if (!chain) {
|
||||
LOG_ERROR(Service_SSL, "SSL_get_peer_cert_chain returned nullptr");
|
||||
return ResultInternalError;
|
||||
}
|
||||
std::vector<std::vector<u8>> ret;
|
||||
int count = sk_X509_num(chain);
|
||||
ASSERT(count >= 0);
|
||||
for (int i = 0; i < count; i++) {
|
||||
@ -161,10 +160,10 @@ public:
|
||||
unsigned char* buf = nullptr;
|
||||
int len = i2d_X509(x509, &buf);
|
||||
ASSERT_OR_EXECUTE(len >= 0 && buf, { continue; });
|
||||
ret.emplace_back(buf, buf + len);
|
||||
out_certs->emplace_back(buf, buf + len);
|
||||
OPENSSL_free(buf);
|
||||
}
|
||||
return ret;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
~SSLConnectionBackendOpenSSL() {
|
||||
@ -253,13 +252,13 @@ public:
|
||||
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>();
|
||||
const Result res = conn->Init();
|
||||
if (res.IsFailure()) {
|
||||
return res;
|
||||
}
|
||||
return conn;
|
||||
|
||||
R_TRY(conn->Init());
|
||||
|
||||
*out_backend = std::move(conn);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -299,21 +299,22 @@ public:
|
||||
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) {
|
||||
LOG_ERROR(Service_SSL, "Called Read but we did not successfully handshake");
|
||||
return ResultInternalError;
|
||||
}
|
||||
if (data.size() == 0 || got_read_eof) {
|
||||
return size_t(0);
|
||||
return ResultSuccess;
|
||||
}
|
||||
while (1) {
|
||||
if (!cleartext_read_buf.empty()) {
|
||||
const size_t read_size = std::min(cleartext_read_buf.size(), data.size());
|
||||
std::memcpy(data.data(), cleartext_read_buf.data(), read_size);
|
||||
*out_size = std::min(cleartext_read_buf.size(), data.size());
|
||||
std::memcpy(data.data(), cleartext_read_buf.data(), *out_size);
|
||||
cleartext_read_buf.erase(cleartext_read_buf.begin(),
|
||||
cleartext_read_buf.begin() + read_size);
|
||||
return read_size;
|
||||
cleartext_read_buf.begin() + *out_size);
|
||||
return ResultSuccess;
|
||||
}
|
||||
if (!ciphertext_read_buf.empty()) {
|
||||
SecBuffer empty{
|
||||
@ -366,7 +367,8 @@ public:
|
||||
case SEC_I_CONTEXT_EXPIRED:
|
||||
// Server hung up by sending close_notify.
|
||||
got_read_eof = true;
|
||||
return size_t(0);
|
||||
*out_size = 0;
|
||||
return ResultSuccess;
|
||||
default:
|
||||
LOG_ERROR(Service_SSL, "DecryptMessage failed: {}",
|
||||
Common::NativeErrorToString(ret));
|
||||
@ -379,18 +381,21 @@ public:
|
||||
}
|
||||
if (ciphertext_read_buf.empty()) {
|
||||
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) {
|
||||
LOG_ERROR(Service_SSL, "Called Write but we did not successfully handshake");
|
||||
return ResultInternalError;
|
||||
}
|
||||
if (data.size() == 0) {
|
||||
return size_t(0);
|
||||
return ResultSuccess;
|
||||
}
|
||||
data = data.subspan(0, std::min<size_t>(data.size(), stream_sizes.cbMaximumMessage));
|
||||
if (!cleartext_write_buf.empty()) {
|
||||
@ -402,7 +407,7 @@ public:
|
||||
LOG_ERROR(Service_SSL, "Called Write but buffer does not match previous buffer");
|
||||
return ResultInternalError;
|
||||
}
|
||||
return WriteAlreadyEncryptedData();
|
||||
return WriteAlreadyEncryptedData(out_size);
|
||||
} else {
|
||||
cleartext_write_buf.assign(data.begin(), data.end());
|
||||
}
|
||||
@ -448,21 +453,21 @@ public:
|
||||
tmp_data_buf.end());
|
||||
ciphertext_write_buf.insert(ciphertext_write_buf.end(), trailer_buf.begin(),
|
||||
trailer_buf.end());
|
||||
return WriteAlreadyEncryptedData();
|
||||
return WriteAlreadyEncryptedData(out_size);
|
||||
}
|
||||
|
||||
ResultVal<size_t> WriteAlreadyEncryptedData() {
|
||||
Result WriteAlreadyEncryptedData(size_t* out_size) {
|
||||
const Result r = FlushCiphertextWriteBuf();
|
||||
if (r != ResultSuccess) {
|
||||
return r;
|
||||
}
|
||||
// write buf is empty
|
||||
const size_t cleartext_bytes_written = cleartext_write_buf.size();
|
||||
*out_size = cleartext_write_buf.size();
|
||||
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;
|
||||
const SECURITY_STATUS ret =
|
||||
QueryContextAttributes(&ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &returned_cert);
|
||||
@ -473,16 +478,15 @@ public:
|
||||
return ResultInternalError;
|
||||
}
|
||||
PCCERT_CONTEXT some_cert = nullptr;
|
||||
std::vector<std::vector<u8>> certs;
|
||||
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) +
|
||||
some_cert->cbCertEncoded);
|
||||
}
|
||||
std::reverse(certs.begin(),
|
||||
certs.end()); // Windows returns certs in reverse order from what we want
|
||||
std::reverse(out_certs->begin(),
|
||||
out_certs->end()); // Windows returns certs in reverse order from what we want
|
||||
CertFreeCertificateContext(returned_cert);
|
||||
return certs;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
~SSLConnectionBackendSchannel() {
|
||||
@ -532,13 +536,13 @@ public:
|
||||
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>();
|
||||
const Result res = conn->Init();
|
||||
if (res.IsFailure()) {
|
||||
return res;
|
||||
}
|
||||
return conn;
|
||||
|
||||
R_TRY(conn->Init());
|
||||
|
||||
*out_backend = std::move(conn);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Service::SSL
|
||||
|
@ -100,27 +100,23 @@ public:
|
||||
|
||||
Result DoHandshake() override {
|
||||
OSStatus status = SSLHandshake(context);
|
||||
return HandleReturn("SSLHandshake", 0, status).Code();
|
||||
return HandleReturn("SSLHandshake", 0, status);
|
||||
}
|
||||
|
||||
ResultVal<size_t> Read(std::span<u8> data) override {
|
||||
size_t actual;
|
||||
OSStatus status = SSLRead(context, data.data(), data.size(), &actual);
|
||||
;
|
||||
return HandleReturn("SSLRead", actual, status);
|
||||
Result Read(size_t* out_size, std::span<u8> data) override {
|
||||
OSStatus status = SSLRead(context, data.data(), data.size(), out_size);
|
||||
return HandleReturn("SSLRead", out_size, status);
|
||||
}
|
||||
|
||||
ResultVal<size_t> Write(std::span<const u8> data) override {
|
||||
size_t actual;
|
||||
OSStatus status = SSLWrite(context, data.data(), data.size(), &actual);
|
||||
;
|
||||
return HandleReturn("SSLWrite", actual, status);
|
||||
Result Write(size_t* out_size, std::span<const u8> data) override {
|
||||
OSStatus status = SSLWrite(context, data.data(), data.size(), out_size);
|
||||
return HandleReturn("SSLWrite", out_size, 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) {
|
||||
case 0:
|
||||
return actual;
|
||||
return ResultSuccess;
|
||||
case errSSLWouldBlock:
|
||||
return ResultWouldBlock;
|
||||
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;
|
||||
OSStatus status = SSLCopyPeerTrust(context, &trust.ptr);
|
||||
if (status) {
|
||||
LOG_ERROR(Service_SSL, "SSLCopyPeerTrust failed: {}", OSStatusToString(status));
|
||||
return ResultInternalError;
|
||||
}
|
||||
std::vector<std::vector<u8>> ret;
|
||||
for (CFIndex i = 0, count = SecTrustGetCertificateCount(trust); i < count; i++) {
|
||||
SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i);
|
||||
CFReleaser<CFDataRef> data(SecCertificateCopyData(cert));
|
||||
ASSERT_OR_EXECUTE(data, { return ResultInternalError; });
|
||||
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) {
|
||||
@ -210,13 +205,13 @@ private:
|
||||
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>();
|
||||
const Result res = conn->Init();
|
||||
if (res.IsFailure()) {
|
||||
return res;
|
||||
}
|
||||
return conn;
|
||||
|
||||
R_TRY(conn->Init());
|
||||
|
||||
*out_backend = std::move(conn);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Service::SSL
|
||||
|
@ -58,14 +58,15 @@ const Layer& Display::GetLayer(std::size_t index) const {
|
||||
return *layers.at(index);
|
||||
}
|
||||
|
||||
ResultVal<Kernel::KReadableEvent*> Display::GetVSyncEvent() {
|
||||
Result Display::GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event) {
|
||||
if (got_vsync_event) {
|
||||
return ResultPermissionDenied;
|
||||
}
|
||||
|
||||
got_vsync_event = true;
|
||||
|
||||
return GetVSyncEventUnchecked();
|
||||
*out_vsync_event = GetVSyncEventUnchecked();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() {
|
||||
|
@ -85,7 +85,7 @@ public:
|
||||
* @returns The internal Vsync event if it has not yet been retrieved,
|
||||
* VI::ResultPermissionDenied otherwise.
|
||||
*/
|
||||
[[nodiscard]] ResultVal<Kernel::KReadableEvent*> GetVSyncEvent();
|
||||
[[nodiscard]] Result GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event);
|
||||
|
||||
/// Gets the internal vsync event.
|
||||
Kernel::KReadableEvent* GetVSyncEventUnchecked();
|
||||
|
@ -683,9 +683,9 @@ private:
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
|
||||
|
||||
const auto vsync_event = nv_flinger.FindVsyncEvent(display_id);
|
||||
if (vsync_event.Failed()) {
|
||||
const auto result = vsync_event.Code();
|
||||
Kernel::KReadableEvent* vsync_event{};
|
||||
const auto result = nv_flinger.FindVsyncEvent(&vsync_event, display_id);
|
||||
if (result != ResultSuccess) {
|
||||
if (result == ResultNotFound) {
|
||||
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};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(*vsync_event);
|
||||
rb.PushCopyObjects(vsync_event);
|
||||
}
|
||||
|
||||
void ConvertScalingMode(HLERequestContext& ctx) {
|
||||
@ -705,15 +705,16 @@ private:
|
||||
const auto mode = rp.PopEnum<NintendoScaleMode>();
|
||||
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};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushEnum(*converted_mode);
|
||||
rb.PushEnum(converted_mode);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(converted_mode.Code());
|
||||
rb.Push(result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -760,18 +761,24 @@ private:
|
||||
rb.Push(alignment);
|
||||
}
|
||||
|
||||
static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) {
|
||||
static Result ConvertScalingModeImpl(ConvertedScaleMode* out_scaling_mode,
|
||||
NintendoScaleMode mode) {
|
||||
switch (mode) {
|
||||
case NintendoScaleMode::None:
|
||||
return ConvertedScaleMode::None;
|
||||
*out_scaling_mode = ConvertedScaleMode::None;
|
||||
return ResultSuccess;
|
||||
case NintendoScaleMode::Freeze:
|
||||
return ConvertedScaleMode::Freeze;
|
||||
*out_scaling_mode = ConvertedScaleMode::Freeze;
|
||||
return ResultSuccess;
|
||||
case NintendoScaleMode::ScaleToWindow:
|
||||
return ConvertedScaleMode::ScaleToWindow;
|
||||
*out_scaling_mode = ConvertedScaleMode::ScaleToWindow;
|
||||
return ResultSuccess;
|
||||
case NintendoScaleMode::ScaleAndCrop:
|
||||
return ConvertedScaleMode::ScaleAndCrop;
|
||||
*out_scaling_mode = ConvertedScaleMode::ScaleAndCrop;
|
||||
return ResultSuccess;
|
||||
case NintendoScaleMode::PreserveAspectRatio:
|
||||
return ConvertedScaleMode::PreserveAspectRatio;
|
||||
*out_scaling_mode = ConvertedScaleMode::PreserveAspectRatio;
|
||||
return ResultSuccess;
|
||||
default:
|
||||
LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode);
|
||||
return ResultOperationFailed;
|
||||
|
@ -39,6 +39,11 @@ namespace Network {
|
||||
|
||||
namespace {
|
||||
|
||||
enum class CallType {
|
||||
Send,
|
||||
Other,
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
using socklen_t = int;
|
||||
@ -96,7 +101,7 @@ bool EnableNonBlock(SOCKET fd, bool enable) {
|
||||
return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
|
||||
}
|
||||
|
||||
Errno TranslateNativeError(int e) {
|
||||
Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
|
||||
switch (e) {
|
||||
case 0:
|
||||
return Errno::SUCCESS;
|
||||
@ -112,6 +117,14 @@ Errno TranslateNativeError(int e) {
|
||||
return Errno::AGAIN;
|
||||
case WSAECONNREFUSED:
|
||||
return Errno::CONNREFUSED;
|
||||
case WSAECONNABORTED:
|
||||
if (call_type == CallType::Send) {
|
||||
// Winsock yields WSAECONNABORTED from `send` in situations where Unix
|
||||
// systems, and actual Switches, yield EPIPE.
|
||||
return Errno::PIPE;
|
||||
} else {
|
||||
return Errno::CONNABORTED;
|
||||
}
|
||||
case WSAECONNRESET:
|
||||
return Errno::CONNRESET;
|
||||
case WSAEHOSTUNREACH:
|
||||
@ -198,7 +211,7 @@ bool EnableNonBlock(int fd, bool enable) {
|
||||
return fcntl(fd, F_SETFL, flags) == 0;
|
||||
}
|
||||
|
||||
Errno TranslateNativeError(int e) {
|
||||
Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
|
||||
switch (e) {
|
||||
case 0:
|
||||
return Errno::SUCCESS;
|
||||
@ -208,6 +221,10 @@ Errno TranslateNativeError(int e) {
|
||||
return Errno::INVAL;
|
||||
case EMFILE:
|
||||
return Errno::MFILE;
|
||||
case EPIPE:
|
||||
return Errno::PIPE;
|
||||
case ECONNABORTED:
|
||||
return Errno::CONNABORTED;
|
||||
case ENOTCONN:
|
||||
return Errno::NOTCONN;
|
||||
case EAGAIN:
|
||||
@ -236,13 +253,13 @@ Errno TranslateNativeError(int e) {
|
||||
|
||||
#endif
|
||||
|
||||
Errno GetAndLogLastError() {
|
||||
Errno GetAndLogLastError(CallType call_type = CallType::Other) {
|
||||
#ifdef _WIN32
|
||||
int e = WSAGetLastError();
|
||||
#else
|
||||
int e = errno;
|
||||
#endif
|
||||
const Errno err = TranslateNativeError(e);
|
||||
const Errno err = TranslateNativeError(e, call_type);
|
||||
if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) {
|
||||
// These happen during normal operation, so only log them at debug level.
|
||||
LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
|
||||
@ -476,7 +493,13 @@ NetworkInstance::~NetworkInstance() {
|
||||
std::optional<IPv4Address> GetHostIPv4Address() {
|
||||
const auto network_interface = Network::GetSelectedNetworkInterface();
|
||||
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 {};
|
||||
}
|
||||
|
||||
@ -725,13 +748,17 @@ std::pair<s32, Errno> Socket::Send(std::span<const u8> message, int flags) {
|
||||
ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
|
||||
ASSERT(flags == 0);
|
||||
|
||||
int native_flags = 0;
|
||||
#if YUZU_UNIX
|
||||
native_flags |= MSG_NOSIGNAL; // do not send us SIGPIPE
|
||||
#endif
|
||||
const auto result = send(fd, reinterpret_cast<const char*>(message.data()),
|
||||
static_cast<int>(message.size()), 0);
|
||||
static_cast<int>(message.size()), native_flags);
|
||||
if (result != SOCKET_ERROR) {
|
||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||
}
|
||||
|
||||
return {-1, GetAndLogLastError()};
|
||||
return {-1, GetAndLogLastError(CallType::Send)};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message,
|
||||
@ -753,7 +780,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message,
|
||||
return {static_cast<s32>(result), Errno::SUCCESS};
|
||||
}
|
||||
|
||||
return {-1, GetAndLogLastError()};
|
||||
return {-1, GetAndLogLastError(CallType::Send)};
|
||||
}
|
||||
|
||||
Errno Socket::Close() {
|
||||
|
@ -33,10 +33,12 @@ enum class Errno {
|
||||
BADF,
|
||||
INVAL,
|
||||
MFILE,
|
||||
PIPE,
|
||||
NOTCONN,
|
||||
AGAIN,
|
||||
CONNREFUSED,
|
||||
CONNRESET,
|
||||
CONNABORTED,
|
||||
HOSTUNREACH,
|
||||
NETDOWN,
|
||||
NETUNREACH,
|
||||
|
@ -200,7 +200,14 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -509,9 +509,9 @@ class GuestMemory {
|
||||
|
||||
public:
|
||||
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)
|
||||
: memory{memory_}, addr{addr_}, size{size_} {
|
||||
: m_memory{memory}, m_addr{addr}, m_size{size} {
|
||||
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Read) {
|
||||
Read(addr, size, backup);
|
||||
@ -521,89 +521,97 @@ public:
|
||||
~GuestMemory() = default;
|
||||
|
||||
T* data() noexcept {
|
||||
return data_span.data();
|
||||
return m_data_span.data();
|
||||
}
|
||||
|
||||
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 {
|
||||
return data();
|
||||
return this->data();
|
||||
}
|
||||
|
||||
[[nodiscard]] const T* begin() const noexcept {
|
||||
return data();
|
||||
return this->data();
|
||||
}
|
||||
|
||||
[[nodiscard]] T* end() noexcept {
|
||||
return data() + size;
|
||||
return this->data() + this->size();
|
||||
}
|
||||
|
||||
[[nodiscard]] const T* end() const noexcept {
|
||||
return data() + size;
|
||||
return this->data() + this->size();
|
||||
}
|
||||
|
||||
T& operator[](size_t index) noexcept {
|
||||
return data_span[index];
|
||||
return m_data_span[index];
|
||||
}
|
||||
|
||||
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 {
|
||||
addr = addr_;
|
||||
size = size_;
|
||||
addr_changed = true;
|
||||
void SetAddressAndSize(u64 addr, std::size_t size) noexcept {
|
||||
m_addr = addr;
|
||||
m_size = size;
|
||||
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 {
|
||||
addr = addr_;
|
||||
size = size_;
|
||||
if (size == 0) {
|
||||
is_data_copy = true;
|
||||
m_addr = addr;
|
||||
m_size = size;
|
||||
if (m_size == 0) {
|
||||
m_is_data_copy = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (TrySetSpan()) {
|
||||
if (this->TrySetSpan()) {
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
memory.FlushRegion(addr, size * sizeof(T));
|
||||
m_memory.FlushRegion(m_addr, this->size_bytes());
|
||||
}
|
||||
} else {
|
||||
if (backup) {
|
||||
backup->resize_destructive(size);
|
||||
data_span = *backup;
|
||||
backup->resize_destructive(this->size());
|
||||
m_data_span = *backup;
|
||||
} else {
|
||||
data_copy.resize(size);
|
||||
data_span = std::span(data_copy);
|
||||
m_data_copy.resize(this->size());
|
||||
m_data_span = std::span(m_data_copy);
|
||||
}
|
||||
is_data_copy = true;
|
||||
span_valid = true;
|
||||
m_is_data_copy = true;
|
||||
m_span_valid = true;
|
||||
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 {
|
||||
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 {
|
||||
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) {
|
||||
memory.WriteBlock(addr, write_data.data(), size * sizeof(T));
|
||||
m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes());
|
||||
} else {
|
||||
memory.WriteBlockUnsafe(addr, write_data.data(), size * sizeof(T));
|
||||
m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
bool TrySetSpan() noexcept {
|
||||
if (u8* ptr = memory.GetSpan(addr, size * sizeof(T)); ptr) {
|
||||
data_span = {reinterpret_cast<T*>(ptr), size};
|
||||
span_valid = true;
|
||||
if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) {
|
||||
m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
|
||||
m_span_valid = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -611,36 +619,36 @@ public:
|
||||
|
||||
protected:
|
||||
bool IsDataCopy() const noexcept {
|
||||
return is_data_copy;
|
||||
return m_is_data_copy;
|
||||
}
|
||||
|
||||
bool AddressChanged() const noexcept {
|
||||
return addr_changed;
|
||||
return m_addr_changed;
|
||||
}
|
||||
|
||||
M& memory;
|
||||
u64 addr;
|
||||
size_t size;
|
||||
std::span<T> data_span{};
|
||||
std::vector<T> data_copy;
|
||||
bool span_valid{false};
|
||||
bool is_data_copy{false};
|
||||
bool addr_changed{false};
|
||||
M& m_memory;
|
||||
u64 m_addr{};
|
||||
size_t m_size{};
|
||||
std::span<T> m_data_span{};
|
||||
std::vector<T> m_data_copy{};
|
||||
bool m_span_valid{false};
|
||||
bool m_is_data_copy{false};
|
||||
bool m_addr_changed{false};
|
||||
};
|
||||
|
||||
template <typename M, typename T, GuestMemoryFlags FLAGS>
|
||||
class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> {
|
||||
public:
|
||||
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)
|
||||
: GuestMemory<M, T, FLAGS>(memory_, addr_, size_, backup) {
|
||||
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
|
||||
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
|
||||
if (!this->TrySetSpan()) {
|
||||
if (backup) {
|
||||
this->data_span = *backup;
|
||||
this->span_valid = true;
|
||||
this->is_data_copy = true;
|
||||
this->m_data_span = *backup;
|
||||
this->m_span_valid = true;
|
||||
this->m_is_data_copy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -648,24 +656,21 @@ public:
|
||||
|
||||
~GuestMemoryScoped() {
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Write) {
|
||||
if (this->size == 0) [[unlikely]] {
|
||||
if (this->size() == 0) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->AddressChanged() || this->IsDataCopy()) {
|
||||
ASSERT(this->span_valid);
|
||||
ASSERT(this->m_span_valid);
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
|
||||
this->memory.WriteBlockCached(this->addr, this->data_span.data(),
|
||||
this->size * sizeof(T));
|
||||
this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes());
|
||||
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
this->memory.WriteBlock(this->addr, this->data_span.data(),
|
||||
this->size * sizeof(T));
|
||||
this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes());
|
||||
} else {
|
||||
this->memory.WriteBlockUnsafe(this->addr, this->data_span.data(),
|
||||
this->size * sizeof(T));
|
||||
this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
|
||||
}
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/polyfill_ranges.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
|
@ -220,7 +220,8 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
|
||||
ASSERT(num_textures <= MAX_TEXTURES);
|
||||
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 =
|
||||
!assembly_shaders || num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
|
||||
writes_global_memory &= !use_storage_buffers;
|
||||
@ -230,7 +231,6 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
|
||||
GenerateTransformFeedbackState();
|
||||
}
|
||||
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),
|
||||
shader_notify, backend, in_parallel,
|
||||
force_context_flush](ShaderContext::Context*) mutable {
|
||||
@ -559,15 +559,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
}
|
||||
|
||||
void GraphicsPipeline::ConfigureTransformFeedbackImpl() const {
|
||||
glTransformFeedbackStreamAttribsNV(num_xfb_attribs, xfb_attribs.data(), num_xfb_strides,
|
||||
xfb_streams.data(), GL_INTERLEAVED_ATTRIBS);
|
||||
glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), GL_SEPARATE_ATTRIBS);
|
||||
}
|
||||
|
||||
void GraphicsPipeline::GenerateTransformFeedbackState() {
|
||||
// TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
|
||||
// when this is required.
|
||||
GLint* cursor{xfb_attribs.data()};
|
||||
GLint* current_stream{xfb_streams.data()};
|
||||
|
||||
for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
|
||||
const auto& layout = key.xfb_state.layouts[feedback];
|
||||
@ -575,15 +573,6 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
|
||||
if (layout.varying_count == 0) {
|
||||
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];
|
||||
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_strides = static_cast<GLsizei>(current_stream - xfb_streams.data());
|
||||
}
|
||||
|
||||
void GraphicsPipeline::WaitForBuild() {
|
||||
|
@ -154,9 +154,7 @@ private:
|
||||
|
||||
static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
|
||||
GLsizei num_xfb_attribs{};
|
||||
GLsizei num_xfb_strides{};
|
||||
std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{};
|
||||
std::array<GLint, Maxwell::NumTransformFeedbackBuffers> xfb_streams{};
|
||||
|
||||
std::mutex built_mutex;
|
||||
std::condition_variable built_condvar;
|
||||
|
@ -326,6 +326,43 @@ std::vector<const char*> ExtensionListForVulkan(
|
||||
|
||||
} // 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,
|
||||
const vk::InstanceDispatch& dld_)
|
||||
: instance{instance_}, dld{dld_}, physical{physical_},
|
||||
@ -397,21 +434,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
if (is_qualcomm || is_turnip) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
|
||||
extensions.custom_border_color = false;
|
||||
loaded_extensions.erase(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
|
||||
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
if (is_qualcomm) {
|
||||
must_emulate_scaled_formats = true;
|
||||
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state");
|
||||
extensions.extended_dynamic_state = false;
|
||||
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Qualcomm drivers have a slow VK_KHR_push_descriptor implementation");
|
||||
extensions.push_descriptor = false;
|
||||
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
||||
RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
||||
|
||||
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
|
||||
// Patch the driver to enable BCn textures.
|
||||
@ -440,15 +476,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
must_emulate_scaled_formats = true;
|
||||
|
||||
LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
|
||||
extensions.extended_dynamic_state = false;
|
||||
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
|
||||
LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2");
|
||||
features.extended_dynamic_state2.extendedDynamicState2 = false;
|
||||
features.extended_dynamic_state2.extendedDynamicState2LogicOp = false;
|
||||
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
|
||||
extensions.extended_dynamic_state2 = false;
|
||||
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
if (is_nvidia) {
|
||||
@ -464,8 +497,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
case NvidiaArchitecture::VoltaOrOlder:
|
||||
if (nv_major_version < 527) {
|
||||
LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor");
|
||||
extensions.push_descriptor = false;
|
||||
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
||||
RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -480,8 +512,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state");
|
||||
extensions.extended_dynamic_state = false;
|
||||
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.extended_dynamic_state,
|
||||
features.extended_dynamic_state,
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
if (extensions.extended_dynamic_state2 && is_radv) {
|
||||
@ -490,11 +523,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
LOG_WARNING(
|
||||
Render_Vulkan,
|
||||
"RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
|
||||
features.extended_dynamic_state2.extendedDynamicState2 = false;
|
||||
features.extended_dynamic_state2.extendedDynamicState2LogicOp = false;
|
||||
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
|
||||
extensions.extended_dynamic_state2 = false;
|
||||
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.extended_dynamic_state2,
|
||||
features.extended_dynamic_state2,
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
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.
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2");
|
||||
features.extended_dynamic_state2.extendedDynamicState2 = false;
|
||||
features.extended_dynamic_state2.extendedDynamicState2LogicOp = false;
|
||||
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
|
||||
extensions.extended_dynamic_state2 = false;
|
||||
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.extended_dynamic_state2,
|
||||
features.extended_dynamic_state2,
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
if (extensions.extended_dynamic_state3 && is_radv) {
|
||||
@ -540,9 +569,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
if (is_rdna2) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
|
||||
features.vertex_input_dynamic_state.vertexInputDynamicState = false;
|
||||
extensions.vertex_input_dynamic_state = false;
|
||||
loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
|
||||
features.vertex_input_dynamic_state,
|
||||
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
if (extensions.vertex_input_dynamic_state && is_qualcomm) {
|
||||
@ -553,9 +582,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
LOG_WARNING(
|
||||
Render_Vulkan,
|
||||
"Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state");
|
||||
features.vertex_input_dynamic_state.vertexInputDynamicState = false;
|
||||
extensions.vertex_input_dynamic_state = false;
|
||||
loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
|
||||
features.vertex_input_dynamic_state,
|
||||
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) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax");
|
||||
extensions.sampler_filter_minmax = false;
|
||||
loaded_extensions.erase(VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME);
|
||||
RemoveExtension(extensions.sampler_filter_minmax,
|
||||
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;
|
||||
if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
|
||||
LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state");
|
||||
extensions.vertex_input_dynamic_state = false;
|
||||
loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
|
||||
features.vertex_input_dynamic_state,
|
||||
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
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
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor");
|
||||
extensions.push_descriptor = false;
|
||||
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
||||
RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
if (is_mvk) {
|
||||
@ -1007,33 +1036,28 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
||||
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() {
|
||||
// VK_EXT_custom_border_color
|
||||
extensions.custom_border_color = features.custom_border_color.customBorderColors &&
|
||||
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_depth_clip_control
|
||||
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_extended_dynamic_state
|
||||
extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
|
||||
RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state,
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state,
|
||||
features.extended_dynamic_state,
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
|
||||
// VK_EXT_extended_dynamic_state2
|
||||
extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2;
|
||||
RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state2,
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state2,
|
||||
features.extended_dynamic_state2,
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||
|
||||
// VK_EXT_extended_dynamic_state3
|
||||
@ -1048,26 +1072,28 @@ void Device::RemoveUnsuitableExtensions() {
|
||||
extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables;
|
||||
dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3;
|
||||
dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3;
|
||||
RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state3,
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state3,
|
||||
features.extended_dynamic_state3,
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
|
||||
|
||||
// VK_EXT_provoking_vertex
|
||||
extensions.provoking_vertex =
|
||||
features.provoking_vertex.provokingVertexLast &&
|
||||
features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
|
||||
RemoveExtensionIfUnsuitable(extensions.provoking_vertex,
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex, features.provoking_vertex,
|
||||
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
|
||||
|
||||
// VK_KHR_shader_atomic_int64
|
||||
extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics &&
|
||||
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_EXT_shader_demote_to_helper_invocation
|
||||
extensions.shader_demote_to_helper_invocation =
|
||||
features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation;
|
||||
RemoveExtensionIfUnsuitable(extensions.shader_demote_to_helper_invocation,
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.shader_demote_to_helper_invocation,
|
||||
features.shader_demote_to_helper_invocation,
|
||||
VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME);
|
||||
|
||||
// VK_EXT_subgroup_size_control
|
||||
@ -1075,7 +1101,8 @@ void Device::RemoveUnsuitableExtensions() {
|
||||
features.subgroup_size_control.subgroupSizeControl &&
|
||||
properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize &&
|
||||
properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize;
|
||||
RemoveExtensionIfUnsuitable(extensions.subgroup_size_control,
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.subgroup_size_control,
|
||||
features.subgroup_size_control,
|
||||
VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
|
||||
|
||||
// VK_EXT_transform_feedback
|
||||
@ -1086,24 +1113,27 @@ void Device::RemoveUnsuitableExtensions() {
|
||||
properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
|
||||
properties.transform_feedback.transformFeedbackQueries &&
|
||||
properties.transform_feedback.transformFeedbackDraw;
|
||||
RemoveExtensionIfUnsuitable(extensions.transform_feedback,
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback,
|
||||
VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
|
||||
|
||||
// VK_EXT_vertex_input_dynamic_state
|
||||
extensions.vertex_input_dynamic_state =
|
||||
features.vertex_input_dynamic_state.vertexInputDynamicState;
|
||||
RemoveExtensionIfUnsuitable(extensions.vertex_input_dynamic_state,
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state,
|
||||
features.vertex_input_dynamic_state,
|
||||
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
|
||||
// VK_KHR_pipeline_executable_properties
|
||||
if (Settings::values.renderer_shader_feedback.GetValue()) {
|
||||
extensions.pipeline_executable_properties =
|
||||
features.pipeline_executable_properties.pipelineExecutableInfo;
|
||||
RemoveExtensionIfUnsuitable(extensions.pipeline_executable_properties,
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.pipeline_executable_properties,
|
||||
features.pipeline_executable_properties,
|
||||
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
|
||||
} else {
|
||||
extensions.pipeline_executable_properties = false;
|
||||
loaded_extensions.erase(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
|
||||
RemoveExtensionFeature(extensions.pipeline_executable_properties,
|
||||
features.pipeline_executable_properties,
|
||||
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
// VK_KHR_workgroup_memory_explicit_layout
|
||||
@ -1113,7 +1143,8 @@ void Device::RemoveUnsuitableExtensions() {
|
||||
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess &&
|
||||
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess &&
|
||||
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout;
|
||||
RemoveExtensionIfUnsuitable(extensions.workgroup_memory_explicit_layout,
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout,
|
||||
features.workgroup_memory_explicit_layout,
|
||||
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
|
@ -639,8 +639,17 @@ private:
|
||||
|
||||
// Remove extensions which have incomplete feature support.
|
||||
void RemoveUnsuitableExtensions();
|
||||
|
||||
void RemoveExtension(bool& extension, 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.
|
||||
void SetupFamilies(VkSurfaceKHR surface);
|
||||
|
||||
|
@ -214,13 +214,17 @@ void GameList::OnTextChanged(const QString& new_text) {
|
||||
const int children_count = folder->rowCount();
|
||||
for (int j = 0; j < children_count; ++j) {
|
||||
++children_total;
|
||||
|
||||
const QStandardItem* child = folder->child(j, 0);
|
||||
|
||||
const auto program_id = child->data(GameListItemPath::ProgramIdRole).toULongLong();
|
||||
|
||||
const QString file_path =
|
||||
child->data(GameListItemPath::FullPathRole).toString().toLower();
|
||||
const QString file_title =
|
||||
child->data(GameListItemPath::TitleRole).toString().toLower();
|
||||
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
|
||||
// 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_title;
|
||||
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);
|
||||
++result_count;
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user