summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt145
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt109
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt35
-rw-r--r--src/android/app/src/main/res/layout-w600dp/fragment_setup.xml60
-rw-r--r--src/android/app/src/main/res/layout-w600dp/page_setup.xml69
-rw-r--r--src/android/app/src/main/res/layout/dialog_slider.xml13
-rw-r--r--src/android/app/src/main/res/layout/fragment_setup.xml62
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting.xml62
-rw-r--r--src/android/app/src/main/res/layout/page_setup.xml30
-rw-r--r--src/android/app/src/main/res/values/strings.xml2
-rw-r--r--src/core/debugger/gdbstub.cpp17
-rw-r--r--src/core/hle/service/sockets/sockets.h2
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp4
-rw-r--r--src/core/internal_network/network.cpp43
-rw-r--r--src/core/internal_network/network.h2
-rw-r--r--src/core/internal_network/network_interface.cpp9
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp8
31 files changed, 554 insertions, 254 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2da983cad..7bb88c8ea 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -134,7 +134,7 @@ else()
134 endif() 134 endif()
135 135
136 # GCC bugs 136 # GCC bugs
137 if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 137 if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
138 # These diagnostics would be great if they worked, but are just completely broken 138 # These diagnostics would be great if they worked, but are just completely broken
139 # and produce bogus errors on external libraries like fmt. 139 # and produce bogus errors on external libraries like fmt.
140 add_compile_options( 140 add_compile_options(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
index 7006651d0..bc6ff1364 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
@@ -49,6 +49,7 @@ class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List
49 val context = YuzuApplication.appContext 49 val context = YuzuApplication.appContext
50 binding.textSettingName.text = context.getString(license.titleId) 50 binding.textSettingName.text = context.getString(license.titleId)
51 binding.textSettingDescription.text = context.getString(license.descriptionId) 51 binding.textSettingDescription.text = context.getString(license.descriptionId)
52 binding.textSettingValue.visibility = View.GONE
52 } 53 }
53 } 54 }
54} 55}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
index 481ddd5a5..6b46d359e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
@@ -5,13 +5,19 @@ package org.yuzu.yuzu_emu.adapters
5 5
6import android.text.Html 6import android.text.Html
7import android.view.LayoutInflater 7import android.view.LayoutInflater
8import android.view.View
8import android.view.ViewGroup 9import android.view.ViewGroup
9import androidx.appcompat.app.AppCompatActivity 10import androidx.appcompat.app.AppCompatActivity
10import androidx.core.content.res.ResourcesCompat 11import androidx.core.content.res.ResourcesCompat
12import androidx.lifecycle.ViewModelProvider
11import androidx.recyclerview.widget.RecyclerView 13import androidx.recyclerview.widget.RecyclerView
12import com.google.android.material.button.MaterialButton 14import com.google.android.material.button.MaterialButton
13import org.yuzu.yuzu_emu.databinding.PageSetupBinding 15import org.yuzu.yuzu_emu.databinding.PageSetupBinding
16import org.yuzu.yuzu_emu.model.HomeViewModel
17import org.yuzu.yuzu_emu.model.SetupCallback
14import org.yuzu.yuzu_emu.model.SetupPage 18import org.yuzu.yuzu_emu.model.SetupPage
19import org.yuzu.yuzu_emu.model.StepState
20import org.yuzu.yuzu_emu.utils.ViewUtils
15 21
16class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) : 22class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
17 RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() { 23 RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
@@ -26,7 +32,7 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
26 holder.bind(pages[position]) 32 holder.bind(pages[position])
27 33
28 inner class SetupPageViewHolder(val binding: PageSetupBinding) : 34 inner class SetupPageViewHolder(val binding: PageSetupBinding) :
29 RecyclerView.ViewHolder(binding.root) { 35 RecyclerView.ViewHolder(binding.root), SetupCallback {
30 lateinit var page: SetupPage 36 lateinit var page: SetupPage
31 37
32 init { 38 init {
@@ -35,6 +41,12 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
35 41
36 fun bind(page: SetupPage) { 42 fun bind(page: SetupPage) {
37 this.page = page 43 this.page = page
44
45 if (page.stepCompleted.invoke() == StepState.COMPLETE) {
46 binding.buttonAction.visibility = View.INVISIBLE
47 binding.textConfirmation.visibility = View.VISIBLE
48 }
49
38 binding.icon.setImageDrawable( 50 binding.icon.setImageDrawable(
39 ResourcesCompat.getDrawable( 51 ResourcesCompat.getDrawable(
40 activity.resources, 52 activity.resources,
@@ -62,9 +74,15 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
62 MaterialButton.ICON_GRAVITY_END 74 MaterialButton.ICON_GRAVITY_END
63 } 75 }
64 setOnClickListener { 76 setOnClickListener {
65 page.buttonAction.invoke() 77 page.buttonAction.invoke(this@SetupPageViewHolder)
66 } 78 }
67 } 79 }
68 } 80 }
81
82 override fun onStepCompleted() {
83 ViewUtils.hideView(binding.buttonAction, 200)
84 ViewUtils.showView(binding.textConfirmation, 200)
85 ViewModelProvider(activity)[HomeViewModel::class.java].setShouldPageForward(true)
86 }
69 } 87 }
70} 88}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index ce0b92c90..9711e2c51 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
@@ -207,8 +207,11 @@ class SettingsAdapter(
207 val sliderBinding = DialogSliderBinding.inflate(inflater) 207 val sliderBinding = DialogSliderBinding.inflate(inflater)
208 208
209 textSliderValue = sliderBinding.textValue 209 textSliderValue = sliderBinding.textValue
210 textSliderValue!!.text = sliderProgress.toString() 210 textSliderValue!!.text = String.format(
211 sliderBinding.textUnits.text = item.units 211 context.getString(R.string.value_with_units),
212 sliderProgress.toString(),
213 item.units
214 )
212 215
213 sliderBinding.slider.apply { 216 sliderBinding.slider.apply {
214 valueFrom = item.min.toFloat() 217 valueFrom = item.min.toFloat()
@@ -216,7 +219,11 @@ class SettingsAdapter(
216 value = sliderProgress.toFloat() 219 value = sliderProgress.toFloat()
217 addOnChangeListener { _: Slider, value: Float, _: Boolean -> 220 addOnChangeListener { _: Slider, value: Float, _: Boolean ->
218 sliderProgress = value.toInt() 221 sliderProgress = value.toInt()
219 textSliderValue!!.text = sliderProgress.toString() 222 textSliderValue!!.text = String.format(
223 context.getString(R.string.value_with_units),
224 sliderProgress.toString(),
225 item.units
226 )
220 } 227 }
221 } 228 }
222 229
@@ -225,10 +232,6 @@ class SettingsAdapter(
225 .setView(sliderBinding.root) 232 .setView(sliderBinding.root)
226 .setPositiveButton(android.R.string.ok, this) 233 .setPositiveButton(android.R.string.ok, this)
227 .setNegativeButton(android.R.string.cancel, defaultCancelListener) 234 .setNegativeButton(android.R.string.cancel, defaultCancelListener)
228 .setNeutralButton(R.string.slider_default) { dialog: DialogInterface, which: Int ->
229 sliderBinding.slider.value = item.defaultValue!!.toFloat()
230 onClick(dialog, which)
231 }
232 .show() 235 .show()
233 } 236 }
234 237
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index 7955532ee..79572fc06 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -25,12 +25,17 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
25 binding.textSettingDescription.setText(item.descriptionId) 25 binding.textSettingDescription.setText(item.descriptionId)
26 binding.textSettingDescription.visibility = View.VISIBLE 26 binding.textSettingDescription.visibility = View.VISIBLE
27 } else { 27 } else {
28 val epochTime = setting.value.toLong() 28 binding.textSettingDescription.visibility = View.GONE
29 val instant = Instant.ofEpochMilli(epochTime * 1000)
30 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
31 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
32 binding.textSettingDescription.text = dateFormatter.format(zonedTime)
33 } 29 }
30
31 binding.textSettingValue.visibility = View.VISIBLE
32 val epochTime = setting.value.toLong()
33 val instant = Instant.ofEpochMilli(epochTime * 1000)
34 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
35 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
36 binding.textSettingValue.text = dateFormatter.format(zonedTime)
37
38 setStyle(setting.isEditable, binding)
34 } 39 }
35 40
36 override fun onClick(clicked: View) { 41 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
index 5dad5945f..83a2e94f1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
@@ -23,6 +23,9 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
23 } else { 23 } else {
24 binding.textSettingDescription.visibility = View.GONE 24 binding.textSettingDescription.visibility = View.GONE
25 } 25 }
26 binding.textSettingValue.visibility = View.GONE
27
28 setStyle(setting.isEditable, binding)
26 } 29 }
27 30
28 override fun onClick(clicked: View) { 31 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt
index f56460893..0fd1d2eaa 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt
@@ -5,6 +5,8 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5 5
6import android.view.View 6import android.view.View
7import androidx.recyclerview.widget.RecyclerView 7import androidx.recyclerview.widget.RecyclerView
8import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
9import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
8import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 10import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
9import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
10 12
@@ -33,4 +35,18 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings
33 abstract override fun onClick(clicked: View) 35 abstract override fun onClick(clicked: View)
34 36
35 abstract override fun onLongClick(clicked: View): Boolean 37 abstract override fun onLongClick(clicked: View): Boolean
38
39 fun setStyle(isEditable: Boolean, binding: ListItemSettingBinding) {
40 val opacity = if (isEditable) 1.0f else 0.5f
41 binding.textSettingName.alpha = opacity
42 binding.textSettingDescription.alpha = opacity
43 binding.textSettingValue.alpha = opacity
44 }
45
46 fun setStyle(isEditable: Boolean, binding: ListItemSettingSwitchBinding) {
47 binding.switchWidget.isEnabled = isEditable
48 val opacity = if (isEditable) 1.0f else 0.5f
49 binding.textSettingName.alpha = opacity
50 binding.textSettingDescription.alpha = opacity
51 }
36} 52}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
index e4e321bd3..b42d955aa 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
@@ -17,28 +17,33 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
17 override fun bind(item: SettingsItem) { 17 override fun bind(item: SettingsItem) {
18 setting = item 18 setting = item
19 binding.textSettingName.setText(item.nameId) 19 binding.textSettingName.setText(item.nameId)
20 binding.textSettingDescription.visibility = View.VISIBLE
21 if (item.descriptionId != 0) { 20 if (item.descriptionId != 0) {
22 binding.textSettingDescription.setText(item.descriptionId) 21 binding.textSettingDescription.setText(item.descriptionId)
23 } else if (item is SingleChoiceSetting) { 22 binding.textSettingDescription.visibility = View.VISIBLE
24 val resMgr = binding.textSettingDescription.context.resources 23 } else {
24 binding.textSettingDescription.visibility = View.GONE
25 }
26
27 binding.textSettingValue.visibility = View.VISIBLE
28 if (item is SingleChoiceSetting) {
29 val resMgr = binding.textSettingValue.context.resources
25 val values = resMgr.getIntArray(item.valuesId) 30 val values = resMgr.getIntArray(item.valuesId)
26 for (i in values.indices) { 31 for (i in values.indices) {
27 if (values[i] == item.selectedValue) { 32 if (values[i] == item.selectedValue) {
28 binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i] 33 binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i]
29 return 34 break
30 } 35 }
31 } 36 }
32 } else if (item is StringSingleChoiceSetting) { 37 } else if (item is StringSingleChoiceSetting) {
33 for (i in item.values!!.indices) { 38 for (i in item.values!!.indices) {
34 if (item.values[i] == item.selectedValue) { 39 if (item.values[i] == item.selectedValue) {
35 binding.textSettingDescription.text = item.choices[i] 40 binding.textSettingValue.text = item.choices[i]
36 return 41 break
37 } 42 }
38 } 43 }
39 } else {
40 binding.textSettingDescription.visibility = View.GONE
41 } 44 }
45
46 setStyle(setting.isEditable, binding)
42 } 47 }
43 48
44 override fun onClick(clicked: View) { 49 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
index cc3f39aa5..a23b5d109 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
@@ -4,6 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.ui.viewholder 4package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5 5
6import android.view.View 6import android.view.View
7import org.yuzu.yuzu_emu.R
7import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 8import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
8import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 9import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
9import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
@@ -22,6 +23,14 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
22 } else { 23 } else {
23 binding.textSettingDescription.visibility = View.GONE 24 binding.textSettingDescription.visibility = View.GONE
24 } 25 }
26 binding.textSettingValue.visibility = View.VISIBLE
27 binding.textSettingValue.text = String.format(
28 binding.textSettingValue.context.getString(R.string.value_with_units),
29 setting.selectedValue,
30 setting.units
31 )
32
33 setStyle(setting.isEditable, binding)
25 } 34 }
26 35
27 override fun onClick(clicked: View) { 36 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
index c545b4174..1cf581a9d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
@@ -22,6 +22,7 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
22 } else { 22 } else {
23 binding.textSettingDescription.visibility = View.GONE 23 binding.textSettingDescription.visibility = View.GONE
24 } 24 }
25 binding.textSettingValue.visibility = View.GONE
25 } 26 }
26 27
27 override fun onClick(clicked: View) { 28 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
index 54f531795..ef34bf5f4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
@@ -25,12 +25,12 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
25 binding.textSettingDescription.text = "" 25 binding.textSettingDescription.text = ""
26 binding.textSettingDescription.visibility = View.GONE 26 binding.textSettingDescription.visibility = View.GONE
27 } 27 }
28 binding.switchWidget.isChecked = setting.isChecked
29 binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> 28 binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
30 adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked) 29 adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked)
31 } 30 }
31 binding.switchWidget.isChecked = setting.isChecked
32 32
33 binding.switchWidget.isEnabled = setting.isEditable 33 setStyle(setting.isEditable, binding)
34 } 34 }
35 35
36 override fun onClick(clicked: View) { 36 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index 6c4ddaf6b..d50c421a0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -19,6 +19,7 @@ import androidx.core.content.ContextCompat
19import androidx.core.view.ViewCompat 19import androidx.core.view.ViewCompat
20import androidx.core.view.WindowInsetsCompat 20import androidx.core.view.WindowInsetsCompat
21import androidx.core.view.isVisible 21import androidx.core.view.isVisible
22import androidx.core.view.updatePadding
22import androidx.fragment.app.Fragment 23import androidx.fragment.app.Fragment
23import androidx.fragment.app.activityViewModels 24import androidx.fragment.app.activityViewModels
24import androidx.navigation.findNavController 25import androidx.navigation.findNavController
@@ -32,10 +33,13 @@ import org.yuzu.yuzu_emu.adapters.SetupAdapter
32import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding 33import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding
33import org.yuzu.yuzu_emu.features.settings.model.Settings 34import org.yuzu.yuzu_emu.features.settings.model.Settings
34import org.yuzu.yuzu_emu.model.HomeViewModel 35import org.yuzu.yuzu_emu.model.HomeViewModel
36import org.yuzu.yuzu_emu.model.SetupCallback
35import org.yuzu.yuzu_emu.model.SetupPage 37import org.yuzu.yuzu_emu.model.SetupPage
38import org.yuzu.yuzu_emu.model.StepState
36import org.yuzu.yuzu_emu.ui.main.MainActivity 39import org.yuzu.yuzu_emu.ui.main.MainActivity
37import org.yuzu.yuzu_emu.utils.DirectoryInitialization 40import org.yuzu.yuzu_emu.utils.DirectoryInitialization
38import org.yuzu.yuzu_emu.utils.GameHelper 41import org.yuzu.yuzu_emu.utils.GameHelper
42import org.yuzu.yuzu_emu.utils.ViewUtils
39 43
40class SetupFragment : Fragment() { 44class SetupFragment : Fragment() {
41 private var _binding: FragmentSetupBinding? = null 45 private var _binding: FragmentSetupBinding? = null
@@ -112,14 +116,22 @@ class SetupFragment : Fragment() {
112 0, 116 0,
113 false, 117 false,
114 R.string.give_permission, 118 R.string.give_permission,
115 { permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) }, 119 {
120 notificationCallback = it
121 permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
122 },
116 true, 123 true,
117 R.string.notification_warning, 124 R.string.notification_warning,
118 R.string.notification_warning_description, 125 R.string.notification_warning_description,
119 0, 126 0,
120 { 127 {
121 NotificationManagerCompat.from(requireContext()) 128 if (NotificationManagerCompat.from(requireContext())
122 .areNotificationsEnabled() 129 .areNotificationsEnabled()
130 ) {
131 StepState.COMPLETE
132 } else {
133 StepState.INCOMPLETE
134 }
123 } 135 }
124 ) 136 )
125 ) 137 )
@@ -133,12 +145,22 @@ class SetupFragment : Fragment() {
133 R.drawable.ic_add, 145 R.drawable.ic_add,
134 true, 146 true,
135 R.string.select_keys, 147 R.string.select_keys,
136 { mainActivity.getProdKey.launch(arrayOf("*/*")) }, 148 {
149 keyCallback = it
150 getProdKey.launch(arrayOf("*/*"))
151 },
137 true, 152 true,
138 R.string.install_prod_keys_warning, 153 R.string.install_prod_keys_warning,
139 R.string.install_prod_keys_warning_description, 154 R.string.install_prod_keys_warning_description,
140 R.string.install_prod_keys_warning_help, 155 R.string.install_prod_keys_warning_help,
141 { File(DirectoryInitialization.userDirectory + "/keys/prod.keys").exists() } 156 {
157 val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
158 if (file.exists()) {
159 StepState.COMPLETE
160 } else {
161 StepState.INCOMPLETE
162 }
163 }
142 ) 164 )
143 ) 165 )
144 add( 166 add(
@@ -150,9 +172,8 @@ class SetupFragment : Fragment() {
150 true, 172 true,
151 R.string.add_games, 173 R.string.add_games,
152 { 174 {
153 mainActivity.getGamesDirectory.launch( 175 gamesDirCallback = it
154 Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data 176 getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
155 )
156 }, 177 },
157 true, 178 true,
158 R.string.add_games_warning, 179 R.string.add_games_warning,
@@ -163,7 +184,11 @@ class SetupFragment : Fragment() {
163 PreferenceManager.getDefaultSharedPreferences( 184 PreferenceManager.getDefaultSharedPreferences(
164 YuzuApplication.appContext 185 YuzuApplication.appContext
165 ) 186 )
166 preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty() 187 if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) {
188 StepState.COMPLETE
189 } else {
190 StepState.INCOMPLETE
191 }
167 } 192 }
168 ) 193 )
169 ) 194 )
@@ -181,6 +206,13 @@ class SetupFragment : Fragment() {
181 ) 206 )
182 } 207 }
183 208
209 homeViewModel.shouldPageForward.observe(viewLifecycleOwner) {
210 if (it) {
211 pageForward()
212 homeViewModel.setShouldPageForward(false)
213 }
214 }
215
184 binding.viewPager2.apply { 216 binding.viewPager2.apply {
185 adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages) 217 adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
186 offscreenPageLimit = 2 218 offscreenPageLimit = 2
@@ -194,15 +226,15 @@ class SetupFragment : Fragment() {
194 super.onPageSelected(position) 226 super.onPageSelected(position)
195 227
196 if (position == 1 && previousPosition == 0) { 228 if (position == 1 && previousPosition == 0) {
197 showView(binding.buttonNext) 229 ViewUtils.showView(binding.buttonNext)
198 showView(binding.buttonBack) 230 ViewUtils.showView(binding.buttonBack)
199 } else if (position == 0 && previousPosition == 1) { 231 } else if (position == 0 && previousPosition == 1) {
200 hideView(binding.buttonBack) 232 ViewUtils.hideView(binding.buttonBack)
201 hideView(binding.buttonNext) 233 ViewUtils.hideView(binding.buttonNext)
202 } else if (position == pages.size - 1 && previousPosition == pages.size - 2) { 234 } else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
203 hideView(binding.buttonNext) 235 ViewUtils.hideView(binding.buttonNext)
204 } else if (position == pages.size - 2 && previousPosition == pages.size - 1) { 236 } else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
205 showView(binding.buttonNext) 237 ViewUtils.showView(binding.buttonNext)
206 } 238 }
207 239
208 previousPosition = position 240 previousPosition = position
@@ -215,7 +247,8 @@ class SetupFragment : Fragment() {
215 247
216 // Checks if the user has completed the task on the current page 248 // Checks if the user has completed the task on the current page
217 if (currentPage.hasWarning) { 249 if (currentPage.hasWarning) {
218 if (currentPage.taskCompleted.invoke()) { 250 val stepState = currentPage.stepCompleted.invoke()
251 if (stepState != StepState.INCOMPLETE) {
219 pageForward() 252 pageForward()
220 return@setOnClickListener 253 return@setOnClickListener
221 } 254 }
@@ -264,9 +297,15 @@ class SetupFragment : Fragment() {
264 _binding = null 297 _binding = null
265 } 298 }
266 299
300 private lateinit var notificationCallback: SetupCallback
301
267 @RequiresApi(Build.VERSION_CODES.TIRAMISU) 302 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
268 private val permissionLauncher = 303 private val permissionLauncher =
269 registerForActivityResult(ActivityResultContracts.RequestPermission()) { 304 registerForActivityResult(ActivityResultContracts.RequestPermission()) {
305 if (it) {
306 notificationCallback.onStepCompleted()
307 }
308
270 if (!it && 309 if (!it &&
271 !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS) 310 !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
272 ) { 311 ) {
@@ -277,6 +316,27 @@ class SetupFragment : Fragment() {
277 } 316 }
278 } 317 }
279 318
319 private lateinit var keyCallback: SetupCallback
320
321 val getProdKey =
322 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
323 if (result != null) {
324 if (mainActivity.processKey(result)) {
325 keyCallback.onStepCompleted()
326 }
327 }
328 }
329
330 private lateinit var gamesDirCallback: SetupCallback
331
332 val getGamesDirectory =
333 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
334 if (result != null) {
335 mainActivity.processGamesDir(result)
336 gamesDirCallback.onStepCompleted()
337 }
338 }
339
280 private fun finishSetup() { 340 private fun finishSetup() {
281 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() 341 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
282 .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false) 342 .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
@@ -284,33 +344,6 @@ class SetupFragment : Fragment() {
284 mainActivity.finishSetup(binding.root.findNavController()) 344 mainActivity.finishSetup(binding.root.findNavController())
285 } 345 }
286 346
287 private fun showView(view: View) {
288 view.apply {
289 alpha = 0f
290 visibility = View.VISIBLE
291 isClickable = true
292 }.animate().apply {
293 duration = 300
294 alpha(1f)
295 }.start()
296 }
297
298 private fun hideView(view: View) {
299 if (view.visibility == View.INVISIBLE) {
300 return
301 }
302
303 view.apply {
304 alpha = 1f
305 isClickable = false
306 }.animate().apply {
307 duration = 300
308 alpha(0f)
309 }.withEndAction {
310 view.visibility = View.INVISIBLE
311 }
312 }
313
314 fun pageForward() { 347 fun pageForward() {
315 binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1 348 binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1
316 } 349 }
@@ -326,15 +359,29 @@ class SetupFragment : Fragment() {
326 private fun setInsets() = 359 private fun setInsets() =
327 ViewCompat.setOnApplyWindowInsetsListener( 360 ViewCompat.setOnApplyWindowInsetsListener(
328 binding.root 361 binding.root
329 ) { view: View, windowInsets: WindowInsetsCompat -> 362 ) { _: View, windowInsets: WindowInsetsCompat ->
330 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 363 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
331 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) 364 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
332 view.setPadding( 365
333 barInsets.left + cutoutInsets.left, 366 val leftPadding = barInsets.left + cutoutInsets.left
334 barInsets.top + cutoutInsets.top, 367 val topPadding = barInsets.top + cutoutInsets.top
335 barInsets.right + cutoutInsets.right, 368 val rightPadding = barInsets.right + cutoutInsets.right
336 barInsets.bottom + cutoutInsets.bottom 369 val bottomPadding = barInsets.bottom + cutoutInsets.bottom
337 ) 370
371 if (resources.getBoolean(R.bool.small_layout)) {
372 binding.viewPager2
373 .updatePadding(left = leftPadding, top = topPadding, right = rightPadding)
374 binding.constraintButtons
375 .updatePadding(left = leftPadding, right = rightPadding, bottom = bottomPadding)
376 } else {
377 binding.viewPager2.updatePadding(top = topPadding, bottom = bottomPadding)
378 binding.constraintButtons
379 .updatePadding(
380 left = leftPadding,
381 right = rightPadding,
382 bottom = bottomPadding
383 )
384 }
338 windowInsets 385 windowInsets
339 } 386 }
340} 387}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
index 263ee7144..e13d84c9c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
@@ -14,6 +14,9 @@ class HomeViewModel : ViewModel() {
14 private val _statusBarShadeVisible = MutableLiveData(true) 14 private val _statusBarShadeVisible = MutableLiveData(true)
15 val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible 15 val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible
16 16
17 private val _shouldPageForward = MutableLiveData(false)
18 val shouldPageForward: LiveData<Boolean> get() = _shouldPageForward
19
17 var navigatedToSetup = false 20 var navigatedToSetup = false
18 21
19 init { 22 init {
@@ -33,4 +36,8 @@ class HomeViewModel : ViewModel() {
33 } 36 }
34 _statusBarShadeVisible.value = visible 37 _statusBarShadeVisible.value = visible
35 } 38 }
39
40 fun setShouldPageForward(pageForward: Boolean) {
41 _shouldPageForward.value = pageForward
42 }
36} 43}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt
index a0c878e1c..09a128ae6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt
@@ -10,10 +10,20 @@ data class SetupPage(
10 val buttonIconId: Int, 10 val buttonIconId: Int,
11 val leftAlignedIcon: Boolean, 11 val leftAlignedIcon: Boolean,
12 val buttonTextId: Int, 12 val buttonTextId: Int,
13 val buttonAction: () -> Unit, 13 val buttonAction: (callback: SetupCallback) -> Unit,
14 val hasWarning: Boolean, 14 val hasWarning: Boolean,
15 val warningTitleId: Int = 0, 15 val warningTitleId: Int = 0,
16 val warningDescriptionId: Int = 0, 16 val warningDescriptionId: Int = 0,
17 val warningHelpLinkId: Int = 0, 17 val warningHelpLinkId: Int = 0,
18 val taskCompleted: () -> Boolean = { true } 18 val stepCompleted: () -> StepState = { StepState.UNDEFINED }
19) 19)
20
21interface SetupCallback {
22 fun onStepCompleted()
23}
24
25enum class StepState {
26 COMPLETE,
27 INCOMPLETE,
28 UNDEFINED
29}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index f7d7aed1e..f77d06262 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -266,73 +266,80 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
266 266
267 val getGamesDirectory = 267 val getGamesDirectory =
268 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result -> 268 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
269 if (result == null) { 269 if (result != null) {
270 return@registerForActivityResult 270 processGamesDir(result)
271 } 271 }
272 }
272 273
273 contentResolver.takePersistableUriPermission( 274 fun processGamesDir(result: Uri) {
274 result, 275 contentResolver.takePersistableUriPermission(
275 Intent.FLAG_GRANT_READ_URI_PERMISSION 276 result,
276 ) 277 Intent.FLAG_GRANT_READ_URI_PERMISSION
278 )
277 279
278 // When a new directory is picked, we currently will reset the existing games 280 // When a new directory is picked, we currently will reset the existing games
279 // database. This effectively means that only one game directory is supported. 281 // database. This effectively means that only one game directory is supported.
280 PreferenceManager.getDefaultSharedPreferences(applicationContext).edit() 282 PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
281 .putString(GameHelper.KEY_GAME_PATH, result.toString()) 283 .putString(GameHelper.KEY_GAME_PATH, result.toString())
282 .apply() 284 .apply()
283 285
284 Toast.makeText( 286 Toast.makeText(
285 applicationContext, 287 applicationContext,
286 R.string.games_dir_selected, 288 R.string.games_dir_selected,
287 Toast.LENGTH_LONG 289 Toast.LENGTH_LONG
288 ).show() 290 ).show()
289 291
290 gamesViewModel.reloadGames(true) 292 gamesViewModel.reloadGames(true)
291 } 293 }
292 294
293 val getProdKey = 295 val getProdKey =
294 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 296 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
295 if (result == null) { 297 if (result != null) {
296 return@registerForActivityResult 298 processKey(result)
297 } 299 }
300 }
298 301
299 if (FileUtil.getExtension(result) != "keys") { 302 fun processKey(result: Uri): Boolean {
300 MessageDialogFragment.newInstance( 303 if (FileUtil.getExtension(result) != "keys") {
301 R.string.reading_keys_failure, 304 MessageDialogFragment.newInstance(
302 R.string.install_prod_keys_failure_extension_description 305 R.string.reading_keys_failure,
303 ).show(supportFragmentManager, MessageDialogFragment.TAG) 306 R.string.install_prod_keys_failure_extension_description
304 return@registerForActivityResult 307 ).show(supportFragmentManager, MessageDialogFragment.TAG)
305 } 308 return false
309 }
306 310
307 contentResolver.takePersistableUriPermission( 311 contentResolver.takePersistableUriPermission(
312 result,
313 Intent.FLAG_GRANT_READ_URI_PERMISSION
314 )
315
316 val dstPath = DirectoryInitialization.userDirectory + "/keys/"
317 if (FileUtil.copyUriToInternalStorage(
318 applicationContext,
308 result, 319 result,
309 Intent.FLAG_GRANT_READ_URI_PERMISSION 320 dstPath,
321 "prod.keys"
310 ) 322 )
311 323 ) {
312 val dstPath = DirectoryInitialization.userDirectory + "/keys/" 324 if (NativeLibrary.reloadKeys()) {
313 if (FileUtil.copyUriToInternalStorage( 325 Toast.makeText(
314 applicationContext, 326 applicationContext,
315 result, 327 R.string.install_keys_success,
316 dstPath, 328 Toast.LENGTH_SHORT
317 "prod.keys" 329 ).show()
318 ) 330 gamesViewModel.reloadGames(true)
319 ) { 331 return true
320 if (NativeLibrary.reloadKeys()) { 332 } else {
321 Toast.makeText( 333 MessageDialogFragment.newInstance(
322 applicationContext, 334 R.string.invalid_keys_error,
323 R.string.install_keys_success, 335 R.string.install_keys_failure_description,
324 Toast.LENGTH_SHORT 336 R.string.dumping_keys_quickstart_link
325 ).show() 337 ).show(supportFragmentManager, MessageDialogFragment.TAG)
326 gamesViewModel.reloadGames(true) 338 return false
327 } else {
328 MessageDialogFragment.newInstance(
329 R.string.invalid_keys_error,
330 R.string.install_keys_failure_description,
331 R.string.dumping_keys_quickstart_link
332 ).show(supportFragmentManager, MessageDialogFragment.TAG)
333 }
334 } 339 }
335 } 340 }
341 return false
342 }
336 343
337 val getFirmware = 344 val getFirmware =
338 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 345 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
new file mode 100644
index 000000000..f9a3e4126
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import android.view.View
7
8object ViewUtils {
9 fun showView(view: View, length: Long = 300) {
10 view.apply {
11 alpha = 0f
12 visibility = View.VISIBLE
13 isClickable = true
14 }.animate().apply {
15 duration = length
16 alpha(1f)
17 }.start()
18 }
19
20 fun hideView(view: View, length: Long = 300) {
21 if (view.visibility == View.INVISIBLE) {
22 return
23 }
24
25 view.apply {
26 alpha = 1f
27 isClickable = false
28 }.animate().apply {
29 duration = length
30 alpha(0f)
31 }.withEndAction {
32 view.visibility = View.INVISIBLE
33 }.start()
34 }
35}
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml b/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml
index cbe631d88..406df9eab 100644
--- a/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml
+++ b/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout 2<RelativeLayout
3 xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 android:id="@+id/setup_root" 5 android:id="@+id/setup_root"
@@ -8,33 +8,39 @@
8 8
9 <androidx.viewpager2.widget.ViewPager2 9 <androidx.viewpager2.widget.ViewPager2
10 android:id="@+id/viewPager2" 10 android:id="@+id/viewPager2"
11 android:layout_width="0dp" 11 android:layout_width="match_parent"
12 android:layout_height="0dp" 12 android:layout_height="match_parent"
13 app:layout_constraintBottom_toBottomOf="parent" 13 android:layout_alignParentTop="true"
14 app:layout_constraintEnd_toEndOf="parent" 14 android:layout_alignParentBottom="true"
15 app:layout_constraintStart_toStartOf="parent" 15 android:clipToPadding="false" />
16 app:layout_constraintTop_toTopOf="parent" />
17 16
18 <com.google.android.material.button.MaterialButton 17 <androidx.constraintlayout.widget.ConstraintLayout
19 style="@style/Widget.Material3.Button.TextButton" 18 android:id="@+id/constraint_buttons"
20 android:id="@+id/button_next" 19 android:layout_width="match_parent"
21 android:layout_width="wrap_content"
22 android:layout_height="wrap_content" 20 android:layout_height="wrap_content"
23 android:layout_margin="16dp" 21 android:layout_alignParentBottom="true"
24 android:text="@string/next" 22 android:layout_margin="8dp">
25 android:visibility="invisible"
26 app:layout_constraintBottom_toBottomOf="parent"
27 app:layout_constraintEnd_toEndOf="parent" />
28 23
29 <com.google.android.material.button.MaterialButton 24 <com.google.android.material.button.MaterialButton
30 android:id="@+id/button_back" 25 android:id="@+id/button_next"
31 style="@style/Widget.Material3.Button.TextButton" 26 style="@style/Widget.Material3.Button.TextButton"
32 android:layout_width="wrap_content" 27 android:layout_width="wrap_content"
33 android:layout_height="wrap_content" 28 android:layout_height="wrap_content"
34 android:layout_margin="16dp" 29 android:text="@string/next"
35 android:text="@string/back" 30 android:visibility="invisible"
36 android:visibility="invisible" 31 app:layout_constraintBottom_toBottomOf="parent"
37 app:layout_constraintBottom_toBottomOf="parent" 32 app:layout_constraintEnd_toEndOf="parent" />
38 app:layout_constraintStart_toStartOf="parent" /> 33
34 <com.google.android.material.button.MaterialButton
35 android:id="@+id/button_back"
36 style="@style/Widget.Material3.Button.TextButton"
37 android:layout_width="wrap_content"
38 android:layout_height="wrap_content"
39 android:text="@string/back"
40 android:visibility="invisible"
41 app:layout_constraintBottom_toBottomOf="parent"
42 app:layout_constraintStart_toStartOf="parent" />
43
44 </androidx.constraintlayout.widget.ConstraintLayout>
39 45
40</androidx.constraintlayout.widget.ConstraintLayout> 46</RelativeLayout>
diff --git a/src/android/app/src/main/res/layout-w600dp/page_setup.xml b/src/android/app/src/main/res/layout-w600dp/page_setup.xml
index e1c26b2f8..9e0ab8ecb 100644
--- a/src/android/app/src/main/res/layout-w600dp/page_setup.xml
+++ b/src/android/app/src/main/res/layout-w600dp/page_setup.xml
@@ -21,45 +21,76 @@
21 21
22 </LinearLayout> 22 </LinearLayout>
23 23
24 <LinearLayout 24 <androidx.constraintlayout.widget.ConstraintLayout
25 android:layout_width="match_parent" 25 android:layout_width="match_parent"
26 android:layout_height="match_parent" 26 android:layout_height="match_parent"
27 android:layout_weight="1" 27 android:layout_weight="1">
28 android:orientation="vertical"
29 android:gravity="center">
30 28
31 <com.google.android.material.textview.MaterialTextView 29 <com.google.android.material.textview.MaterialTextView
32 style="@style/TextAppearance.Material3.DisplaySmall"
33 android:id="@+id/text_title" 30 android:id="@+id/text_title"
34 android:layout_width="match_parent" 31 style="@style/TextAppearance.Material3.DisplaySmall"
35 android:layout_height="wrap_content" 32 android:layout_width="0dp"
36 android:textAlignment="center" 33 android:layout_height="0dp"
34 android:gravity="center"
37 android:textColor="?attr/colorOnSurface" 35 android:textColor="?attr/colorOnSurface"
38 android:textStyle="bold" 36 android:textStyle="bold"
37 app:layout_constraintBottom_toTopOf="@+id/text_description"
38 app:layout_constraintEnd_toEndOf="parent"
39 app:layout_constraintStart_toStartOf="parent"
40 app:layout_constraintTop_toTopOf="parent"
41 app:layout_constraintVertical_weight="2"
39 tools:text="@string/welcome" /> 42 tools:text="@string/welcome" />
40 43
41 <com.google.android.material.textview.MaterialTextView 44 <com.google.android.material.textview.MaterialTextView
42 style="@style/TextAppearance.Material3.TitleLarge"
43 android:id="@+id/text_description" 45 android:id="@+id/text_description"
44 android:layout_width="match_parent" 46 style="@style/TextAppearance.Material3.TitleLarge"
45 android:layout_height="wrap_content" 47 android:layout_width="0dp"
46 android:layout_marginTop="16dp" 48 android:layout_height="0dp"
47 android:paddingHorizontal="32dp" 49 android:gravity="center"
48 android:textAlignment="center" 50 android:textSize="20sp"
49 android:textSize="26sp" 51 android:paddingHorizontal="16dp"
50 app:lineHeight="40sp" 52 app:layout_constraintBottom_toTopOf="@+id/button_action"
53 app:layout_constraintEnd_toEndOf="parent"
54 app:layout_constraintStart_toStartOf="parent"
55 app:layout_constraintTop_toBottomOf="@+id/text_title"
56 app:layout_constraintVertical_weight="2"
57 app:lineHeight="30sp"
51 tools:text="@string/welcome_description" /> 58 tools:text="@string/welcome_description" />
52 59
60 <com.google.android.material.textview.MaterialTextView
61 android:id="@+id/text_confirmation"
62 style="@style/TextAppearance.Material3.TitleLarge"
63 android:layout_width="0dp"
64 android:layout_height="0dp"
65 android:paddingHorizontal="16dp"
66 android:paddingBottom="20dp"
67 android:gravity="center"
68 android:textSize="30sp"
69 android:visibility="invisible"
70 android:text="@string/step_complete"
71 android:textStyle="bold"
72 app:layout_constraintBottom_toBottomOf="parent"
73 app:layout_constraintEnd_toEndOf="parent"
74 app:layout_constraintStart_toStartOf="parent"
75 app:layout_constraintTop_toBottomOf="@+id/text_description"
76 app:layout_constraintVertical_weight="1"
77 app:lineHeight="30sp" />
78
53 <com.google.android.material.button.MaterialButton 79 <com.google.android.material.button.MaterialButton
54 android:id="@+id/button_action" 80 android:id="@+id/button_action"
55 android:layout_width="wrap_content" 81 android:layout_width="wrap_content"
56 android:layout_height="56dp" 82 android:layout_height="56dp"
57 android:layout_marginTop="32dp" 83 android:layout_marginTop="16dp"
84 android:layout_marginBottom="48dp"
58 android:textSize="20sp" 85 android:textSize="20sp"
59 app:iconSize="24sp"
60 app:iconGravity="end" 86 app:iconGravity="end"
87 app:iconSize="24sp"
88 app:layout_constraintBottom_toBottomOf="parent"
89 app:layout_constraintEnd_toEndOf="parent"
90 app:layout_constraintStart_toStartOf="parent"
91 app:layout_constraintTop_toBottomOf="@+id/text_description"
61 tools:text="Get started" /> 92 tools:text="Get started" />
62 93
63 </LinearLayout> 94 </androidx.constraintlayout.widget.ConstraintLayout>
64 95
65</LinearLayout> 96</LinearLayout>
diff --git a/src/android/app/src/main/res/layout/dialog_slider.xml b/src/android/app/src/main/res/layout/dialog_slider.xml
index 8c84cb606..d1cb31739 100644
--- a/src/android/app/src/main/res/layout/dialog_slider.xml
+++ b/src/android/app/src/main/res/layout/dialog_slider.xml
@@ -5,23 +5,16 @@
5 android:layout_height="wrap_content" 5 android:layout_height="wrap_content"
6 android:orientation="vertical"> 6 android:orientation="vertical">
7 7
8 <TextView 8 <com.google.android.material.textview.MaterialTextView
9 android:id="@+id/text_value" 9 android:id="@+id/text_value"
10 style="@style/TextAppearance.Material3.LabelMedium"
10 android:layout_width="wrap_content" 11 android:layout_width="wrap_content"
11 android:layout_height="wrap_content" 12 android:layout_height="wrap_content"
12 android:layout_alignParentTop="true" 13 android:layout_alignParentTop="true"
13 android:layout_centerHorizontal="true" 14 android:layout_centerHorizontal="true"
14 android:layout_marginBottom="@dimen/spacing_medlarge" 15 android:layout_marginBottom="@dimen/spacing_medlarge"
15 android:layout_marginTop="@dimen/spacing_medlarge" 16 android:layout_marginTop="@dimen/spacing_medlarge"
16 tools:text="75" /> 17 tools:text="75%" />
17
18 <TextView
19 android:id="@+id/text_units"
20 android:layout_width="wrap_content"
21 android:layout_height="wrap_content"
22 android:layout_alignTop="@+id/text_value"
23 android:layout_toEndOf="@+id/text_value"
24 tools:text="%" />
25 18
26 <com.google.android.material.slider.Slider 19 <com.google.android.material.slider.Slider
27 android:id="@+id/slider" 20 android:id="@+id/slider"
diff --git a/src/android/app/src/main/res/layout/fragment_setup.xml b/src/android/app/src/main/res/layout/fragment_setup.xml
index d7bafaea2..9499f6463 100644
--- a/src/android/app/src/main/res/layout/fragment_setup.xml
+++ b/src/android/app/src/main/res/layout/fragment_setup.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout 2<RelativeLayout
3 xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 android:id="@+id/setup_root" 5 android:id="@+id/setup_root"
@@ -8,35 +8,39 @@
8 8
9 <androidx.viewpager2.widget.ViewPager2 9 <androidx.viewpager2.widget.ViewPager2
10 android:id="@+id/viewPager2" 10 android:id="@+id/viewPager2"
11 android:layout_width="0dp" 11 android:layout_width="match_parent"
12 android:layout_height="0dp"
13 android:clipToPadding="false"
14 android:layout_marginBottom="16dp"
15 app:layout_constraintBottom_toTopOf="@+id/button_next"
16 app:layout_constraintEnd_toEndOf="parent"
17 app:layout_constraintStart_toStartOf="parent"
18 app:layout_constraintTop_toTopOf="parent" />
19
20 <com.google.android.material.button.MaterialButton
21 style="@style/Widget.Material3.Button.TextButton"
22 android:id="@+id/button_next"
23 android:layout_width="wrap_content"
24 android:layout_height="wrap_content" 12 android:layout_height="wrap_content"
25 android:layout_margin="12dp" 13 android:layout_above="@+id/constraint_buttons"
26 android:text="@string/next" 14 android:layout_alignParentTop="true"
27 android:visibility="invisible" 15 android:clipToPadding="false" />
28 app:layout_constraintBottom_toBottomOf="parent"
29 app:layout_constraintEnd_toEndOf="parent" />
30 16
31 <com.google.android.material.button.MaterialButton 17 <androidx.constraintlayout.widget.ConstraintLayout
32 style="@style/Widget.Material3.Button.TextButton" 18 android:id="@+id/constraint_buttons"
33 android:id="@+id/button_back" 19 android:layout_width="match_parent"
34 android:layout_width="wrap_content"
35 android:layout_height="wrap_content" 20 android:layout_height="wrap_content"
36 android:layout_margin="12dp" 21 android:layout_margin="8dp"
37 android:text="@string/back" 22 android:layout_alignParentBottom="true">
38 android:visibility="invisible" 23
39 app:layout_constraintBottom_toBottomOf="parent" 24 <com.google.android.material.button.MaterialButton
40 app:layout_constraintStart_toStartOf="parent" /> 25 android:id="@+id/button_next"
26 style="@style/Widget.Material3.Button.TextButton"
27 android:layout_width="wrap_content"
28 android:layout_height="wrap_content"
29 android:text="@string/next"
30 android:visibility="invisible"
31 app:layout_constraintBottom_toBottomOf="parent"
32 app:layout_constraintEnd_toEndOf="parent" />
33
34 <com.google.android.material.button.MaterialButton
35 android:id="@+id/button_back"
36 style="@style/Widget.Material3.Button.TextButton"
37 android:layout_width="wrap_content"
38 android:layout_height="wrap_content"
39 android:text="@string/back"
40 android:visibility="invisible"
41 app:layout_constraintBottom_toBottomOf="parent"
42 app:layout_constraintStart_toStartOf="parent" />
43
44 </androidx.constraintlayout.widget.ConstraintLayout>
41 45
42</androidx.constraintlayout.widget.ConstraintLayout> 46</RelativeLayout>
diff --git a/src/android/app/src/main/res/layout/list_item_setting.xml b/src/android/app/src/main/res/layout/list_item_setting.xml
index ec896342b..f1037a740 100644
--- a/src/android/app/src/main/res/layout/list_item_setting.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting.xml
@@ -1,9 +1,10 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2<RelativeLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto"
3 xmlns:tools="http://schemas.android.com/tools" 5 xmlns:tools="http://schemas.android.com/tools"
4 android:layout_width="match_parent" 6 android:layout_width="match_parent"
5 android:layout_height="wrap_content" 7 android:layout_height="wrap_content"
6 xmlns:app="http://schemas.android.com/apk/res-auto"
7 android:background="?android:attr/selectableItemBackground" 8 android:background="?android:attr/selectableItemBackground"
8 android:clickable="true" 9 android:clickable="true"
9 android:focusable="true" 10 android:focusable="true"
@@ -11,31 +12,40 @@
11 android:minHeight="72dp" 12 android:minHeight="72dp"
12 android:padding="@dimen/spacing_large"> 13 android:padding="@dimen/spacing_large">
13 14
14 <com.google.android.material.textview.MaterialTextView 15 <LinearLayout
15 style="@style/TextAppearance.Material3.HeadlineMedium" 16 android:layout_width="match_parent"
16 android:id="@+id/text_setting_name"
17 android:layout_width="0dp"
18 android:layout_height="wrap_content" 17 android:layout_height="wrap_content"
19 android:layout_alignParentEnd="true" 18 android:orientation="vertical">
20 android:layout_alignParentStart="true"
21 android:layout_alignParentTop="true"
22 android:textSize="16sp"
23 android:textAlignment="viewStart"
24 app:lineHeight="28dp"
25 tools:text="Setting Name" />
26 19
27 <TextView 20 <com.google.android.material.textview.MaterialTextView
28 style="@style/TextAppearance.Material3.BodySmall" 21 android:id="@+id/text_setting_name"
29 android:id="@+id/text_setting_description" 22 style="@style/TextAppearance.Material3.HeadlineMedium"
30 android:layout_width="wrap_content" 23 android:layout_width="match_parent"
31 android:layout_height="wrap_content" 24 android:layout_height="wrap_content"
32 android:layout_alignParentEnd="true" 25 android:textAlignment="viewStart"
33 android:layout_alignParentStart="true" 26 android:textSize="16sp"
34 android:layout_alignStart="@+id/text_setting_name" 27 app:lineHeight="22dp"
35 android:layout_below="@+id/text_setting_name" 28 tools:text="Setting Name" />
36 android:layout_marginTop="@dimen/spacing_small" 29
37 android:visibility="visible" 30 <com.google.android.material.textview.MaterialTextView
38 android:textAlignment="viewStart" 31 android:id="@+id/text_setting_description"
39 tools:text="@string/app_disclaimer" /> 32 style="@style/TextAppearance.Material3.BodySmall"
33 android:layout_width="match_parent"
34 android:layout_height="wrap_content"
35 android:layout_marginTop="@dimen/spacing_small"
36 android:textAlignment="viewStart"
37 tools:text="@string/app_disclaimer" />
38
39 <com.google.android.material.textview.MaterialTextView
40 android:id="@+id/text_setting_value"
41 style="@style/TextAppearance.Material3.LabelMedium"
42 android:layout_width="match_parent"
43 android:layout_height="wrap_content"
44 android:layout_marginTop="@dimen/spacing_small"
45 android:textAlignment="viewStart"
46 android:textStyle="bold"
47 tools:text="1x" />
48
49 </LinearLayout>
40 50
41</RelativeLayout> 51</RelativeLayout>
diff --git a/src/android/app/src/main/res/layout/page_setup.xml b/src/android/app/src/main/res/layout/page_setup.xml
index 1436ef308..535abcf02 100644
--- a/src/android/app/src/main/res/layout/page_setup.xml
+++ b/src/android/app/src/main/res/layout/page_setup.xml
@@ -21,11 +21,12 @@
21 app:layout_constraintVertical_chainStyle="spread" 21 app:layout_constraintVertical_chainStyle="spread"
22 app:layout_constraintWidth_max="220dp" 22 app:layout_constraintWidth_max="220dp"
23 app:layout_constraintWidth_min="110dp" 23 app:layout_constraintWidth_min="110dp"
24 app:layout_constraintVertical_weight="3" /> 24 app:layout_constraintVertical_weight="3"
25 tools:src="@drawable/ic_notification" />
25 26
26 <com.google.android.material.textview.MaterialTextView 27 <com.google.android.material.textview.MaterialTextView
27 android:id="@+id/text_title" 28 android:id="@+id/text_title"
28 style="@style/TextAppearance.Material3.DisplayMedium" 29 style="@style/TextAppearance.Material3.DisplaySmall"
29 android:layout_width="0dp" 30 android:layout_width="0dp"
30 android:layout_height="0dp" 31 android:layout_height="0dp"
31 android:textAlignment="center" 32 android:textAlignment="center"
@@ -44,23 +45,42 @@
44 android:layout_width="0dp" 45 android:layout_width="0dp"
45 android:layout_height="0dp" 46 android:layout_height="0dp"
46 android:textAlignment="center" 47 android:textAlignment="center"
47 android:textSize="26sp" 48 android:textSize="20sp"
48 android:paddingHorizontal="16dp" 49 android:paddingHorizontal="16dp"
49 app:layout_constraintBottom_toTopOf="@+id/button_action" 50 app:layout_constraintBottom_toTopOf="@+id/button_action"
50 app:layout_constraintEnd_toEndOf="parent" 51 app:layout_constraintEnd_toEndOf="parent"
51 app:layout_constraintStart_toStartOf="parent" 52 app:layout_constraintStart_toStartOf="parent"
52 app:layout_constraintTop_toBottomOf="@+id/text_title" 53 app:layout_constraintTop_toBottomOf="@+id/text_title"
53 app:layout_constraintVertical_weight="2" 54 app:layout_constraintVertical_weight="2"
54 app:lineHeight="40sp" 55 app:lineHeight="30sp"
55 tools:text="@string/welcome_description" /> 56 tools:text="@string/welcome_description" />
56 57
58 <com.google.android.material.textview.MaterialTextView
59 android:id="@+id/text_confirmation"
60 style="@style/TextAppearance.Material3.TitleLarge"
61 android:layout_width="wrap_content"
62 android:layout_height="0dp"
63 android:paddingHorizontal="16dp"
64 android:paddingTop="24dp"
65 android:textAlignment="center"
66 android:textSize="30sp"
67 android:visibility="invisible"
68 android:text="@string/step_complete"
69 android:textStyle="bold"
70 app:layout_constraintBottom_toBottomOf="parent"
71 app:layout_constraintEnd_toEndOf="parent"
72 app:layout_constraintStart_toStartOf="parent"
73 app:layout_constraintTop_toBottomOf="@+id/text_description"
74 app:layout_constraintVertical_weight="1"
75 app:lineHeight="30sp" />
76
57 <com.google.android.material.button.MaterialButton 77 <com.google.android.material.button.MaterialButton
58 android:id="@+id/button_action" 78 android:id="@+id/button_action"
59 android:layout_width="wrap_content" 79 android:layout_width="wrap_content"
60 android:layout_height="56dp" 80 android:layout_height="56dp"
61 android:textSize="20sp"
62 android:layout_marginTop="16dp" 81 android:layout_marginTop="16dp"
63 android:layout_marginBottom="48dp" 82 android:layout_marginBottom="48dp"
83 android:textSize="20sp"
64 app:iconGravity="end" 84 app:iconGravity="end"
65 app:iconSize="24sp" 85 app:iconSize="24sp"
66 app:layout_constraintBottom_toBottomOf="parent" 86 app:layout_constraintBottom_toBottomOf="parent"
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 02e25504d..de1b2909b 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -29,6 +29,7 @@
29 <string name="back">Back</string> 29 <string name="back">Back</string>
30 <string name="add_games">Add Games</string> 30 <string name="add_games">Add Games</string>
31 <string name="add_games_description">Select your games folder</string> 31 <string name="add_games_description">Select your games folder</string>
32 <string name="step_complete">Complete!</string>
32 33
33 <!-- Home strings --> 34 <!-- Home strings -->
34 <string name="home_games">Games</string> 35 <string name="home_games">Games</string>
@@ -149,6 +150,7 @@
149 <string name="frame_limit_slider">Limit speed percent</string> 150 <string name="frame_limit_slider">Limit speed percent</string>
150 <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> 151 <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>
151 <string name="cpu_accuracy">CPU accuracy</string> 152 <string name="cpu_accuracy">CPU accuracy</string>
153 <string name="value_with_units">%1$s%2$s</string>
152 154
153 <!-- System settings strings --> 155 <!-- System settings strings -->
154 <string name="use_docked_mode">Docked Mode</string> 156 <string name="use_docked_mode">Docked Mode</string>
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 0f839d5b4..e55831f27 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -263,6 +263,23 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
263 263
264 std::vector<u8> mem(size); 264 std::vector<u8> mem(size);
265 if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) { 265 if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) {
266 // Restore any bytes belonging to replaced instructions.
267 auto it = replaced_instructions.lower_bound(addr);
268 for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
269 // Get the bytes of the instruction we previously replaced.
270 const u32 original_bytes = it->second;
271
272 // Calculate where to start writing to the output buffer.
273 const size_t output_offset = it->first - addr;
274
275 // Calculate how many bytes to write.
276 // The loop condition ensures output_offset < size.
277 const size_t n = std::min<size_t>(size - output_offset, sizeof(u32));
278
279 // Write the bytes to the output buffer.
280 std::memcpy(mem.data() + output_offset, &original_bytes, n);
281 }
282
266 SendReply(Common::HexToString(mem)); 283 SendReply(Common::HexToString(mem));
267 } else { 284 } else {
268 SendReply(GDB_STUB_REPLY_ERR); 285 SendReply(GDB_STUB_REPLY_ERR);
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index 77426c46e..f86af01a4 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -18,7 +18,9 @@ enum class Errno : u32 {
18 AGAIN = 11, 18 AGAIN = 11,
19 INVAL = 22, 19 INVAL = 22,
20 MFILE = 24, 20 MFILE = 24,
21 PIPE = 32,
21 MSGSIZE = 90, 22 MSGSIZE = 90,
23 CONNABORTED = 103,
22 CONNRESET = 104, 24 CONNRESET = 104,
23 NOTCONN = 107, 25 NOTCONN = 107,
24 TIMEDOUT = 110, 26 TIMEDOUT = 110,
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
index c1187209f..aed05250c 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -23,10 +23,14 @@ Errno Translate(Network::Errno value) {
23 return Errno::INVAL; 23 return Errno::INVAL;
24 case Network::Errno::MFILE: 24 case Network::Errno::MFILE:
25 return Errno::MFILE; 25 return Errno::MFILE;
26 case Network::Errno::PIPE:
27 return Errno::PIPE;
26 case Network::Errno::NOTCONN: 28 case Network::Errno::NOTCONN:
27 return Errno::NOTCONN; 29 return Errno::NOTCONN;
28 case Network::Errno::TIMEDOUT: 30 case Network::Errno::TIMEDOUT:
29 return Errno::TIMEDOUT; 31 return Errno::TIMEDOUT;
32 case Network::Errno::CONNABORTED:
33 return Errno::CONNABORTED;
30 case Network::Errno::CONNRESET: 34 case Network::Errno::CONNRESET:
31 return Errno::CONNRESET; 35 return Errno::CONNRESET;
32 case Network::Errno::INPROGRESS: 36 case Network::Errno::INPROGRESS:
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 28f89c599..5d28300e6 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -39,6 +39,11 @@ namespace Network {
39 39
40namespace { 40namespace {
41 41
42enum class CallType {
43 Send,
44 Other,
45};
46
42#ifdef _WIN32 47#ifdef _WIN32
43 48
44using socklen_t = int; 49using socklen_t = int;
@@ -96,7 +101,7 @@ bool EnableNonBlock(SOCKET fd, bool enable) {
96 return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; 101 return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
97} 102}
98 103
99Errno TranslateNativeError(int e) { 104Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
100 switch (e) { 105 switch (e) {
101 case 0: 106 case 0:
102 return Errno::SUCCESS; 107 return Errno::SUCCESS;
@@ -112,6 +117,14 @@ Errno TranslateNativeError(int e) {
112 return Errno::AGAIN; 117 return Errno::AGAIN;
113 case WSAECONNREFUSED: 118 case WSAECONNREFUSED:
114 return Errno::CONNREFUSED; 119 return Errno::CONNREFUSED;
120 case WSAECONNABORTED:
121 if (call_type == CallType::Send) {
122 // Winsock yields WSAECONNABORTED from `send` in situations where Unix
123 // systems, and actual Switches, yield EPIPE.
124 return Errno::PIPE;
125 } else {
126 return Errno::CONNABORTED;
127 }
115 case WSAECONNRESET: 128 case WSAECONNRESET:
116 return Errno::CONNRESET; 129 return Errno::CONNRESET;
117 case WSAEHOSTUNREACH: 130 case WSAEHOSTUNREACH:
@@ -198,7 +211,7 @@ bool EnableNonBlock(int fd, bool enable) {
198 return fcntl(fd, F_SETFL, flags) == 0; 211 return fcntl(fd, F_SETFL, flags) == 0;
199} 212}
200 213
201Errno TranslateNativeError(int e) { 214Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
202 switch (e) { 215 switch (e) {
203 case 0: 216 case 0:
204 return Errno::SUCCESS; 217 return Errno::SUCCESS;
@@ -208,6 +221,10 @@ Errno TranslateNativeError(int e) {
208 return Errno::INVAL; 221 return Errno::INVAL;
209 case EMFILE: 222 case EMFILE:
210 return Errno::MFILE; 223 return Errno::MFILE;
224 case EPIPE:
225 return Errno::PIPE;
226 case ECONNABORTED:
227 return Errno::CONNABORTED;
211 case ENOTCONN: 228 case ENOTCONN:
212 return Errno::NOTCONN; 229 return Errno::NOTCONN;
213 case EAGAIN: 230 case EAGAIN:
@@ -236,13 +253,13 @@ Errno TranslateNativeError(int e) {
236 253
237#endif 254#endif
238 255
239Errno GetAndLogLastError() { 256Errno GetAndLogLastError(CallType call_type = CallType::Other) {
240#ifdef _WIN32 257#ifdef _WIN32
241 int e = WSAGetLastError(); 258 int e = WSAGetLastError();
242#else 259#else
243 int e = errno; 260 int e = errno;
244#endif 261#endif
245 const Errno err = TranslateNativeError(e); 262 const Errno err = TranslateNativeError(e, call_type);
246 if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) { 263 if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) {
247 // These happen during normal operation, so only log them at debug level. 264 // These happen during normal operation, so only log them at debug level.
248 LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); 265 LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
@@ -476,7 +493,13 @@ NetworkInstance::~NetworkInstance() {
476std::optional<IPv4Address> GetHostIPv4Address() { 493std::optional<IPv4Address> GetHostIPv4Address() {
477 const auto network_interface = Network::GetSelectedNetworkInterface(); 494 const auto network_interface = Network::GetSelectedNetworkInterface();
478 if (!network_interface.has_value()) { 495 if (!network_interface.has_value()) {
479 LOG_DEBUG(Network, "GetSelectedNetworkInterface returned no interface"); 496 // Only print the error once to avoid log spam
497 static bool print_error = true;
498 if (print_error) {
499 LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
500 print_error = false;
501 }
502
480 return {}; 503 return {};
481 } 504 }
482 505
@@ -725,13 +748,17 @@ std::pair<s32, Errno> Socket::Send(std::span<const u8> message, int flags) {
725 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); 748 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
726 ASSERT(flags == 0); 749 ASSERT(flags == 0);
727 750
751 int native_flags = 0;
752#if YUZU_UNIX
753 native_flags |= MSG_NOSIGNAL; // do not send us SIGPIPE
754#endif
728 const auto result = send(fd, reinterpret_cast<const char*>(message.data()), 755 const auto result = send(fd, reinterpret_cast<const char*>(message.data()),
729 static_cast<int>(message.size()), 0); 756 static_cast<int>(message.size()), native_flags);
730 if (result != SOCKET_ERROR) { 757 if (result != SOCKET_ERROR) {
731 return {static_cast<s32>(result), Errno::SUCCESS}; 758 return {static_cast<s32>(result), Errno::SUCCESS};
732 } 759 }
733 760
734 return {-1, GetAndLogLastError()}; 761 return {-1, GetAndLogLastError(CallType::Send)};
735} 762}
736 763
737std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message, 764std::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,
753 return {static_cast<s32>(result), Errno::SUCCESS}; 780 return {static_cast<s32>(result), Errno::SUCCESS};
754 } 781 }
755 782
756 return {-1, GetAndLogLastError()}; 783 return {-1, GetAndLogLastError(CallType::Send)};
757} 784}
758 785
759Errno Socket::Close() { 786Errno Socket::Close() {
diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h
index badcb8369..c7e20ae34 100644
--- a/src/core/internal_network/network.h
+++ b/src/core/internal_network/network.h
@@ -33,10 +33,12 @@ enum class Errno {
33 BADF, 33 BADF,
34 INVAL, 34 INVAL,
35 MFILE, 35 MFILE,
36 PIPE,
36 NOTCONN, 37 NOTCONN,
37 AGAIN, 38 AGAIN,
38 CONNREFUSED, 39 CONNREFUSED,
39 CONNRESET, 40 CONNRESET,
41 CONNABORTED,
40 HOSTUNREACH, 42 HOSTUNREACH,
41 NETDOWN, 43 NETDOWN,
42 NETUNREACH, 44 NETUNREACH,
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 4c909a6d3..7c37f660b 100644
--- a/src/core/internal_network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -200,7 +200,14 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
200 }); 200 });
201 201
202 if (res == network_interfaces.end()) { 202 if (res == network_interfaces.end()) {
203 LOG_DEBUG(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); 203 // Only print the error once to avoid log spam
204 static bool print_error = true;
205 if (print_error) {
206 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"",
207 selected_network_interface);
208 print_error = false;
209 }
210
204 return std::nullopt; 211 return std::nullopt;
205 } 212 }
206 213
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index a8540339d..35bf80ea3 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -126,7 +126,7 @@ struct FormatTuple {
126 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1R5G5B5_UNORM 126 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1R5G5B5_UNORM
127 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM 127 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM
128 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT 128 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT
129 {VK_FORMAT_A2R10G10B10_UNORM_PACK32, Attachable | Storage}, // A2R10G10B10_UNORM 129 {VK_FORMAT_A2R10G10B10_UNORM_PACK32, Attachable}, // A2R10G10B10_UNORM
130 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle) 130 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle)
131 {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled) 131 {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled)
132 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM 132 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index adde96aa5..617417040 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -71,6 +71,11 @@ constexpr std::array R8G8B8_SSCALED{
71 VK_FORMAT_UNDEFINED, 71 VK_FORMAT_UNDEFINED,
72}; 72};
73 73
74constexpr std::array VK_FORMAT_R32G32B32_SFLOAT{
75 VK_FORMAT_R32G32B32A32_SFLOAT,
76 VK_FORMAT_UNDEFINED,
77};
78
74} // namespace Alternatives 79} // namespace Alternatives
75 80
76enum class NvidiaArchitecture { 81enum class NvidiaArchitecture {
@@ -103,6 +108,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
103 return Alternatives::R16G16B16_SSCALED.data(); 108 return Alternatives::R16G16B16_SSCALED.data();
104 case VK_FORMAT_R8G8B8_SSCALED: 109 case VK_FORMAT_R8G8B8_SSCALED:
105 return Alternatives::R8G8B8_SSCALED.data(); 110 return Alternatives::R8G8B8_SSCALED.data();
111 case VK_FORMAT_R32G32B32_SFLOAT:
112 return Alternatives::VK_FORMAT_R32G32B32_SFLOAT.data();
106 default: 113 default:
107 return nullptr; 114 return nullptr;
108 } 115 }
@@ -130,6 +137,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
130 VK_FORMAT_A2B10G10R10_UINT_PACK32, 137 VK_FORMAT_A2B10G10R10_UINT_PACK32,
131 VK_FORMAT_A2B10G10R10_UNORM_PACK32, 138 VK_FORMAT_A2B10G10R10_UNORM_PACK32,
132 VK_FORMAT_A2B10G10R10_USCALED_PACK32, 139 VK_FORMAT_A2B10G10R10_USCALED_PACK32,
140 VK_FORMAT_A2R10G10B10_UNORM_PACK32,
133 VK_FORMAT_A8B8G8R8_SINT_PACK32, 141 VK_FORMAT_A8B8G8R8_SINT_PACK32,
134 VK_FORMAT_A8B8G8R8_SNORM_PACK32, 142 VK_FORMAT_A8B8G8R8_SNORM_PACK32,
135 VK_FORMAT_A8B8G8R8_SRGB_PACK32, 143 VK_FORMAT_A8B8G8R8_SRGB_PACK32,