summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt124
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt2
-rw-r--r--src/android/app/src/main/jni/android_settings.h7
-rw-r--r--src/android/app/src/main/res/values/arrays.xml11
-rw-r--r--src/android/app/src/main/res/values/strings.xml8
-rw-r--r--src/core/CMakeLists.txt12
-rw-r--r--src/core/hle/service/ldn/lan_discovery.cpp20
-rw-r--r--src/core/hle/service/ldn/lan_discovery.h5
-rw-r--r--src/core/hle/service/ldn/ldn.cpp802
-rw-r--r--src/core/hle/service/ldn/ldn.h6
-rw-r--r--src/core/hle/service/ldn/ldn_types.h68
-rw-r--r--src/core/hle/service/ldn/monitor_service.cpp43
-rw-r--r--src/core/hle/service/ldn/monitor_service.h28
-rw-r--r--src/core/hle/service/ldn/sf_monitor_service.cpp40
-rw-r--r--src/core/hle/service/ldn/sf_monitor_service.h26
-rw-r--r--src/core/hle/service/ldn/sf_service.cpp37
-rw-r--r--src/core/hle/service/ldn/sf_service.h21
-rw-r--r--src/core/hle/service/ldn/sf_service_monitor.cpp50
-rw-r--r--src/core/hle/service/ldn/sf_service_monitor.h26
-rw-r--r--src/core/hle/service/ldn/system_local_communication_service.cpp56
-rw-r--r--src/core/hle/service/ldn/system_local_communication_service.h25
-rw-r--r--src/core/hle/service/ldn/user_local_communication_service.cpp320
-rw-r--r--src/core/hle/service/ldn/user_local_communication_service.h103
-rw-r--r--src/video_core/texture_cache/image_info.cpp1
-rw-r--r--src/video_core/texture_cache/image_info.h1
-rw-r--r--src/video_core/texture_cache/texture_cache.h15
-rw-r--r--src/yuzu/configuration/shared_translation.cpp158
-rw-r--r--src/yuzu/hotkeys.cpp6
35 files changed, 1257 insertions, 857 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index 71be2d0b2..0165cb2d1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -24,7 +24,9 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
24 THEME_MODE("theme_mode"), 24 THEME_MODE("theme_mode"),
25 OVERLAY_SCALE("control_scale"), 25 OVERLAY_SCALE("control_scale"),
26 OVERLAY_OPACITY("control_opacity"), 26 OVERLAY_OPACITY("control_opacity"),
27 LOCK_DRAWER("lock_drawer"); 27 LOCK_DRAWER("lock_drawer"),
28 VERTICAL_ALIGNMENT("vertical_alignment"),
29 FSR_SHARPENING_SLIDER("fsr_sharpening_slider");
28 30
29 override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) 31 override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
30 32
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index fee80bb21..862c6c483 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -93,4 +93,15 @@ object Settings {
93 entries.firstOrNull { it.int == int } ?: Unspecified 93 entries.firstOrNull { it.int == int } ?: Unspecified
94 } 94 }
95 } 95 }
96
97 enum class EmulationVerticalAlignment(val int: Int) {
98 Top(1),
99 Center(0),
100 Bottom(2);
101
102 companion object {
103 fun from(int: Int): EmulationVerticalAlignment =
104 entries.firstOrNull { it.int == int } ?: Center
105 }
106 }
96} 107}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 12f7aa1ab..21ca97bc1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -189,6 +189,16 @@ abstract class SettingsItem(
189 ) 189 )
190 ) 190 )
191 put( 191 put(
192 SliderSetting(
193 IntSetting.FSR_SHARPENING_SLIDER,
194 R.string.fsr_sharpness,
195 R.string.fsr_sharpness_description,
196 0,
197 100,
198 "%"
199 )
200 )
201 put(
192 SingleChoiceSetting( 202 SingleChoiceSetting(
193 IntSetting.RENDERER_ANTI_ALIASING, 203 IntSetting.RENDERER_ANTI_ALIASING,
194 R.string.renderer_anti_aliasing, 204 R.string.renderer_anti_aliasing,
@@ -216,6 +226,15 @@ abstract class SettingsItem(
216 ) 226 )
217 ) 227 )
218 put( 228 put(
229 SingleChoiceSetting(
230 IntSetting.VERTICAL_ALIGNMENT,
231 R.string.vertical_alignment,
232 0,
233 R.array.verticalAlignmentEntries,
234 R.array.verticalAlignmentValues
235 )
236 )
237 put(
219 SwitchSetting( 238 SwitchSetting(
220 BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE, 239 BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE,
221 R.string.use_disk_shader_cache, 240 R.string.use_disk_shader_cache,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 2ad2f4966..db1a58147 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -143,10 +143,12 @@ class SettingsFragmentPresenter(
143 add(IntSetting.RENDERER_RESOLUTION.key) 143 add(IntSetting.RENDERER_RESOLUTION.key)
144 add(IntSetting.RENDERER_VSYNC.key) 144 add(IntSetting.RENDERER_VSYNC.key)
145 add(IntSetting.RENDERER_SCALING_FILTER.key) 145 add(IntSetting.RENDERER_SCALING_FILTER.key)
146 add(IntSetting.FSR_SHARPENING_SLIDER.key)
146 add(IntSetting.RENDERER_ANTI_ALIASING.key) 147 add(IntSetting.RENDERER_ANTI_ALIASING.key)
147 add(IntSetting.MAX_ANISOTROPY.key) 148 add(IntSetting.MAX_ANISOTROPY.key)
148 add(IntSetting.RENDERER_SCREEN_LAYOUT.key) 149 add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
149 add(IntSetting.RENDERER_ASPECT_RATIO.key) 150 add(IntSetting.RENDERER_ASPECT_RATIO.key)
151 add(IntSetting.VERTICAL_ALIGNMENT.key)
150 add(BooleanSetting.PICTURE_IN_PICTURE.key) 152 add(BooleanSetting.PICTURE_IN_PICTURE.key)
151 add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) 153 add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
152 add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) 154 add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
index f5647fa95..872553ac4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
@@ -104,7 +104,10 @@ class AddonsFragment : Fragment() {
104 requireActivity(), 104 requireActivity(),
105 titleId = R.string.addon_notice, 105 titleId = R.string.addon_notice,
106 descriptionId = R.string.addon_notice_description, 106 descriptionId = R.string.addon_notice_description,
107 positiveAction = { addonViewModel.showModInstallPicker(true) } 107 dismissible = false,
108 positiveAction = { addonViewModel.showModInstallPicker(true) },
109 negativeAction = {},
110 negativeButtonTitleId = R.string.close
108 ).show(parentFragmentManager, MessageDialogFragment.TAG) 111 ).show(parentFragmentManager, MessageDialogFragment.TAG)
109 addonViewModel.showModNoticeDialog(false) 112 addonViewModel.showModNoticeDialog(false)
110 } 113 }
@@ -119,7 +122,8 @@ class AddonsFragment : Fragment() {
119 requireActivity(), 122 requireActivity(),
120 titleId = R.string.confirm_uninstall, 123 titleId = R.string.confirm_uninstall,
121 descriptionId = R.string.confirm_uninstall_description, 124 descriptionId = R.string.confirm_uninstall_description,
122 positiveAction = { addonViewModel.onDeleteAddon(it) } 125 positiveAction = { addonViewModel.onDeleteAddon(it) },
126 negativeAction = {}
123 ).show(parentFragmentManager, MessageDialogFragment.TAG) 127 ).show(parentFragmentManager, MessageDialogFragment.TAG)
124 addonViewModel.setAddonToDelete(null) 128 addonViewModel.setAddonToDelete(null)
125 } 129 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 44af896da..6b25cc525 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -15,7 +15,9 @@ import android.os.Handler
15import android.os.Looper 15import android.os.Looper
16import android.os.PowerManager 16import android.os.PowerManager
17import android.os.SystemClock 17import android.os.SystemClock
18import android.util.Rational
18import android.view.* 19import android.view.*
20import android.widget.FrameLayout
19import android.widget.TextView 21import android.widget.TextView
20import android.widget.Toast 22import android.widget.Toast
21import androidx.activity.OnBackPressedCallback 23import androidx.activity.OnBackPressedCallback
@@ -24,6 +26,7 @@ import androidx.core.content.res.ResourcesCompat
24import androidx.core.graphics.Insets 26import androidx.core.graphics.Insets
25import androidx.core.view.ViewCompat 27import androidx.core.view.ViewCompat
26import androidx.core.view.WindowInsetsCompat 28import androidx.core.view.WindowInsetsCompat
29import androidx.core.view.updateLayoutParams
27import androidx.core.view.updatePadding 30import androidx.core.view.updatePadding
28import androidx.drawerlayout.widget.DrawerLayout 31import androidx.drawerlayout.widget.DrawerLayout
29import androidx.drawerlayout.widget.DrawerLayout.DrawerListener 32import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
@@ -52,6 +55,7 @@ import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
52import org.yuzu.yuzu_emu.features.settings.model.IntSetting 55import org.yuzu.yuzu_emu.features.settings.model.IntSetting
53import org.yuzu.yuzu_emu.features.settings.model.Settings 56import org.yuzu.yuzu_emu.features.settings.model.Settings
54import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation 57import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation
58import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationVerticalAlignment
55import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 59import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
56import org.yuzu.yuzu_emu.model.DriverViewModel 60import org.yuzu.yuzu_emu.model.DriverViewModel
57import org.yuzu.yuzu_emu.model.Game 61import org.yuzu.yuzu_emu.model.Game
@@ -617,7 +621,46 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
617 } 621 }
618 622
619 private fun updateScreenLayout() { 623 private fun updateScreenLayout() {
620 binding.surfaceEmulation.setAspectRatio(null) 624 val verticalAlignment =
625 EmulationVerticalAlignment.from(IntSetting.VERTICAL_ALIGNMENT.getInt())
626 val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.getInt()) {
627 0 -> Rational(16, 9)
628 1 -> Rational(4, 3)
629 2 -> Rational(21, 9)
630 3 -> Rational(16, 10)
631 else -> null // Best fit
632 }
633 when (verticalAlignment) {
634 EmulationVerticalAlignment.Top -> {
635 binding.surfaceEmulation.setAspectRatio(aspectRatio)
636 val params = FrameLayout.LayoutParams(
637 ViewGroup.LayoutParams.MATCH_PARENT,
638 ViewGroup.LayoutParams.WRAP_CONTENT
639 )
640 params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
641 binding.surfaceEmulation.layoutParams = params
642 }
643
644 EmulationVerticalAlignment.Center -> {
645 binding.surfaceEmulation.setAspectRatio(null)
646 binding.surfaceEmulation.updateLayoutParams {
647 width = ViewGroup.LayoutParams.MATCH_PARENT
648 height = ViewGroup.LayoutParams.MATCH_PARENT
649 }
650 }
651
652 EmulationVerticalAlignment.Bottom -> {
653 binding.surfaceEmulation.setAspectRatio(aspectRatio)
654 val params =
655 FrameLayout.LayoutParams(
656 ViewGroup.LayoutParams.MATCH_PARENT,
657 ViewGroup.LayoutParams.WRAP_CONTENT
658 )
659 params.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
660 binding.surfaceEmulation.layoutParams = params
661 }
662 }
663 emulationState.updateSurface()
621 emulationActivity?.buildPictureInPictureParams() 664 emulationActivity?.buildPictureInPictureParams()
622 updateOrientation() 665 updateOrientation()
623 } 666 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
index d14b2c634..3ea5e16ca 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
@@ -243,7 +243,9 @@ class GamePropertiesFragment : Fragment() {
243 requireActivity(), 243 requireActivity(),
244 titleId = R.string.delete_save_data, 244 titleId = R.string.delete_save_data,
245 descriptionId = R.string.delete_save_data_warning_description, 245 descriptionId = R.string.delete_save_data_warning_description,
246 positiveAction = { 246 positiveButtonTitleId = android.R.string.cancel,
247 negativeButtonTitleId = android.R.string.ok,
248 negativeAction = {
247 File(args.game.saveDir).deleteRecursively() 249 File(args.game.saveDir).deleteRecursively()
248 Toast.makeText( 250 Toast.makeText(
249 YuzuApplication.appContext, 251 YuzuApplication.appContext,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
index 22b084b9a..c370964e1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.app.Dialog 6import android.app.Dialog
7import android.content.DialogInterface
8import android.content.Intent 7import android.content.Intent
9import android.net.Uri 8import android.net.Uri
10import android.os.Bundle 9import android.os.Bundle
@@ -16,18 +15,52 @@ import androidx.lifecycle.ViewModelProvider
16import com.google.android.material.dialog.MaterialAlertDialogBuilder 15import com.google.android.material.dialog.MaterialAlertDialogBuilder
17import org.yuzu.yuzu_emu.R 16import org.yuzu.yuzu_emu.R
18import org.yuzu.yuzu_emu.model.MessageDialogViewModel 17import org.yuzu.yuzu_emu.model.MessageDialogViewModel
18import org.yuzu.yuzu_emu.utils.Log
19 19
20class MessageDialogFragment : DialogFragment() { 20class MessageDialogFragment : DialogFragment() {
21 private val messageDialogViewModel: MessageDialogViewModel by activityViewModels() 21 private val messageDialogViewModel: MessageDialogViewModel by activityViewModels()
22 22
23 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { 23 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
24 val titleId = requireArguments().getInt(TITLE_ID) 24 val titleId = requireArguments().getInt(TITLE_ID)
25 val titleString = requireArguments().getString(TITLE_STRING)!! 25 val title = if (titleId != 0) {
26 getString(titleId)
27 } else {
28 requireArguments().getString(TITLE_STRING)!!
29 }
30
26 val descriptionId = requireArguments().getInt(DESCRIPTION_ID) 31 val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
27 val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!! 32 val description = if (descriptionId != 0) {
33 getString(descriptionId)
34 } else {
35 requireArguments().getString(DESCRIPTION_STRING)!!
36 }
37
38 val positiveButtonId = requireArguments().getInt(POSITIVE_BUTTON_TITLE_ID)
39 val positiveButtonString = requireArguments().getString(POSITIVE_BUTTON_TITLE_STRING)!!
40 val positiveButton = if (positiveButtonId != 0) {
41 getString(positiveButtonId)
42 } else if (positiveButtonString.isNotEmpty()) {
43 positiveButtonString
44 } else if (messageDialogViewModel.positiveAction != null) {
45 getString(android.R.string.ok)
46 } else {
47 getString(R.string.close)
48 }
49
50 val negativeButtonId = requireArguments().getInt(NEGATIVE_BUTTON_TITLE_ID)
51 val negativeButtonString = requireArguments().getString(NEGATIVE_BUTTON_TITLE_STRING)!!
52 val negativeButton = if (negativeButtonId != 0) {
53 getString(negativeButtonId)
54 } else if (negativeButtonString.isNotEmpty()) {
55 negativeButtonString
56 } else {
57 getString(android.R.string.cancel)
58 }
59
28 val helpLinkId = requireArguments().getInt(HELP_LINK) 60 val helpLinkId = requireArguments().getInt(HELP_LINK)
29 val dismissible = requireArguments().getBoolean(DISMISSIBLE) 61 val dismissible = requireArguments().getBoolean(DISMISSIBLE)
30 val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION) 62 val clearPositiveAction = requireArguments().getBoolean(CLEAR_ACTIONS)
63 val showNegativeButton = requireArguments().getBoolean(SHOW_NEGATIVE_BUTTON)
31 64
32 val builder = MaterialAlertDialogBuilder(requireContext()) 65 val builder = MaterialAlertDialogBuilder(requireContext())
33 66
@@ -35,21 +68,19 @@ class MessageDialogFragment : DialogFragment() {
35 messageDialogViewModel.positiveAction = null 68 messageDialogViewModel.positiveAction = null
36 } 69 }
37 70
38 if (messageDialogViewModel.positiveAction == null) { 71 builder.setPositiveButton(positiveButton) { _, _ ->
39 builder.setPositiveButton(R.string.close, null) 72 messageDialogViewModel.positiveAction?.invoke()
40 } else { 73 }
41 builder.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> 74 if (messageDialogViewModel.negativeAction != null || showNegativeButton) {
42 messageDialogViewModel.positiveAction?.invoke() 75 builder.setNegativeButton(negativeButton) { _, _ ->
43 }.setNegativeButton(android.R.string.cancel, null) 76 messageDialogViewModel.negativeAction?.invoke()
77 }
44 } 78 }
45 79
46 if (titleId != 0) builder.setTitle(titleId) 80 if (title.isNotEmpty()) builder.setTitle(title)
47 if (titleString.isNotEmpty()) builder.setTitle(titleString) 81 if (description.isNotEmpty()) {
48 82 builder.setMessage(Html.fromHtml(description, Html.FROM_HTML_MODE_LEGACY))
49 if (descriptionId != 0) {
50 builder.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY))
51 } 83 }
52 if (descriptionString.isNotEmpty()) builder.setMessage(descriptionString)
53 84
54 if (helpLinkId != 0) { 85 if (helpLinkId != 0) {
55 builder.setNeutralButton(R.string.learn_more) { _, _ -> 86 builder.setNeutralButton(R.string.learn_more) { _, _ ->
@@ -76,8 +107,41 @@ class MessageDialogFragment : DialogFragment() {
76 private const val DESCRIPTION_STRING = "DescriptionString" 107 private const val DESCRIPTION_STRING = "DescriptionString"
77 private const val HELP_LINK = "Link" 108 private const val HELP_LINK = "Link"
78 private const val DISMISSIBLE = "Dismissible" 109 private const val DISMISSIBLE = "Dismissible"
79 private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction" 110 private const val CLEAR_ACTIONS = "ClearActions"
80 111 private const val POSITIVE_BUTTON_TITLE_ID = "PositiveButtonTitleId"
112 private const val POSITIVE_BUTTON_TITLE_STRING = "PositiveButtonTitleString"
113 private const val SHOW_NEGATIVE_BUTTON = "ShowNegativeButton"
114 private const val NEGATIVE_BUTTON_TITLE_ID = "NegativeButtonTitleId"
115 private const val NEGATIVE_BUTTON_TITLE_STRING = "NegativeButtonTitleString"
116
117 /**
118 * Creates a new [MessageDialogFragment] instance.
119 * @param activity Activity that will hold a [MessageDialogViewModel] instance if using
120 * [positiveAction] or [negativeAction].
121 * @param titleId String resource ID that will be used for the title. [titleString] used if 0.
122 * @param titleString String that will be used for the title. No title is set if empty.
123 * @param descriptionId String resource ID that will be used for the description.
124 * [descriptionString] used if 0.
125 * @param descriptionString String that will be used for the description.
126 * No description is set if empty.
127 * @param helpLinkId String resource ID that contains a help link. Will be added as a neutral
128 * button with the title R.string.help.
129 * @param dismissible Whether the dialog is dismissible or not. Typically used to ensure that
130 * the user clicks on one of the dialog buttons before closing.
131 * @param positiveButtonTitleId String resource ID that will be used for the positive button.
132 * [positiveButtonTitleString] used if 0.
133 * @param positiveButtonTitleString String that will be used for the positive button.
134 * android.R.string.close used if empty. android.R.string.ok will be used if [positiveAction]
135 * is not null.
136 * @param positiveAction Lambda to run when the positive button is clicked.
137 * @param showNegativeButton Normally the negative button isn't shown if there is no
138 * [negativeAction] set. This can override that behavior to always show a button.
139 * @param negativeButtonTitleId String resource ID that will be used for the negative button.
140 * [negativeButtonTitleString] used if 0.
141 * @param negativeButtonTitleString String that will be used for the negative button.
142 * android.R.string.cancel used if empty.
143 * @param negativeAction Lambda to run when the negative button is clicked
144 */
81 fun newInstance( 145 fun newInstance(
82 activity: FragmentActivity? = null, 146 activity: FragmentActivity? = null,
83 titleId: Int = 0, 147 titleId: Int = 0,
@@ -86,16 +150,27 @@ class MessageDialogFragment : DialogFragment() {
86 descriptionString: String = "", 150 descriptionString: String = "",
87 helpLinkId: Int = 0, 151 helpLinkId: Int = 0,
88 dismissible: Boolean = true, 152 dismissible: Boolean = true,
89 positiveAction: (() -> Unit)? = null 153 positiveButtonTitleId: Int = 0,
154 positiveButtonTitleString: String = "",
155 positiveAction: (() -> Unit)? = null,
156 showNegativeButton: Boolean = false,
157 negativeButtonTitleId: Int = 0,
158 negativeButtonTitleString: String = "",
159 negativeAction: (() -> Unit)? = null
90 ): MessageDialogFragment { 160 ): MessageDialogFragment {
91 var clearPositiveAction = false 161 var clearActions = false
92 if (activity != null) { 162 if (activity != null) {
93 ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply { 163 ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
94 clear() 164 clear()
95 this.positiveAction = positiveAction 165 this.positiveAction = positiveAction
166 this.negativeAction = negativeAction
96 } 167 }
97 } else { 168 } else {
98 clearPositiveAction = true 169 clearActions = true
170 }
171
172 if (activity == null && (positiveAction == null || negativeAction == null)) {
173 Log.warning("[$TAG] Tried to set action with no activity!")
99 } 174 }
100 175
101 val dialog = MessageDialogFragment() 176 val dialog = MessageDialogFragment()
@@ -106,7 +181,12 @@ class MessageDialogFragment : DialogFragment() {
106 putString(DESCRIPTION_STRING, descriptionString) 181 putString(DESCRIPTION_STRING, descriptionString)
107 putInt(HELP_LINK, helpLinkId) 182 putInt(HELP_LINK, helpLinkId)
108 putBoolean(DISMISSIBLE, dismissible) 183 putBoolean(DISMISSIBLE, dismissible)
109 putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction) 184 putBoolean(CLEAR_ACTIONS, clearActions)
185 putInt(POSITIVE_BUTTON_TITLE_ID, positiveButtonTitleId)
186 putString(POSITIVE_BUTTON_TITLE_STRING, positiveButtonTitleString)
187 putBoolean(SHOW_NEGATIVE_BUTTON, showNegativeButton)
188 putInt(NEGATIVE_BUTTON_TITLE_ID, negativeButtonTitleId)
189 putString(NEGATIVE_BUTTON_TITLE_STRING, negativeButtonTitleString)
110 } 190 }
111 dialog.arguments = bundle 191 dialog.arguments = bundle
112 return dialog 192 return dialog
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt
index 641c5cb17..2db005e49 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/MessageDialogViewModel.kt
@@ -7,8 +7,10 @@ import androidx.lifecycle.ViewModel
7 7
8class MessageDialogViewModel : ViewModel() { 8class MessageDialogViewModel : ViewModel() {
9 var positiveAction: (() -> Unit)? = null 9 var positiveAction: (() -> Unit)? = null
10 var negativeAction: (() -> Unit)? = null
10 11
11 fun clear() { 12 fun clear() {
12 positiveAction = null 13 positiveAction = null
14 negativeAction = null
13 } 15 }
14} 16}
diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h
index 4a3bc8e53..00baf86a9 100644
--- a/src/android/app/src/main/jni/android_settings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -38,6 +38,13 @@ struct Values {
38 Settings::Specialization::Default, 38 Settings::Specialization::Default,
39 true, 39 true,
40 true}; 40 true};
41 Settings::Setting<s32> vertical_alignment{linkage,
42 0,
43 "vertical_alignment",
44 Settings::Category::Android,
45 Settings::Specialization::Default,
46 true,
47 true};
41 48
42 Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path", 49 Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",
43 Settings::Category::GpuDriver}; 50 Settings::Category::GpuDriver};
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 4701913eb..1bd6455b4 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -292,4 +292,15 @@
292 <item>5</item> 292 <item>5</item>
293 </integer-array> 293 </integer-array>
294 294
295 <string-array name="verticalAlignmentEntries">
296 <item>@string/top</item>
297 <item>@string/center</item>
298 <item>@string/bottom</item>
299 </string-array>
300 <integer-array name="verticalAlignmentValues">
301 <item>1</item>
302 <item>0</item>
303 <item>2</item>
304 </integer-array>
305
295</resources> 306</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 489e00107..78a4c958a 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -226,6 +226,8 @@
226 <string name="renderer_screen_layout">Orientation</string> 226 <string name="renderer_screen_layout">Orientation</string>
227 <string name="renderer_aspect_ratio">Aspect ratio</string> 227 <string name="renderer_aspect_ratio">Aspect ratio</string>
228 <string name="renderer_scaling_filter">Window adapting filter</string> 228 <string name="renderer_scaling_filter">Window adapting filter</string>
229 <string name="fsr_sharpness">FSR sharpness</string>
230 <string name="fsr_sharpness_description">Determines how sharpened the image will look while using FSR\'s dynamic contrast</string>
229 <string name="renderer_anti_aliasing">Anti-aliasing method</string> 231 <string name="renderer_anti_aliasing">Anti-aliasing method</string>
230 <string name="renderer_force_max_clock">Force maximum clocks (Adreno only)</string> 232 <string name="renderer_force_max_clock">Force maximum clocks (Adreno only)</string>
231 <string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string> 233 <string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string>
@@ -558,6 +560,12 @@
558 <string name="mute">Mute</string> 560 <string name="mute">Mute</string>
559 <string name="unmute">Unmute</string> 561 <string name="unmute">Unmute</string>
560 562
563 <!-- Emulation vertical alignment -->
564 <string name="vertical_alignment">Vertical alignment</string>
565 <string name="top">Top</string>
566 <string name="center">Center</string>
567 <string name="bottom">Bottom</string>
568
561 <!-- Licenses screen strings --> 569 <!-- Licenses screen strings -->
562 <string name="licenses">Licenses</string> 570 <string name="licenses">Licenses</string>
563 <string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string> 571 <string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string>
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5e2f4869e..dc2c611eb 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -666,6 +666,18 @@ add_library(core STATIC
666 hle/service/ldn/ldn.h 666 hle/service/ldn/ldn.h
667 hle/service/ldn/ldn_results.h 667 hle/service/ldn/ldn_results.h
668 hle/service/ldn/ldn_types.h 668 hle/service/ldn/ldn_types.h
669 hle/service/ldn/monitor_service.cpp
670 hle/service/ldn/monitor_service.h
671 hle/service/ldn/sf_monitor_service.cpp
672 hle/service/ldn/sf_monitor_service.h
673 hle/service/ldn/sf_service.cpp
674 hle/service/ldn/sf_service.h
675 hle/service/ldn/sf_service_monitor.cpp
676 hle/service/ldn/sf_service_monitor.h
677 hle/service/ldn/system_local_communication_service.cpp
678 hle/service/ldn/system_local_communication_service.h
679 hle/service/ldn/user_local_communication_service.cpp
680 hle/service/ldn/user_local_communication_service.h
669 hle/service/ldr/ldr.cpp 681 hle/service/ldr/ldr.cpp
670 hle/service/ldr/ldr.h 682 hle/service/ldr/ldr.h
671 hle/service/lm/lm.cpp 683 hle/service/lm/lm.cpp
diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp
index 8f3c04550..b9db19618 100644
--- a/src/core/hle/service/ldn/lan_discovery.cpp
+++ b/src/core/hle/service/ldn/lan_discovery.cpp
@@ -85,15 +85,14 @@ Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const {
85} 85}
86 86
87Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network, 87Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network,
88 std::vector<NodeLatestUpdate>& out_updates, 88 std::span<NodeLatestUpdate> out_updates) {
89 std::size_t buffer_count) { 89 if (out_updates.size() > NodeCountMax) {
90 if (buffer_count > NodeCountMax) {
91 return ResultInvalidBufferCount; 90 return ResultInvalidBufferCount;
92 } 91 }
93 92
94 if (state == State::AccessPointCreated || state == State::StationConnected) { 93 if (state == State::AccessPointCreated || state == State::StationConnected) {
95 std::memcpy(&out_network, &network_info, sizeof(network_info)); 94 std::memcpy(&out_network, &network_info, sizeof(network_info));
96 for (std::size_t i = 0; i < buffer_count; i++) { 95 for (std::size_t i = 0; i < out_updates.size(); i++) {
97 out_updates[i].state_change = node_changes[i].state_change; 96 out_updates[i].state_change = node_changes[i].state_change;
98 node_changes[i].state_change = NodeStateChange::None; 97 node_changes[i].state_change = NodeStateChange::None;
99 } 98 }
@@ -107,15 +106,8 @@ DisconnectReason LANDiscovery::GetDisconnectReason() const {
107 return disconnect_reason; 106 return disconnect_reason;
108} 107}
109 108
110Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count, 109Result LANDiscovery::Scan(std::span<NetworkInfo> out_networks, s16& out_count,
111 const ScanFilter& filter) { 110 const ScanFilter& filter) {
112 if (!IsFlagSet(filter.flag, ScanFilterFlag::NetworkType) ||
113 filter.network_type <= NetworkType::All) {
114 if (!IsFlagSet(filter.flag, ScanFilterFlag::Ssid) && filter.ssid.length >= SsidLengthMax) {
115 return ResultBadInput;
116 }
117 }
118
119 { 111 {
120 std::scoped_lock lock{packet_mutex}; 112 std::scoped_lock lock{packet_mutex};
121 scan_results.clear(); 113 scan_results.clear();
@@ -128,7 +120,7 @@ Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count,
128 120
129 std::scoped_lock lock{packet_mutex}; 121 std::scoped_lock lock{packet_mutex};
130 for (const auto& [key, info] : scan_results) { 122 for (const auto& [key, info] : scan_results) {
131 if (count >= networks.size()) { 123 if (out_count >= static_cast<s16>(out_networks.size())) {
132 break; 124 break;
133 } 125 }
134 126
@@ -159,7 +151,7 @@ Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count,
159 } 151 }
160 } 152 }
161 153
162 networks[count++] = info; 154 out_networks[out_count++] = info;
163 } 155 }
164 156
165 return ResultSuccess; 157 return ResultSuccess;
diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h
index 3833cd764..8f7a8dfc4 100644
--- a/src/core/hle/service/ldn/lan_discovery.h
+++ b/src/core/hle/service/ldn/lan_discovery.h
@@ -54,11 +54,10 @@ public:
54 void SetState(State new_state); 54 void SetState(State new_state);
55 55
56 Result GetNetworkInfo(NetworkInfo& out_network) const; 56 Result GetNetworkInfo(NetworkInfo& out_network) const;
57 Result GetNetworkInfo(NetworkInfo& out_network, std::vector<NodeLatestUpdate>& out_updates, 57 Result GetNetworkInfo(NetworkInfo& out_network, std::span<NodeLatestUpdate> out_updates);
58 std::size_t buffer_count);
59 58
60 DisconnectReason GetDisconnectReason() const; 59 DisconnectReason GetDisconnectReason() const;
61 Result Scan(std::vector<NetworkInfo>& networks, u16& count, const ScanFilter& filter); 60 Result Scan(std::span<NetworkInfo> out_networks, s16& out_count, const ScanFilter& filter);
62 Result SetAdvertiseData(std::span<const u8> data); 61 Result SetAdvertiseData(std::span<const u8> data);
63 62
64 Result OpenAccessPoint(); 63 Result OpenAccessPoint();
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 961f89a14..f2d638c30 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -1,36 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <memory>
5
6#include "core/core.h" 4#include "core/core.h"
7#include "core/hle/service/ldn/lan_discovery.h" 5#include "core/hle/service/cmif_serialization.h"
8#include "core/hle/service/ldn/ldn.h" 6#include "core/hle/service/ldn/ldn.h"
9#include "core/hle/service/ldn/ldn_results.h" 7#include "core/hle/service/ldn/monitor_service.h"
10#include "core/hle/service/ldn/ldn_types.h" 8#include "core/hle/service/ldn/sf_monitor_service.h"
11#include "core/hle/service/server_manager.h" 9#include "core/hle/service/ldn/sf_service.h"
12#include "core/internal_network/network.h" 10#include "core/hle/service/ldn/sf_service_monitor.h"
13#include "core/internal_network/network_interface.h" 11#include "core/hle/service/ldn/system_local_communication_service.h"
14#include "network/network.h" 12#include "core/hle/service/ldn/user_local_communication_service.h"
15
16// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
17#undef CreateEvent
18 13
19namespace Service::LDN { 14namespace Service::LDN {
20 15
21class IMonitorService final : public ServiceFramework<IMonitorService> { 16class IMonitorServiceCreator final : public ServiceFramework<IMonitorServiceCreator> {
22public: 17public:
23 explicit IMonitorService(Core::System& system_) : ServiceFramework{system_, "IMonitorService"} { 18 explicit IMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:m"} {
24 // clang-format off 19 // clang-format off
25 static const FunctionInfo functions[] = { 20 static const FunctionInfo functions[] = {
26 {0, &IMonitorService::GetStateForMonitor, "GetStateForMonitor"}, 21 {0, C<&IMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"}
27 {1, nullptr, "GetNetworkInfoForMonitor"},
28 {2, nullptr, "GetIpv4AddressForMonitor"},
29 {3, nullptr, "GetDisconnectReasonForMonitor"},
30 {4, nullptr, "GetSecurityParameterForMonitor"},
31 {5, nullptr, "GetNetworkConfigForMonitor"},
32 {100, &IMonitorService::InitializeMonitor, "InitializeMonitor"},
33 {101, nullptr, "FinalizeMonitor"},
34 }; 22 };
35 // clang-format on 23 // clang-format on
36 24
@@ -38,84 +26,20 @@ public:
38 } 26 }
39 27
40private: 28private:
41 void GetStateForMonitor(HLERequestContext& ctx) { 29 Result CreateMonitorService(OutInterface<IMonitorService> out_interface) {
42 LOG_INFO(Service_LDN, "called");
43
44 IPC::ResponseBuilder rb{ctx, 3};
45 rb.Push(ResultSuccess);
46 rb.PushEnum(state);
47 }
48
49 void InitializeMonitor(HLERequestContext& ctx) {
50 LOG_INFO(Service_LDN, "called");
51
52 state = State::Initialized;
53
54 IPC::ResponseBuilder rb{ctx, 2};
55 rb.Push(ResultSuccess);
56 }
57
58 State state{State::None};
59};
60
61class LDNM final : public ServiceFramework<LDNM> {
62public:
63 explicit LDNM(Core::System& system_) : ServiceFramework{system_, "ldn:m"} {
64 // clang-format off
65 static const FunctionInfo functions[] = {
66 {0, &LDNM::CreateMonitorService, "CreateMonitorService"}
67 };
68 // clang-format on
69
70 RegisterHandlers(functions);
71 }
72
73 void CreateMonitorService(HLERequestContext& ctx) {
74 LOG_DEBUG(Service_LDN, "called"); 30 LOG_DEBUG(Service_LDN, "called");
75 31
76 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 32 *out_interface = std::make_shared<IMonitorService>(system);
77 rb.Push(ResultSuccess); 33 R_SUCCEED();
78 rb.PushIpcInterface<IMonitorService>(system);
79 } 34 }
80}; 35};
81 36
82class ISystemLocalCommunicationService final 37class ISystemServiceCreator final : public ServiceFramework<ISystemServiceCreator> {
83 : public ServiceFramework<ISystemLocalCommunicationService> {
84public: 38public:
85 explicit ISystemLocalCommunicationService(Core::System& system_) 39 explicit ISystemServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:s"} {
86 : ServiceFramework{system_, "ISystemLocalCommunicationService"} {
87 // clang-format off 40 // clang-format off
88 static const FunctionInfo functions[] = { 41 static const FunctionInfo functions[] = {
89 {0, nullptr, "GetState"}, 42 {0, C<&ISystemServiceCreator::CreateSystemLocalCommunicationService>, "CreateSystemLocalCommunicationService"},
90 {1, nullptr, "GetNetworkInfo"},
91 {2, nullptr, "GetIpv4Address"},
92 {3, nullptr, "GetDisconnectReason"},
93 {4, nullptr, "GetSecurityParameter"},
94 {5, nullptr, "GetNetworkConfig"},
95 {100, nullptr, "AttachStateChangeEvent"},
96 {101, nullptr, "GetNetworkInfoLatestUpdate"},
97 {102, nullptr, "Scan"},
98 {103, nullptr, "ScanPrivate"},
99 {104, nullptr, "SetWirelessControllerRestriction"},
100 {200, nullptr, "OpenAccessPoint"},
101 {201, nullptr, "CloseAccessPoint"},
102 {202, nullptr, "CreateNetwork"},
103 {203, nullptr, "CreateNetworkPrivate"},
104 {204, nullptr, "DestroyNetwork"},
105 {205, nullptr, "Reject"},
106 {206, nullptr, "SetAdvertiseData"},
107 {207, nullptr, "SetStationAcceptPolicy"},
108 {208, nullptr, "AddAcceptFilterEntry"},
109 {209, nullptr, "ClearAcceptFilter"},
110 {300, nullptr, "OpenStation"},
111 {301, nullptr, "CloseStation"},
112 {302, nullptr, "Connect"},
113 {303, nullptr, "ConnectPrivate"},
114 {304, nullptr, "Disconnect"},
115 {400, nullptr, "InitializeSystem"},
116 {401, nullptr, "FinalizeSystem"},
117 {402, nullptr, "SetOperationMode"},
118 {403, &ISystemLocalCommunicationService::InitializeSystem2, "InitializeSystem2"},
119 }; 43 };
120 // clang-format on 44 // clang-format on
121 45
@@ -123,687 +47,78 @@ public:
123 } 47 }
124 48
125private: 49private:
126 void InitializeSystem2(HLERequestContext& ctx) { 50 Result CreateSystemLocalCommunicationService(
127 LOG_WARNING(Service_LDN, "(STUBBED) called"); 51 OutInterface<ISystemLocalCommunicationService> out_interface) {
128
129 IPC::ResponseBuilder rb{ctx, 2};
130 rb.Push(ResultSuccess);
131 }
132};
133
134class IUserLocalCommunicationService final
135 : public ServiceFramework<IUserLocalCommunicationService> {
136public:
137 explicit IUserLocalCommunicationService(Core::System& system_)
138 : ServiceFramework{system_, "IUserLocalCommunicationService"},
139 service_context{system, "IUserLocalCommunicationService"},
140 room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} {
141 // clang-format off
142 static const FunctionInfo functions[] = {
143 {0, &IUserLocalCommunicationService::GetState, "GetState"},
144 {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"},
145 {2, &IUserLocalCommunicationService::GetIpv4Address, "GetIpv4Address"},
146 {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"},
147 {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"},
148 {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"},
149 {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"},
150 {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"},
151 {102, &IUserLocalCommunicationService::Scan, "Scan"},
152 {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"},
153 {104, &IUserLocalCommunicationService::SetWirelessControllerRestriction, "SetWirelessControllerRestriction"},
154 {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"},
155 {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"},
156 {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"},
157 {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"},
158 {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"},
159 {205, nullptr, "Reject"},
160 {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"},
161 {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"},
162 {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"},
163 {209, nullptr, "ClearAcceptFilter"},
164 {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"},
165 {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"},
166 {302, &IUserLocalCommunicationService::Connect, "Connect"},
167 {303, nullptr, "ConnectPrivate"},
168 {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"},
169 {400, &IUserLocalCommunicationService::Initialize, "Initialize"},
170 {401, &IUserLocalCommunicationService::Finalize, "Finalize"},
171 {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"},
172 };
173 // clang-format on
174
175 RegisterHandlers(functions);
176
177 state_change_event =
178 service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent");
179 }
180
181 ~IUserLocalCommunicationService() {
182 if (is_initialized) {
183 if (auto room_member = room_network.GetRoomMember().lock()) {
184 room_member->Unbind(ldn_packet_received);
185 }
186 }
187
188 service_context.CloseEvent(state_change_event);
189 }
190
191 /// Callback to parse and handle a received LDN packet.
192 void OnLDNPacketReceived(const Network::LDNPacket& packet) {
193 lan_discovery.ReceivePacket(packet);
194 }
195
196 void OnEventFired() {
197 state_change_event->Signal();
198 }
199
200 void GetState(HLERequestContext& ctx) {
201 State state = State::Error;
202
203 if (is_initialized) {
204 state = lan_discovery.GetState();
205 }
206
207 IPC::ResponseBuilder rb{ctx, 3};
208 rb.Push(ResultSuccess);
209 rb.PushEnum(state);
210 }
211
212 void GetNetworkInfo(HLERequestContext& ctx) {
213 const auto write_buffer_size = ctx.GetWriteBufferSize();
214
215 if (write_buffer_size != sizeof(NetworkInfo)) {
216 LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size);
217 IPC::ResponseBuilder rb{ctx, 2};
218 rb.Push(ResultBadInput);
219 return;
220 }
221
222 NetworkInfo network_info{};
223 const auto rc = lan_discovery.GetNetworkInfo(network_info);
224 if (rc.IsError()) {
225 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
226 IPC::ResponseBuilder rb{ctx, 2};
227 rb.Push(rc);
228 return;
229 }
230
231 ctx.WriteBuffer<NetworkInfo>(network_info);
232 IPC::ResponseBuilder rb{ctx, 2};
233 rb.Push(ResultSuccess);
234 }
235
236 void GetIpv4Address(HLERequestContext& ctx) {
237 const auto network_interface = Network::GetSelectedNetworkInterface();
238
239 if (!network_interface) {
240 LOG_ERROR(Service_LDN, "No network interface available");
241 IPC::ResponseBuilder rb{ctx, 2};
242 rb.Push(ResultNoIpAddress);
243 return;
244 }
245
246 Ipv4Address current_address{Network::TranslateIPv4(network_interface->ip_address)};
247 Ipv4Address subnet_mask{Network::TranslateIPv4(network_interface->subnet_mask)};
248
249 // When we're connected to a room, spoof the hosts IP address
250 if (auto room_member = room_network.GetRoomMember().lock()) {
251 if (room_member->IsConnected()) {
252 current_address = room_member->GetFakeIpAddress();
253 }
254 }
255
256 std::reverse(std::begin(current_address), std::end(current_address)); // ntohl
257 std::reverse(std::begin(subnet_mask), std::end(subnet_mask)); // ntohl
258
259 IPC::ResponseBuilder rb{ctx, 4};
260 rb.Push(ResultSuccess);
261 rb.PushRaw(current_address);
262 rb.PushRaw(subnet_mask);
263 }
264
265 void GetDisconnectReason(HLERequestContext& ctx) {
266 IPC::ResponseBuilder rb{ctx, 3};
267 rb.Push(ResultSuccess);
268 rb.PushEnum(lan_discovery.GetDisconnectReason());
269 }
270
271 void GetSecurityParameter(HLERequestContext& ctx) {
272 SecurityParameter security_parameter{};
273 NetworkInfo info{};
274 const Result rc = lan_discovery.GetNetworkInfo(info);
275
276 if (rc.IsError()) {
277 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
278 IPC::ResponseBuilder rb{ctx, 2};
279 rb.Push(rc);
280 return;
281 }
282
283 security_parameter.session_id = info.network_id.session_id;
284 std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(),
285 sizeof(SecurityParameter::data));
286
287 IPC::ResponseBuilder rb{ctx, 10};
288 rb.Push(rc);
289 rb.PushRaw<SecurityParameter>(security_parameter);
290 }
291
292 void GetNetworkConfig(HLERequestContext& ctx) {
293 NetworkConfig config{};
294 NetworkInfo info{};
295 const Result rc = lan_discovery.GetNetworkInfo(info);
296
297 if (rc.IsError()) {
298 LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw);
299 IPC::ResponseBuilder rb{ctx, 2};
300 rb.Push(rc);
301 return;
302 }
303
304 config.intent_id = info.network_id.intent_id;
305 config.channel = info.common.channel;
306 config.node_count_max = info.ldn.node_count_max;
307 config.local_communication_version = info.ldn.nodes[0].local_communication_version;
308
309 IPC::ResponseBuilder rb{ctx, 10};
310 rb.Push(rc);
311 rb.PushRaw<NetworkConfig>(config);
312 }
313
314 void AttachStateChangeEvent(HLERequestContext& ctx) {
315 LOG_INFO(Service_LDN, "called");
316
317 IPC::ResponseBuilder rb{ctx, 2, 1};
318 rb.Push(ResultSuccess);
319 rb.PushCopyObjects(state_change_event->GetReadableEvent());
320 }
321
322 void GetNetworkInfoLatestUpdate(HLERequestContext& ctx) {
323 const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0);
324 const std::size_t node_buffer_count = ctx.GetWriteBufferNumElements<NodeLatestUpdate>(1);
325
326 if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
327 LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size,
328 node_buffer_count);
329 IPC::ResponseBuilder rb{ctx, 2};
330 rb.Push(ResultBadInput);
331 return;
332 }
333
334 NetworkInfo info{};
335 std::vector<NodeLatestUpdate> latest_update(node_buffer_count);
336
337 const auto rc = lan_discovery.GetNetworkInfo(info, latest_update, latest_update.size());
338 if (rc.IsError()) {
339 LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
340 IPC::ResponseBuilder rb{ctx, 2};
341 rb.Push(rc);
342 return;
343 }
344
345 ctx.WriteBuffer(info, 0);
346 ctx.WriteBuffer(latest_update, 1);
347
348 IPC::ResponseBuilder rb{ctx, 2};
349 rb.Push(ResultSuccess);
350 }
351
352 void Scan(HLERequestContext& ctx) {
353 ScanImpl(ctx);
354 }
355
356 void ScanPrivate(HLERequestContext& ctx) {
357 ScanImpl(ctx, true);
358 }
359
360 void ScanImpl(HLERequestContext& ctx, bool is_private = false) {
361 IPC::RequestParser rp{ctx};
362 const auto channel{rp.PopEnum<WifiChannel>()};
363 const auto scan_filter{rp.PopRaw<ScanFilter>()};
364
365 const std::size_t network_info_size = ctx.GetWriteBufferNumElements<NetworkInfo>();
366
367 if (network_info_size == 0) {
368 LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size);
369 IPC::ResponseBuilder rb{ctx, 2};
370 rb.Push(ResultBadInput);
371 return;
372 }
373
374 u16 count = 0;
375 std::vector<NetworkInfo> network_infos(network_info_size);
376 Result rc = lan_discovery.Scan(network_infos, count, scan_filter);
377
378 LOG_INFO(Service_LDN,
379 "called, channel={}, filter_scan_flag={}, filter_network_type={}, is_private={}",
380 channel, scan_filter.flag, scan_filter.network_type, is_private);
381
382 ctx.WriteBuffer(network_infos);
383
384 IPC::ResponseBuilder rb{ctx, 3};
385 rb.Push(rc);
386 rb.Push<u32>(count);
387 }
388
389 void SetWirelessControllerRestriction(HLERequestContext& ctx) {
390 LOG_WARNING(Service_LDN, "(STUBBED) called");
391
392 IPC::ResponseBuilder rb{ctx, 2};
393 rb.Push(ResultSuccess);
394 }
395
396 void OpenAccessPoint(HLERequestContext& ctx) {
397 LOG_INFO(Service_LDN, "called");
398
399 IPC::ResponseBuilder rb{ctx, 2};
400 rb.Push(lan_discovery.OpenAccessPoint());
401 }
402
403 void CloseAccessPoint(HLERequestContext& ctx) {
404 LOG_INFO(Service_LDN, "called");
405
406 IPC::ResponseBuilder rb{ctx, 2};
407 rb.Push(lan_discovery.CloseAccessPoint());
408 }
409
410 void CreateNetwork(HLERequestContext& ctx) {
411 LOG_INFO(Service_LDN, "called");
412
413 CreateNetworkImpl(ctx);
414 }
415
416 void CreateNetworkPrivate(HLERequestContext& ctx) {
417 LOG_INFO(Service_LDN, "called");
418
419 CreateNetworkImpl(ctx, true);
420 }
421
422 void CreateNetworkImpl(HLERequestContext& ctx, bool is_private = false) {
423 IPC::RequestParser rp{ctx};
424
425 const auto security_config{rp.PopRaw<SecurityConfig>()};
426 [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw<SecurityParameter>()
427 : SecurityParameter{}};
428 const auto user_config{rp.PopRaw<UserConfig>()};
429 rp.Pop<u32>(); // Padding
430 const auto network_Config{rp.PopRaw<NetworkConfig>()};
431
432 IPC::ResponseBuilder rb{ctx, 2};
433 rb.Push(lan_discovery.CreateNetwork(security_config, user_config, network_Config));
434 }
435
436 void DestroyNetwork(HLERequestContext& ctx) {
437 LOG_INFO(Service_LDN, "called");
438
439 IPC::ResponseBuilder rb{ctx, 2};
440 rb.Push(lan_discovery.DestroyNetwork());
441 }
442
443 void SetAdvertiseData(HLERequestContext& ctx) {
444 const auto read_buffer = ctx.ReadBuffer();
445
446 IPC::ResponseBuilder rb{ctx, 2};
447 rb.Push(lan_discovery.SetAdvertiseData(read_buffer));
448 }
449
450 void SetStationAcceptPolicy(HLERequestContext& ctx) {
451 LOG_WARNING(Service_LDN, "(STUBBED) called");
452
453 IPC::ResponseBuilder rb{ctx, 2};
454 rb.Push(ResultSuccess);
455 }
456
457 void AddAcceptFilterEntry(HLERequestContext& ctx) {
458 LOG_WARNING(Service_LDN, "(STUBBED) called");
459
460 IPC::ResponseBuilder rb{ctx, 2};
461 rb.Push(ResultSuccess);
462 }
463
464 void OpenStation(HLERequestContext& ctx) {
465 LOG_INFO(Service_LDN, "called");
466
467 IPC::ResponseBuilder rb{ctx, 2};
468 rb.Push(lan_discovery.OpenStation());
469 }
470
471 void CloseStation(HLERequestContext& ctx) {
472 LOG_INFO(Service_LDN, "called");
473
474 IPC::ResponseBuilder rb{ctx, 2};
475 rb.Push(lan_discovery.CloseStation());
476 }
477
478 void Connect(HLERequestContext& ctx) {
479 IPC::RequestParser rp{ctx};
480 struct Parameters {
481 SecurityConfig security_config;
482 UserConfig user_config;
483 u32 local_communication_version;
484 u32 option;
485 };
486 static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size.");
487
488 const auto parameters{rp.PopRaw<Parameters>()};
489
490 LOG_INFO(Service_LDN,
491 "called, passphrase_size={}, security_mode={}, "
492 "local_communication_version={}",
493 parameters.security_config.passphrase_size,
494 parameters.security_config.security_mode, parameters.local_communication_version);
495
496 const auto read_buffer = ctx.ReadBuffer();
497 if (read_buffer.size() != sizeof(NetworkInfo)) {
498 LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
499 IPC::ResponseBuilder rb{ctx, 2};
500 rb.Push(ResultBadInput);
501 return;
502 }
503
504 NetworkInfo network_info{};
505 std::memcpy(&network_info, read_buffer.data(), read_buffer.size());
506
507 IPC::ResponseBuilder rb{ctx, 2};
508 rb.Push(lan_discovery.Connect(network_info, parameters.user_config,
509 static_cast<u16>(parameters.local_communication_version)));
510 }
511
512 void Disconnect(HLERequestContext& ctx) {
513 LOG_INFO(Service_LDN, "called");
514
515 IPC::ResponseBuilder rb{ctx, 2};
516 rb.Push(lan_discovery.Disconnect());
517 }
518
519 void Initialize(HLERequestContext& ctx) {
520 const auto rc = InitializeImpl(ctx);
521 if (rc.IsError()) {
522 LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
523 }
524
525 IPC::ResponseBuilder rb{ctx, 2};
526 rb.Push(rc);
527 }
528
529 void Finalize(HLERequestContext& ctx) {
530 if (auto room_member = room_network.GetRoomMember().lock()) {
531 room_member->Unbind(ldn_packet_received);
532 }
533
534 is_initialized = false;
535
536 IPC::ResponseBuilder rb{ctx, 2};
537 rb.Push(lan_discovery.Finalize());
538 }
539
540 void Initialize2(HLERequestContext& ctx) {
541 const auto rc = InitializeImpl(ctx);
542 if (rc.IsError()) {
543 LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
544 }
545
546 IPC::ResponseBuilder rb{ctx, 2};
547 rb.Push(rc);
548 }
549
550 Result InitializeImpl(HLERequestContext& ctx) {
551 const auto network_interface = Network::GetSelectedNetworkInterface();
552 if (!network_interface) {
553 LOG_ERROR(Service_LDN, "No network interface is set");
554 return ResultAirplaneModeEnabled;
555 }
556
557 if (auto room_member = room_network.GetRoomMember().lock()) {
558 ldn_packet_received = room_member->BindOnLdnPacketReceived(
559 [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); });
560 } else {
561 LOG_ERROR(Service_LDN, "Couldn't bind callback!");
562 return ResultAirplaneModeEnabled;
563 }
564
565 lan_discovery.Initialize([&]() { OnEventFired(); });
566 is_initialized = true;
567 return ResultSuccess;
568 }
569
570 KernelHelpers::ServiceContext service_context;
571 Kernel::KEvent* state_change_event;
572 Network::RoomNetwork& room_network;
573 LANDiscovery lan_discovery;
574
575 // Callback identifier for the OnLDNPacketReceived event.
576 Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received;
577
578 bool is_initialized{};
579};
580
581class LDNS final : public ServiceFramework<LDNS> {
582public:
583 explicit LDNS(Core::System& system_) : ServiceFramework{system_, "ldn:s"} {
584 // clang-format off
585 static const FunctionInfo functions[] = {
586 {0, &LDNS::CreateSystemLocalCommunicationService, "CreateSystemLocalCommunicationService"},
587 };
588 // clang-format on
589
590 RegisterHandlers(functions);
591 }
592
593 void CreateSystemLocalCommunicationService(HLERequestContext& ctx) {
594 LOG_DEBUG(Service_LDN, "called"); 52 LOG_DEBUG(Service_LDN, "called");
595 53
596 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 54 *out_interface = std::make_shared<ISystemLocalCommunicationService>(system);
597 rb.Push(ResultSuccess); 55 R_SUCCEED();
598 rb.PushIpcInterface<ISystemLocalCommunicationService>(system);
599 } 56 }
600}; 57};
601 58
602class LDNU final : public ServiceFramework<LDNU> { 59class IUserServiceCreator final : public ServiceFramework<IUserServiceCreator> {
603public: 60public:
604 explicit LDNU(Core::System& system_) : ServiceFramework{system_, "ldn:u"} { 61 explicit IUserServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:u"} {
605 // clang-format off 62 // clang-format off
606 static const FunctionInfo functions[] = { 63 static const FunctionInfo functions[] = {
607 {0, &LDNU::CreateUserLocalCommunicationService, "CreateUserLocalCommunicationService"}, 64 {0, C<&IUserServiceCreator::CreateUserLocalCommunicationService>, "CreateUserLocalCommunicationService"},
608 }; 65 };
609 // clang-format on 66 // clang-format on
610 67
611 RegisterHandlers(functions); 68 RegisterHandlers(functions);
612 } 69 }
613 70
614 void CreateUserLocalCommunicationService(HLERequestContext& ctx) { 71private:
72 Result CreateUserLocalCommunicationService(
73 OutInterface<IUserLocalCommunicationService> out_interface) {
615 LOG_DEBUG(Service_LDN, "called"); 74 LOG_DEBUG(Service_LDN, "called");
616 75
617 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 76 *out_interface = std::make_shared<IUserLocalCommunicationService>(system);
618 rb.Push(ResultSuccess); 77 R_SUCCEED();
619 rb.PushIpcInterface<IUserLocalCommunicationService>(system);
620 } 78 }
621}; 79};
622 80
623class INetworkService final : public ServiceFramework<INetworkService> { 81class ISfServiceCreator final : public ServiceFramework<ISfServiceCreator> {
624public: 82public:
625 explicit INetworkService(Core::System& system_) : ServiceFramework{system_, "INetworkService"} { 83 explicit ISfServiceCreator(Core::System& system_, bool is_system_, const char* name_)
84 : ServiceFramework{system_, name_}, is_system{is_system_} {
626 // clang-format off 85 // clang-format off
627 static const FunctionInfo functions[] = { 86 static const FunctionInfo functions[] = {
628 {0, nullptr, "Initialize"}, 87 {0, C<&ISfServiceCreator::CreateNetworkService>, "CreateNetworkService"},
629 {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"}, 88 {8, C<&ISfServiceCreator::CreateNetworkServiceMonitor>, "CreateNetworkServiceMonitor"},
630 {264, nullptr, "GetNetworkInterfaceLastError"},
631 {272, nullptr, "GetRole"},
632 {280, nullptr, "GetAdvertiseData"},
633 {288, nullptr, "GetGroupInfo"},
634 {296, nullptr, "GetGroupInfo2"},
635 {304, nullptr, "GetGroupOwner"},
636 {312, nullptr, "GetIpConfig"},
637 {320, nullptr, "GetLinkLevel"},
638 {512, nullptr, "Scan"},
639 {768, nullptr, "CreateGroup"},
640 {776, nullptr, "DestroyGroup"},
641 {784, nullptr, "SetAdvertiseData"},
642 {1536, nullptr, "SendToOtherGroup"},
643 {1544, nullptr, "RecvFromOtherGroup"},
644 {1552, nullptr, "AddAcceptableGroupId"},
645 {1560, nullptr, "ClearAcceptableGroupId"},
646 }; 89 };
647 // clang-format on 90 // clang-format on
648 91
649 RegisterHandlers(functions); 92 RegisterHandlers(functions);
650 } 93 }
651};
652
653class INetworkServiceMonitor final : public ServiceFramework<INetworkServiceMonitor> {
654public:
655 explicit INetworkServiceMonitor(Core::System& system_)
656 : ServiceFramework{system_, "INetworkServiceMonitor"} {
657 // clang-format off
658 static const FunctionInfo functions[] = {
659 {0, &INetworkServiceMonitor::Initialize, "Initialize"},
660 {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"},
661 {264, nullptr, "GetNetworkInterfaceLastError"},
662 {272, nullptr, "GetRole"},
663 {280, nullptr, "GetAdvertiseData"},
664 {281, nullptr, "GetAdvertiseData2"},
665 {288, nullptr, "GetGroupInfo"},
666 {296, nullptr, "GetGroupInfo2"},
667 {304, nullptr, "GetGroupOwner"},
668 {312, nullptr, "GetIpConfig"},
669 {320, nullptr, "GetLinkLevel"},
670 {328, nullptr, "AttachJoinEvent"},
671 {336, nullptr, "GetMembers"},
672 };
673 // clang-format on
674
675 RegisterHandlers(functions);
676 }
677
678 void Initialize(HLERequestContext& ctx) {
679 LOG_WARNING(Service_LDN, "(STUBBED) called");
680
681 IPC::ResponseBuilder rb{ctx, 2};
682 rb.Push(ResultDisabled);
683 }
684};
685
686class LP2PAPP final : public ServiceFramework<LP2PAPP> {
687public:
688 explicit LP2PAPP(Core::System& system_) : ServiceFramework{system_, "lp2p:app"} {
689 // clang-format off
690 static const FunctionInfo functions[] = {
691 {0, &LP2PAPP::CreateMonitorService, "CreateNetworkService"},
692 {8, &LP2PAPP::CreateMonitorService, "CreateNetworkServiceMonitor"},
693 };
694 // clang-format on
695
696 RegisterHandlers(functions);
697 }
698
699 void CreateNetworkervice(HLERequestContext& ctx) {
700 IPC::RequestParser rp{ctx};
701 const u64 reserved_input = rp.Pop<u64>();
702 const u32 input = rp.Pop<u32>();
703
704 LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input,
705 input);
706
707 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
708 rb.Push(ResultSuccess);
709 rb.PushIpcInterface<INetworkService>(system);
710 }
711
712 void CreateMonitorService(HLERequestContext& ctx) {
713 IPC::RequestParser rp{ctx};
714 const u64 reserved_input = rp.Pop<u64>();
715
716 LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input);
717
718 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
719 rb.Push(ResultSuccess);
720 rb.PushIpcInterface<INetworkServiceMonitor>(system);
721 }
722};
723
724class LP2PSYS final : public ServiceFramework<LP2PSYS> {
725public:
726 explicit LP2PSYS(Core::System& system_) : ServiceFramework{system_, "lp2p:sys"} {
727 // clang-format off
728 static const FunctionInfo functions[] = {
729 {0, &LP2PSYS::CreateMonitorService, "CreateNetworkService"},
730 {8, &LP2PSYS::CreateMonitorService, "CreateNetworkServiceMonitor"},
731 };
732 // clang-format on
733
734 RegisterHandlers(functions);
735 }
736
737 void CreateNetworkervice(HLERequestContext& ctx) {
738 IPC::RequestParser rp{ctx};
739 const u64 reserved_input = rp.Pop<u64>();
740 const u32 input = rp.Pop<u32>();
741 94
95private:
96 Result CreateNetworkService(OutInterface<ISfService> out_interface, u32 input,
97 u64 reserved_input) {
742 LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input, 98 LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input,
743 input); 99 input);
744 100
745 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 101 *out_interface = std::make_shared<ISfService>(system);
746 rb.Push(ResultSuccess); 102 R_SUCCEED();
747 rb.PushIpcInterface<INetworkService>(system);
748 } 103 }
749 104
750 void CreateMonitorService(HLERequestContext& ctx) { 105 Result CreateNetworkServiceMonitor(OutInterface<ISfServiceMonitor> out_interface,
751 IPC::RequestParser rp{ctx}; 106 u64 reserved_input) {
752 const u64 reserved_input = rp.Pop<u64>();
753
754 LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input); 107 LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input);
755 108
756 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 109 *out_interface = std::make_shared<ISfServiceMonitor>(system);
757 rb.Push(ResultSuccess); 110 R_SUCCEED();
758 rb.PushIpcInterface<INetworkServiceMonitor>(system);
759 } 111 }
760};
761 112
762class ISfMonitorService final : public ServiceFramework<ISfMonitorService> { 113 bool is_system{};
763public:
764 explicit ISfMonitorService(Core::System& system_)
765 : ServiceFramework{system_, "ISfMonitorService"} {
766 // clang-format off
767 static const FunctionInfo functions[] = {
768 {0, &ISfMonitorService::Initialize, "Initialize"},
769 {288, &ISfMonitorService::GetGroupInfo, "GetGroupInfo"},
770 {320, nullptr, "GetLinkLevel"},
771 };
772 // clang-format on
773
774 RegisterHandlers(functions);
775 }
776
777private:
778 void Initialize(HLERequestContext& ctx) {
779 LOG_WARNING(Service_LDN, "(STUBBED) called");
780
781 IPC::ResponseBuilder rb{ctx, 3};
782 rb.Push(ResultSuccess);
783 rb.Push(0);
784 }
785
786 void GetGroupInfo(HLERequestContext& ctx) {
787 LOG_WARNING(Service_LDN, "(STUBBED) called");
788
789 struct GroupInfo {
790 std::array<u8, 0x200> info;
791 };
792
793 GroupInfo group_info{};
794
795 ctx.WriteBuffer(group_info);
796 IPC::ResponseBuilder rb{ctx, 2};
797 rb.Push(ResultSuccess);
798 }
799}; 114};
800 115
801class LP2PM final : public ServiceFramework<LP2PM> { 116class ISfMonitorServiceCreator final : public ServiceFramework<ISfMonitorServiceCreator> {
802public: 117public:
803 explicit LP2PM(Core::System& system_) : ServiceFramework{system_, "lp2p:m"} { 118 explicit ISfMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "lp2p:m"} {
804 // clang-format off 119 // clang-format off
805 static const FunctionInfo functions[] = { 120 static const FunctionInfo functions[] = {
806 {0, &LP2PM::CreateMonitorService, "CreateMonitorService"}, 121 {0, C<&ISfMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"},
807 }; 122 };
808 // clang-format on 123 // clang-format on
809 124
@@ -811,28 +126,27 @@ public:
811 } 126 }
812 127
813private: 128private:
814 void CreateMonitorService(HLERequestContext& ctx) { 129 Result CreateMonitorService(OutInterface<ISfMonitorService> out_interface, u64 reserved_input) {
815 IPC::RequestParser rp{ctx};
816 const u64 reserved_input = rp.Pop<u64>();
817
818 LOG_INFO(Service_LDN, "called, reserved_input={}", reserved_input); 130 LOG_INFO(Service_LDN, "called, reserved_input={}", reserved_input);
819 131
820 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 132 *out_interface = std::make_shared<ISfMonitorService>(system);
821 rb.Push(ResultSuccess); 133 R_SUCCEED();
822 rb.PushIpcInterface<ISfMonitorService>(system);
823 } 134 }
824}; 135};
825 136
826void LoopProcess(Core::System& system) { 137void LoopProcess(Core::System& system) {
827 auto server_manager = std::make_unique<ServerManager>(system); 138 auto server_manager = std::make_unique<ServerManager>(system);
828 139
829 server_manager->RegisterNamedService("ldn:m", std::make_shared<LDNM>(system)); 140 server_manager->RegisterNamedService("ldn:m", std::make_shared<IMonitorServiceCreator>(system));
830 server_manager->RegisterNamedService("ldn:s", std::make_shared<LDNS>(system)); 141 server_manager->RegisterNamedService("ldn:s", std::make_shared<ISystemServiceCreator>(system));
831 server_manager->RegisterNamedService("ldn:u", std::make_shared<LDNU>(system)); 142 server_manager->RegisterNamedService("ldn:u", std::make_shared<IUserServiceCreator>(system));
832 143
833 server_manager->RegisterNamedService("lp2p:app", std::make_shared<LP2PAPP>(system)); 144 server_manager->RegisterNamedService(
834 server_manager->RegisterNamedService("lp2p:sys", std::make_shared<LP2PSYS>(system)); 145 "lp2p:app", std::make_shared<ISfServiceCreator>(system, false, "lp2p:app"));
835 server_manager->RegisterNamedService("lp2p:m", std::make_shared<LP2PM>(system)); 146 server_manager->RegisterNamedService(
147 "lp2p:sys", std::make_shared<ISfServiceCreator>(system, true, "lp2p:sys"));
148 server_manager->RegisterNamedService("lp2p:m",
149 std::make_shared<ISfMonitorServiceCreator>(system));
836 150
837 ServerManager::RunServer(std::move(server_manager)); 151 ServerManager::RunServer(std::move(server_manager));
838} 152}
diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h
index f4a319168..dae037fa8 100644
--- a/src/core/hle/service/ldn/ldn.h
+++ b/src/core/hle/service/ldn/ldn.h
@@ -3,12 +3,6 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/result.h"
8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/kernel_helpers.h"
10#include "core/hle/service/sm/sm.h"
11
12namespace Core { 6namespace Core {
13class System; 7class System;
14} 8}
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h
index 44c2c773b..6198aa07b 100644
--- a/src/core/hle/service/ldn/ldn_types.h
+++ b/src/core/hle/service/ldn/ldn_types.h
@@ -123,6 +123,18 @@ enum class NodeStatus : u8 {
123 Connected, 123 Connected,
124}; 124};
125 125
126enum class WirelessControllerRestriction : u32 {
127 None,
128 Default,
129};
130
131struct ConnectOption {
132 union {
133 u32 raw;
134 };
135};
136static_assert(sizeof(ConnectOption) == 0x4, "ConnectOption is an invalid size");
137
126struct NodeLatestUpdate { 138struct NodeLatestUpdate {
127 NodeStateChange state_change; 139 NodeStateChange state_change;
128 INSERT_PADDING_BYTES(0x7); // Unknown 140 INSERT_PADDING_BYTES(0x7); // Unknown
@@ -139,9 +151,9 @@ static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size");
139 151
140struct IntentId { 152struct IntentId {
141 u64 local_communication_id; 153 u64 local_communication_id;
142 INSERT_PADDING_BYTES(0x2); // Reserved 154 INSERT_PADDING_BYTES_NOINIT(0x2); // Reserved
143 u16 scene_id; 155 u16 scene_id;
144 INSERT_PADDING_BYTES(0x4); // Reserved 156 INSERT_PADDING_BYTES_NOINIT(0x4); // Reserved
145}; 157};
146static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size"); 158static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size");
147 159
@@ -152,13 +164,14 @@ struct NetworkId {
152static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size"); 164static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size");
153 165
154struct Ssid { 166struct Ssid {
155 u8 length{}; 167 u8 length;
156 std::array<char, SsidLengthMax + 1> raw{}; 168 std::array<char, SsidLengthMax + 1> raw;
157 169
158 Ssid() = default; 170 Ssid() = default;
159 171
160 constexpr explicit Ssid(std::string_view data) { 172 constexpr explicit Ssid(std::string_view data) {
161 length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); 173 length = static_cast<u8>(std::min(data.size(), SsidLengthMax));
174 raw = {};
162 data.copy(raw.data(), length); 175 data.copy(raw.data(), length);
163 raw[length] = 0; 176 raw[length] = 0;
164 } 177 }
@@ -181,7 +194,7 @@ using Ipv4Address = std::array<u8, 4>;
181static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size"); 194static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");
182 195
183struct MacAddress { 196struct MacAddress {
184 std::array<u8, 6> raw{}; 197 std::array<u8, 6> raw;
185 198
186 friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default; 199 friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default;
187}; 200};
@@ -211,7 +224,7 @@ struct CommonNetworkInfo {
211 WifiChannel channel; 224 WifiChannel channel;
212 LinkLevel link_level; 225 LinkLevel link_level;
213 PackedNetworkType network_type; 226 PackedNetworkType network_type;
214 INSERT_PADDING_BYTES(0x4); 227 INSERT_PADDING_BYTES_NOINIT(0x4);
215}; 228};
216static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size"); 229static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size");
217 230
@@ -221,9 +234,9 @@ struct NodeInfo {
221 s8 node_id; 234 s8 node_id;
222 u8 is_connected; 235 u8 is_connected;
223 std::array<u8, UserNameBytesMax + 1> user_name; 236 std::array<u8, UserNameBytesMax + 1> user_name;
224 INSERT_PADDING_BYTES(0x1); // Reserved 237 INSERT_PADDING_BYTES_NOINIT(0x1); // Reserved
225 s16 local_communication_version; 238 s16 local_communication_version;
226 INSERT_PADDING_BYTES(0x10); // Reserved 239 INSERT_PADDING_BYTES_NOINIT(0x10); // Reserved
227}; 240};
228static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size"); 241static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size");
229 242
@@ -232,14 +245,14 @@ struct LdnNetworkInfo {
232 SecurityMode security_mode; 245 SecurityMode security_mode;
233 AcceptPolicy station_accept_policy; 246 AcceptPolicy station_accept_policy;
234 u8 has_action_frame; 247 u8 has_action_frame;
235 INSERT_PADDING_BYTES(0x2); // Padding 248 INSERT_PADDING_BYTES_NOINIT(0x2); // Padding
236 u8 node_count_max; 249 u8 node_count_max;
237 u8 node_count; 250 u8 node_count;
238 std::array<NodeInfo, NodeCountMax> nodes; 251 std::array<NodeInfo, NodeCountMax> nodes;
239 INSERT_PADDING_BYTES(0x2); // Reserved 252 INSERT_PADDING_BYTES_NOINIT(0x2); // Reserved
240 u16 advertise_data_size; 253 u16 advertise_data_size;
241 std::array<u8, AdvertiseDataSizeMax> advertise_data; 254 std::array<u8, AdvertiseDataSizeMax> advertise_data;
242 INSERT_PADDING_BYTES(0x8C); // Reserved 255 INSERT_PADDING_BYTES_NOINIT(0x8C); // Reserved
243 u64 random_authentication_id; 256 u64 random_authentication_id;
244}; 257};
245static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size"); 258static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size");
@@ -250,6 +263,7 @@ struct NetworkInfo {
250 LdnNetworkInfo ldn; 263 LdnNetworkInfo ldn;
251}; 264};
252static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size"); 265static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size");
266static_assert(std::is_trivial_v<NetworkInfo>, "NetworkInfo type must be trivially copyable.");
253 267
254struct SecurityConfig { 268struct SecurityConfig {
255 SecurityMode security_mode; 269 SecurityMode security_mode;
@@ -303,4 +317,36 @@ struct AddressList {
303}; 317};
304static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size"); 318static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size");
305 319
320struct GroupInfo {
321 std::array<u8, 0x200> info;
322};
323
324struct CreateNetworkConfig {
325 SecurityConfig security_config;
326 UserConfig user_config;
327 INSERT_PADDING_BYTES(0x4);
328 NetworkConfig network_config;
329};
330static_assert(sizeof(CreateNetworkConfig) == 0x98, "CreateNetworkConfig is an invalid size");
331
332#pragma pack(push, 4)
333struct CreateNetworkConfigPrivate {
334 SecurityConfig security_config;
335 SecurityParameter security_parameter;
336 UserConfig user_config;
337 INSERT_PADDING_BYTES(0x4);
338 NetworkConfig network_config;
339};
340#pragma pack(pop)
341static_assert(sizeof(CreateNetworkConfigPrivate) == 0xB8,
342 "CreateNetworkConfigPrivate is an invalid size");
343
344struct ConnectNetworkData {
345 SecurityConfig security_config;
346 UserConfig user_config;
347 s32 local_communication_version;
348 ConnectOption option;
349};
350static_assert(sizeof(ConnectNetworkData) == 0x7c, "ConnectNetworkData is an invalid size");
351
306} // namespace Service::LDN 352} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/monitor_service.cpp b/src/core/hle/service/ldn/monitor_service.cpp
new file mode 100644
index 000000000..3471f69da
--- /dev/null
+++ b/src/core/hle/service/ldn/monitor_service.cpp
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/ldn/monitor_service.h"
6
7namespace Service::LDN {
8
9IMonitorService::IMonitorService(Core::System& system_)
10 : ServiceFramework{system_, "IMonitorService"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, C<&IMonitorService::GetStateForMonitor>, "GetStateForMonitor"},
14 {1, nullptr, "GetNetworkInfoForMonitor"},
15 {2, nullptr, "GetIpv4AddressForMonitor"},
16 {3, nullptr, "GetDisconnectReasonForMonitor"},
17 {4, nullptr, "GetSecurityParameterForMonitor"},
18 {5, nullptr, "GetNetworkConfigForMonitor"},
19 {100, C<&IMonitorService::InitializeMonitor>, "InitializeMonitor"},
20 {101, nullptr, "FinalizeMonitor"},
21 };
22 // clang-format on
23
24 RegisterHandlers(functions);
25}
26
27IMonitorService::~IMonitorService() = default;
28
29Result IMonitorService::GetStateForMonitor(Out<State> out_state) {
30 LOG_INFO(Service_LDN, "called");
31
32 *out_state = state;
33 R_SUCCEED();
34}
35
36Result IMonitorService::InitializeMonitor() {
37 LOG_INFO(Service_LDN, "called");
38
39 state = State::Initialized;
40 R_SUCCEED();
41}
42
43} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/monitor_service.h b/src/core/hle/service/ldn/monitor_service.h
new file mode 100644
index 000000000..61aacef30
--- /dev/null
+++ b/src/core/hle/service/ldn/monitor_service.h
@@ -0,0 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/ldn/ldn_types.h"
8#include "core/hle/service/service.h"
9
10namespace Core {
11class System;
12}
13
14namespace Service::LDN {
15
16class IMonitorService final : public ServiceFramework<IMonitorService> {
17public:
18 explicit IMonitorService(Core::System& system_);
19 ~IMonitorService() override;
20
21private:
22 Result GetStateForMonitor(Out<State> out_state);
23 Result InitializeMonitor();
24
25 State state{State::None};
26};
27
28} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_monitor_service.cpp b/src/core/hle/service/ldn/sf_monitor_service.cpp
new file mode 100644
index 000000000..9e6736ff2
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_monitor_service.cpp
@@ -0,0 +1,40 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/ldn/ldn_types.h"
6#include "core/hle/service/ldn/sf_monitor_service.h"
7
8namespace Service::LDN {
9
10ISfMonitorService::ISfMonitorService(Core::System& system_)
11 : ServiceFramework{system_, "ISfMonitorService"} {
12 // clang-format off
13 static const FunctionInfo functions[] = {
14 {0, C<&ISfMonitorService::Initialize>, "Initialize"},
15 {288, C<&ISfMonitorService::GetGroupInfo>, "GetGroupInfo"},
16 {320, nullptr, "GetLinkLevel"},
17 };
18 // clang-format on
19
20 RegisterHandlers(functions);
21}
22
23ISfMonitorService::~ISfMonitorService() = default;
24
25Result ISfMonitorService::Initialize(Out<u32> out_value) {
26 LOG_WARNING(Service_LDN, "(STUBBED) called");
27
28 *out_value = 0;
29 R_SUCCEED();
30}
31
32Result ISfMonitorService::GetGroupInfo(
33 OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) {
34 LOG_WARNING(Service_LDN, "(STUBBED) called");
35
36 *out_group_info = GroupInfo{};
37 R_SUCCEED();
38}
39
40} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_monitor_service.h b/src/core/hle/service/ldn/sf_monitor_service.h
new file mode 100644
index 000000000..d02115201
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_monitor_service.h
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::LDN {
14struct GroupInfo;
15
16class ISfMonitorService final : public ServiceFramework<ISfMonitorService> {
17public:
18 explicit ISfMonitorService(Core::System& system_);
19 ~ISfMonitorService() override;
20
21private:
22 Result Initialize(Out<u32> out_value);
23 Result GetGroupInfo(OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info);
24};
25
26} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_service.cpp b/src/core/hle/service/ldn/sf_service.cpp
new file mode 100644
index 000000000..61cabe219
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_service.cpp
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/ldn/sf_service.h"
5
6namespace Service::LDN {
7
8ISfService::ISfService(Core::System& system_) : ServiceFramework{system_, "ISfService"} {
9 // clang-format off
10 static const FunctionInfo functions[] = {
11 {0, nullptr, "Initialize"},
12 {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"},
13 {264, nullptr, "GetNetworkInterfaceLastError"},
14 {272, nullptr, "GetRole"},
15 {280, nullptr, "GetAdvertiseData"},
16 {288, nullptr, "GetGroupInfo"},
17 {296, nullptr, "GetGroupInfo2"},
18 {304, nullptr, "GetGroupOwner"},
19 {312, nullptr, "GetIpConfig"},
20 {320, nullptr, "GetLinkLevel"},
21 {512, nullptr, "Scan"},
22 {768, nullptr, "CreateGroup"},
23 {776, nullptr, "DestroyGroup"},
24 {784, nullptr, "SetAdvertiseData"},
25 {1536, nullptr, "SendToOtherGroup"},
26 {1544, nullptr, "RecvFromOtherGroup"},
27 {1552, nullptr, "AddAcceptableGroupId"},
28 {1560, nullptr, "ClearAcceptableGroupId"},
29 };
30 // clang-format on
31
32 RegisterHandlers(functions);
33}
34
35ISfService::~ISfService() = default;
36
37} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_service.h b/src/core/hle/service/ldn/sf_service.h
new file mode 100644
index 000000000..05534b567
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_service.h
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::LDN {
14
15class ISfService final : public ServiceFramework<ISfService> {
16public:
17 explicit ISfService(Core::System& system_);
18 ~ISfService() override;
19};
20
21} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_service_monitor.cpp b/src/core/hle/service/ldn/sf_service_monitor.cpp
new file mode 100644
index 000000000..33e3c1d69
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_service_monitor.cpp
@@ -0,0 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/ldn/ldn_types.h"
6#include "core/hle/service/ldn/sf_service_monitor.h"
7
8namespace Service::LDN {
9
10ISfServiceMonitor::ISfServiceMonitor(Core::System& system_)
11 : ServiceFramework{system_, "ISfServiceMonitor"} {
12 // clang-format off
13 static const FunctionInfo functions[] = {
14 {0, C<&ISfServiceMonitor::Initialize>, "Initialize"},
15 {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"},
16 {264, nullptr, "GetNetworkInterfaceLastError"},
17 {272, nullptr, "GetRole"},
18 {280, nullptr, "GetAdvertiseData"},
19 {281, nullptr, "GetAdvertiseData2"},
20 {288, C<&ISfServiceMonitor::GetGroupInfo>, "GetGroupInfo"},
21 {296, nullptr, "GetGroupInfo2"},
22 {304, nullptr, "GetGroupOwner"},
23 {312, nullptr, "GetIpConfig"},
24 {320, nullptr, "GetLinkLevel"},
25 {328, nullptr, "AttachJoinEvent"},
26 {336, nullptr, "GetMembers"},
27 };
28 // clang-format on
29
30 RegisterHandlers(functions);
31}
32
33ISfServiceMonitor::~ISfServiceMonitor() = default;
34
35Result ISfServiceMonitor::Initialize(Out<u32> out_value) {
36 LOG_WARNING(Service_LDN, "(STUBBED) called");
37
38 *out_value = 0;
39 R_SUCCEED();
40}
41
42Result ISfServiceMonitor::GetGroupInfo(
43 OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) {
44 LOG_WARNING(Service_LDN, "(STUBBED) called");
45
46 *out_group_info = GroupInfo{};
47 R_SUCCEED();
48}
49
50} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/sf_service_monitor.h b/src/core/hle/service/ldn/sf_service_monitor.h
new file mode 100644
index 000000000..3cfc5005e
--- /dev/null
+++ b/src/core/hle/service/ldn/sf_service_monitor.h
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::LDN {
14struct GroupInfo;
15
16class ISfServiceMonitor final : public ServiceFramework<ISfServiceMonitor> {
17public:
18 explicit ISfServiceMonitor(Core::System& system_);
19 ~ISfServiceMonitor() override;
20
21private:
22 Result Initialize(Out<u32> out_value);
23 Result GetGroupInfo(OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info);
24};
25
26} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/system_local_communication_service.cpp b/src/core/hle/service/ldn/system_local_communication_service.cpp
new file mode 100644
index 000000000..7b52223cd
--- /dev/null
+++ b/src/core/hle/service/ldn/system_local_communication_service.cpp
@@ -0,0 +1,56 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/ldn/system_local_communication_service.h"
6
7namespace Service::LDN {
8
9ISystemLocalCommunicationService::ISystemLocalCommunicationService(Core::System& system_)
10 : ServiceFramework{system_, "ISystemLocalCommunicationService"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, nullptr, "GetState"},
14 {1, nullptr, "GetNetworkInfo"},
15 {2, nullptr, "GetIpv4Address"},
16 {3, nullptr, "GetDisconnectReason"},
17 {4, nullptr, "GetSecurityParameter"},
18 {5, nullptr, "GetNetworkConfig"},
19 {100, nullptr, "AttachStateChangeEvent"},
20 {101, nullptr, "GetNetworkInfoLatestUpdate"},
21 {102, nullptr, "Scan"},
22 {103, nullptr, "ScanPrivate"},
23 {104, nullptr, "SetWirelessControllerRestriction"},
24 {200, nullptr, "OpenAccessPoint"},
25 {201, nullptr, "CloseAccessPoint"},
26 {202, nullptr, "CreateNetwork"},
27 {203, nullptr, "CreateNetworkPrivate"},
28 {204, nullptr, "DestroyNetwork"},
29 {205, nullptr, "Reject"},
30 {206, nullptr, "SetAdvertiseData"},
31 {207, nullptr, "SetStationAcceptPolicy"},
32 {208, nullptr, "AddAcceptFilterEntry"},
33 {209, nullptr, "ClearAcceptFilter"},
34 {300, nullptr, "OpenStation"},
35 {301, nullptr, "CloseStation"},
36 {302, nullptr, "Connect"},
37 {303, nullptr, "ConnectPrivate"},
38 {304, nullptr, "Disconnect"},
39 {400, nullptr, "InitializeSystem"},
40 {401, nullptr, "FinalizeSystem"},
41 {402, nullptr, "SetOperationMode"},
42 {403, C<&ISystemLocalCommunicationService::InitializeSystem2>, "InitializeSystem2"},
43 };
44 // clang-format on
45
46 RegisterHandlers(functions);
47}
48
49ISystemLocalCommunicationService::~ISystemLocalCommunicationService() = default;
50
51Result ISystemLocalCommunicationService::InitializeSystem2() {
52 LOG_WARNING(Service_LDN, "(STUBBED) called");
53 R_SUCCEED();
54}
55
56} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/system_local_communication_service.h b/src/core/hle/service/ldn/system_local_communication_service.h
new file mode 100644
index 000000000..a02b097ea
--- /dev/null
+++ b/src/core/hle/service/ldn/system_local_communication_service.h
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::LDN {
14
15class ISystemLocalCommunicationService final
16 : public ServiceFramework<ISystemLocalCommunicationService> {
17public:
18 explicit ISystemLocalCommunicationService(Core::System& system_);
19 ~ISystemLocalCommunicationService() override;
20
21private:
22 Result InitializeSystem2();
23};
24
25} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/user_local_communication_service.cpp b/src/core/hle/service/ldn/user_local_communication_service.cpp
new file mode 100644
index 000000000..f28368962
--- /dev/null
+++ b/src/core/hle/service/ldn/user_local_communication_service.cpp
@@ -0,0 +1,320 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <memory>
5
6#include "core/core.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/cmif_serialization.h"
9#include "core/hle/service/ldn/ldn_results.h"
10#include "core/hle/service/ldn/ldn_types.h"
11#include "core/hle/service/ldn/user_local_communication_service.h"
12#include "core/hle/service/server_manager.h"
13#include "core/internal_network/network.h"
14#include "core/internal_network/network_interface.h"
15#include "network/network.h"
16
17// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
18#undef CreateEvent
19
20namespace Service::LDN {
21
22IUserLocalCommunicationService::IUserLocalCommunicationService(Core::System& system_)
23 : ServiceFramework{system_, "IUserLocalCommunicationService"},
24 service_context{system, "IUserLocalCommunicationService"},
25 room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} {
26 // clang-format off
27 static const FunctionInfo functions[] = {
28 {0, C<&IUserLocalCommunicationService::GetState>, "GetState"},
29 {1, C<&IUserLocalCommunicationService::GetNetworkInfo>, "GetNetworkInfo"},
30 {2, C<&IUserLocalCommunicationService::GetIpv4Address>, "GetIpv4Address"},
31 {3, C<&IUserLocalCommunicationService::GetDisconnectReason>, "GetDisconnectReason"},
32 {4, C<&IUserLocalCommunicationService::GetSecurityParameter>, "GetSecurityParameter"},
33 {5, C<&IUserLocalCommunicationService::GetNetworkConfig>, "GetNetworkConfig"},
34 {100, C<&IUserLocalCommunicationService::AttachStateChangeEvent>, "AttachStateChangeEvent"},
35 {101, C<&IUserLocalCommunicationService::GetNetworkInfoLatestUpdate>, "GetNetworkInfoLatestUpdate"},
36 {102, C<&IUserLocalCommunicationService::Scan>, "Scan"},
37 {103, C<&IUserLocalCommunicationService::ScanPrivate>, "ScanPrivate"},
38 {104, C<&IUserLocalCommunicationService::SetWirelessControllerRestriction>, "SetWirelessControllerRestriction"},
39 {200, C<&IUserLocalCommunicationService::OpenAccessPoint>, "OpenAccessPoint"},
40 {201, C<&IUserLocalCommunicationService::CloseAccessPoint>, "CloseAccessPoint"},
41 {202, C<&IUserLocalCommunicationService::CreateNetwork>, "CreateNetwork"},
42 {203, C<&IUserLocalCommunicationService::CreateNetworkPrivate>, "CreateNetworkPrivate"},
43 {204, C<&IUserLocalCommunicationService::DestroyNetwork>, "DestroyNetwork"},
44 {205, nullptr, "Reject"},
45 {206, C<&IUserLocalCommunicationService::SetAdvertiseData>, "SetAdvertiseData"},
46 {207, C<&IUserLocalCommunicationService::SetStationAcceptPolicy>, "SetStationAcceptPolicy"},
47 {208, C<&IUserLocalCommunicationService::AddAcceptFilterEntry>, "AddAcceptFilterEntry"},
48 {209, nullptr, "ClearAcceptFilter"},
49 {300, C<&IUserLocalCommunicationService::OpenStation>, "OpenStation"},
50 {301, C<&IUserLocalCommunicationService::CloseStation>, "CloseStation"},
51 {302, C<&IUserLocalCommunicationService::Connect>, "Connect"},
52 {303, nullptr, "ConnectPrivate"},
53 {304, C<&IUserLocalCommunicationService::Disconnect>, "Disconnect"},
54 {400, C<&IUserLocalCommunicationService::Initialize>, "Initialize"},
55 {401, C<&IUserLocalCommunicationService::Finalize>, "Finalize"},
56 {402, C<&IUserLocalCommunicationService::Initialize2>, "Initialize2"},
57 };
58 // clang-format on
59
60 RegisterHandlers(functions);
61
62 state_change_event =
63 service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent");
64}
65
66IUserLocalCommunicationService::~IUserLocalCommunicationService() {
67 if (is_initialized) {
68 if (auto room_member = room_network.GetRoomMember().lock()) {
69 room_member->Unbind(ldn_packet_received);
70 }
71 }
72
73 service_context.CloseEvent(state_change_event);
74}
75
76Result IUserLocalCommunicationService::GetState(Out<State> out_state) {
77 *out_state = State::Error;
78
79 if (is_initialized) {
80 *out_state = lan_discovery.GetState();
81 }
82
83 LOG_INFO(Service_LDN, "called, state={}", *out_state);
84
85 R_SUCCEED();
86}
87
88Result IUserLocalCommunicationService::GetNetworkInfo(
89 OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info) {
90 LOG_INFO(Service_LDN, "called");
91
92 R_RETURN(lan_discovery.GetNetworkInfo(*out_network_info));
93}
94
95Result IUserLocalCommunicationService::GetIpv4Address(Out<Ipv4Address> out_current_address,
96 Out<Ipv4Address> out_subnet_mask) {
97 LOG_INFO(Service_LDN, "called");
98 const auto network_interface = Network::GetSelectedNetworkInterface();
99
100 R_UNLESS(network_interface.has_value(), ResultNoIpAddress);
101
102 *out_current_address = {Network::TranslateIPv4(network_interface->ip_address)};
103 *out_subnet_mask = {Network::TranslateIPv4(network_interface->subnet_mask)};
104
105 // When we're connected to a room, spoof the hosts IP address
106 if (auto room_member = room_network.GetRoomMember().lock()) {
107 if (room_member->IsConnected()) {
108 *out_current_address = room_member->GetFakeIpAddress();
109 }
110 }
111
112 std::reverse(std::begin(*out_current_address), std::end(*out_current_address)); // ntohl
113 std::reverse(std::begin(*out_subnet_mask), std::end(*out_subnet_mask)); // ntohl
114 R_SUCCEED();
115}
116
117Result IUserLocalCommunicationService::GetDisconnectReason(
118 Out<DisconnectReason> out_disconnect_reason) {
119 LOG_INFO(Service_LDN, "called");
120
121 *out_disconnect_reason = lan_discovery.GetDisconnectReason();
122 R_SUCCEED();
123}
124
125Result IUserLocalCommunicationService::GetSecurityParameter(
126 Out<SecurityParameter> out_security_parameter) {
127 LOG_INFO(Service_LDN, "called");
128
129 NetworkInfo info{};
130 R_TRY(lan_discovery.GetNetworkInfo(info));
131
132 out_security_parameter->session_id = info.network_id.session_id;
133 std::memcpy(out_security_parameter->data.data(), info.ldn.security_parameter.data(),
134 sizeof(SecurityParameter::data));
135 R_SUCCEED();
136}
137
138Result IUserLocalCommunicationService::GetNetworkConfig(Out<NetworkConfig> out_network_config) {
139 LOG_INFO(Service_LDN, "called");
140
141 NetworkInfo info{};
142 R_TRY(lan_discovery.GetNetworkInfo(info));
143
144 out_network_config->intent_id = info.network_id.intent_id;
145 out_network_config->channel = info.common.channel;
146 out_network_config->node_count_max = info.ldn.node_count_max;
147 out_network_config->local_communication_version = info.ldn.nodes[0].local_communication_version;
148 R_SUCCEED();
149}
150
151Result IUserLocalCommunicationService::AttachStateChangeEvent(
152 OutCopyHandle<Kernel::KReadableEvent> out_event) {
153 LOG_INFO(Service_LDN, "called");
154
155 *out_event = &state_change_event->GetReadableEvent();
156 R_SUCCEED();
157}
158
159Result IUserLocalCommunicationService::GetNetworkInfoLatestUpdate(
160 OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info,
161 OutArray<NodeLatestUpdate, BufferAttr_HipcPointer> out_node_latest_update) {
162 LOG_INFO(Service_LDN, "called");
163
164 R_UNLESS(!out_node_latest_update.empty(), ResultBadInput);
165
166 R_RETURN(lan_discovery.GetNetworkInfo(*out_network_info, out_node_latest_update));
167}
168
169Result IUserLocalCommunicationService::Scan(
170 Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
171 OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info) {
172 LOG_INFO(Service_LDN, "called, channel={}, filter_scan_flag={}, filter_network_type={}",
173 channel, scan_filter.flag, scan_filter.network_type);
174
175 R_UNLESS(!out_network_info.empty(), ResultBadInput);
176 R_RETURN(lan_discovery.Scan(out_network_info, *network_count, scan_filter));
177}
178
179Result IUserLocalCommunicationService::ScanPrivate(
180 Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
181 OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info) {
182 LOG_INFO(Service_LDN, "called, channel={}, filter_scan_flag={}, filter_network_type={}",
183 channel, scan_filter.flag, scan_filter.network_type);
184
185 R_UNLESS(out_network_info.empty(), ResultBadInput);
186 R_RETURN(lan_discovery.Scan(out_network_info, *network_count, scan_filter));
187}
188
189Result IUserLocalCommunicationService::SetWirelessControllerRestriction(
190 WirelessControllerRestriction wireless_restriction) {
191 LOG_WARNING(Service_LDN, "(STUBBED) called");
192 R_SUCCEED();
193}
194
195Result IUserLocalCommunicationService::OpenAccessPoint() {
196 LOG_INFO(Service_LDN, "called");
197
198 R_RETURN(lan_discovery.OpenAccessPoint());
199}
200
201Result IUserLocalCommunicationService::CloseAccessPoint() {
202 LOG_INFO(Service_LDN, "called");
203
204 R_RETURN(lan_discovery.CloseAccessPoint());
205}
206
207Result IUserLocalCommunicationService::CreateNetwork(const CreateNetworkConfig& create_config) {
208 LOG_INFO(Service_LDN, "called");
209
210 R_RETURN(lan_discovery.CreateNetwork(create_config.security_config, create_config.user_config,
211 create_config.network_config));
212}
213
214Result IUserLocalCommunicationService::CreateNetworkPrivate(
215 const CreateNetworkConfigPrivate& create_config,
216 InArray<AddressEntry, BufferAttr_HipcPointer> address_list) {
217 LOG_INFO(Service_LDN, "called");
218
219 R_RETURN(lan_discovery.CreateNetwork(create_config.security_config, create_config.user_config,
220 create_config.network_config));
221}
222
223Result IUserLocalCommunicationService::DestroyNetwork() {
224 LOG_INFO(Service_LDN, "called");
225
226 R_RETURN(lan_discovery.DestroyNetwork());
227}
228
229Result IUserLocalCommunicationService::SetAdvertiseData(
230 InBuffer<BufferAttr_HipcAutoSelect> buffer_data) {
231 LOG_INFO(Service_LDN, "called");
232
233 R_RETURN(lan_discovery.SetAdvertiseData(buffer_data));
234}
235
236Result IUserLocalCommunicationService::SetStationAcceptPolicy(AcceptPolicy accept_policy) {
237 LOG_WARNING(Service_LDN, "(STUBBED) called");
238 R_SUCCEED();
239}
240
241Result IUserLocalCommunicationService::AddAcceptFilterEntry(MacAddress mac_address) {
242 LOG_WARNING(Service_LDN, "(STUBBED) called");
243 R_SUCCEED();
244}
245
246Result IUserLocalCommunicationService::OpenStation() {
247 LOG_INFO(Service_LDN, "called");
248
249 R_RETURN(lan_discovery.OpenStation());
250}
251
252Result IUserLocalCommunicationService::CloseStation() {
253 LOG_INFO(Service_LDN, "called");
254
255 R_RETURN(lan_discovery.CloseStation());
256}
257
258Result IUserLocalCommunicationService::Connect(
259 const ConnectNetworkData& connect_data,
260 InLargeData<NetworkInfo, BufferAttr_HipcPointer> network_info) {
261 LOG_INFO(Service_LDN,
262 "called, passphrase_size={}, security_mode={}, "
263 "local_communication_version={}",
264 connect_data.security_config.passphrase_size,
265 connect_data.security_config.security_mode, connect_data.local_communication_version);
266
267 R_RETURN(lan_discovery.Connect(*network_info, connect_data.user_config,
268 static_cast<u16>(connect_data.local_communication_version)));
269}
270
271Result IUserLocalCommunicationService::Disconnect() {
272 LOG_INFO(Service_LDN, "called");
273
274 R_RETURN(lan_discovery.Disconnect());
275}
276
277Result IUserLocalCommunicationService::Initialize(ClientProcessId aruid) {
278 LOG_INFO(Service_LDN, "called, process_id={}", aruid.pid);
279
280 const auto network_interface = Network::GetSelectedNetworkInterface();
281 R_UNLESS(network_interface, ResultAirplaneModeEnabled);
282
283 if (auto room_member = room_network.GetRoomMember().lock()) {
284 ldn_packet_received = room_member->BindOnLdnPacketReceived(
285 [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); });
286 } else {
287 LOG_ERROR(Service_LDN, "Couldn't bind callback!");
288 R_RETURN(ResultAirplaneModeEnabled);
289 }
290
291 lan_discovery.Initialize([&]() { OnEventFired(); });
292 is_initialized = true;
293 R_SUCCEED();
294}
295
296Result IUserLocalCommunicationService::Finalize() {
297 LOG_INFO(Service_LDN, "called");
298 if (auto room_member = room_network.GetRoomMember().lock()) {
299 room_member->Unbind(ldn_packet_received);
300 }
301
302 is_initialized = false;
303
304 R_RETURN(lan_discovery.Finalize());
305}
306
307Result IUserLocalCommunicationService::Initialize2(u32 version, ClientProcessId process_id) {
308 LOG_INFO(Service_LDN, "called, version={}, process_id={}", version, process_id.pid);
309 R_RETURN(Initialize(process_id));
310}
311
312void IUserLocalCommunicationService::OnLDNPacketReceived(const Network::LDNPacket& packet) {
313 lan_discovery.ReceivePacket(packet);
314}
315
316void IUserLocalCommunicationService::OnEventFired() {
317 state_change_event->Signal();
318}
319
320} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/user_local_communication_service.h b/src/core/hle/service/ldn/user_local_communication_service.h
new file mode 100644
index 000000000..6698d10d2
--- /dev/null
+++ b/src/core/hle/service/ldn/user_local_communication_service.h
@@ -0,0 +1,103 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/ldn/lan_discovery.h"
9#include "core/hle/service/ldn/ldn_types.h"
10#include "core/hle/service/service.h"
11
12namespace Core {
13class System;
14}
15
16namespace Network {
17class RoomNetwork;
18}
19
20namespace Service::LDN {
21
22class IUserLocalCommunicationService final
23 : public ServiceFramework<IUserLocalCommunicationService> {
24public:
25 explicit IUserLocalCommunicationService(Core::System& system_);
26 ~IUserLocalCommunicationService() override;
27
28private:
29 Result GetState(Out<State> out_state);
30
31 Result GetNetworkInfo(OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info);
32
33 Result GetIpv4Address(Out<Ipv4Address> out_current_address, Out<Ipv4Address> out_subnet_mask);
34
35 Result GetDisconnectReason(Out<DisconnectReason> out_disconnect_reason);
36
37 Result GetSecurityParameter(Out<SecurityParameter> out_security_parameter);
38
39 Result GetNetworkConfig(Out<NetworkConfig> out_network_config);
40
41 Result AttachStateChangeEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
42
43 Result GetNetworkInfoLatestUpdate(
44 OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info,
45 OutArray<NodeLatestUpdate, BufferAttr_HipcPointer> out_node_latest_update);
46
47 Result Scan(Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
48 OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info);
49
50 Result ScanPrivate(Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
51 OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info);
52
53 Result SetWirelessControllerRestriction(WirelessControllerRestriction wireless_restriction);
54
55 Result OpenAccessPoint();
56
57 Result CloseAccessPoint();
58
59 Result CreateNetwork(const CreateNetworkConfig& create_network_Config);
60
61 Result CreateNetworkPrivate(const CreateNetworkConfigPrivate& create_network_Config,
62 InArray<AddressEntry, BufferAttr_HipcPointer> address_list);
63
64 Result DestroyNetwork();
65
66 Result SetAdvertiseData(InBuffer<BufferAttr_HipcAutoSelect> buffer_data);
67
68 Result SetStationAcceptPolicy(AcceptPolicy accept_policy);
69
70 Result AddAcceptFilterEntry(MacAddress mac_address);
71
72 Result OpenStation();
73
74 Result CloseStation();
75
76 Result Connect(const ConnectNetworkData& connect_data,
77 InLargeData<NetworkInfo, BufferAttr_HipcPointer> network_info);
78
79 Result Disconnect();
80
81 Result Initialize(ClientProcessId aruid);
82
83 Result Finalize();
84
85 Result Initialize2(u32 version, ClientProcessId aruid);
86
87private:
88 /// Callback to parse and handle a received LDN packet.
89 void OnLDNPacketReceived(const Network::LDNPacket& packet);
90 void OnEventFired();
91
92 KernelHelpers::ServiceContext service_context;
93 Kernel::KEvent* state_change_event;
94 Network::RoomNetwork& room_network;
95 LANDiscovery lan_discovery;
96
97 // Callback identifier for the OnLDNPacketReceived event.
98 Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received;
99
100 bool is_initialized{};
101};
102
103} // namespace Service::LDN
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index b72788c6d..9444becce 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -42,6 +42,7 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
42 }; 42 };
43 } 43 }
44 rescaleable = false; 44 rescaleable = false;
45 is_sparse = config.is_sparse != 0;
45 tile_width_spacing = config.tile_width_spacing; 46 tile_width_spacing = config.tile_width_spacing;
46 if (config.texture_type != TextureType::Texture2D && 47 if (config.texture_type != TextureType::Texture2D &&
47 config.texture_type != TextureType::Texture2DNoMipmap) { 48 config.texture_type != TextureType::Texture2DNoMipmap) {
diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h
index 8a4cb0cbd..eb490a642 100644
--- a/src/video_core/texture_cache/image_info.h
+++ b/src/video_core/texture_cache/image_info.h
@@ -41,6 +41,7 @@ struct ImageInfo {
41 bool downscaleable = false; 41 bool downscaleable = false;
42 bool forced_flushed = false; 42 bool forced_flushed = false;
43 bool dma_downloaded = false; 43 bool dma_downloaded = false;
44 bool is_sparse = false;
44}; 45};
45 46
46} // namespace VideoCommon 47} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 3a1cc060e..01c3561c9 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -600,17 +600,17 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz
600 [&](ImageId id, Image&) { deleted_images.push_back(id); }); 600 [&](ImageId id, Image&) { deleted_images.push_back(id); });
601 for (const ImageId id : deleted_images) { 601 for (const ImageId id : deleted_images) {
602 Image& image = slot_images[id]; 602 Image& image = slot_images[id];
603 if (True(image.flags & ImageFlagBits::CpuModified)) { 603 if (False(image.flags & ImageFlagBits::CpuModified)) {
604 continue; 604 image.flags |= ImageFlagBits::CpuModified;
605 if (True(image.flags & ImageFlagBits::Tracked)) {
606 UntrackImage(image, id);
607 }
605 } 608 }
606 image.flags |= ImageFlagBits::CpuModified; 609
607 if (True(image.flags & ImageFlagBits::Remapped)) { 610 if (True(image.flags & ImageFlagBits::Remapped)) {
608 continue; 611 continue;
609 } 612 }
610 image.flags |= ImageFlagBits::Remapped; 613 image.flags |= ImageFlagBits::Remapped;
611 if (True(image.flags & ImageFlagBits::Tracked)) {
612 UntrackImage(image, id);
613 }
614 } 614 }
615} 615}
616 616
@@ -1469,7 +1469,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA
1469 const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr); 1469 const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr);
1470 Image& new_image = slot_images[new_image_id]; 1470 Image& new_image = slot_images[new_image_id];
1471 1471
1472 if (!gpu_memory->IsContinuousRange(new_image.gpu_addr, new_image.guest_size_bytes)) { 1472 if (!gpu_memory->IsContinuousRange(new_image.gpu_addr, new_image.guest_size_bytes) &&
1473 new_info.is_sparse) {
1473 new_image.flags |= ImageFlagBits::Sparse; 1474 new_image.flags |= ImageFlagBits::Sparse;
1474 } 1475 }
1475 1476
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index ce65b2bf1..d138b53c8 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -54,13 +54,28 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
54 QStringLiteral()); 54 QStringLiteral());
55 55
56 // Core 56 // Core
57 INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), QStringLiteral()); 57 INSERT(
58 INSERT(Settings, memory_layout_mode, tr("Memory Layout"), QStringLiteral()); 58 Settings, use_multi_core, tr("Multicore CPU Emulation"),
59 tr("This option increases CPU emulation thread use from 1 to the Switch’s maximum of 4.\n"
60 "This is mainly a debug option and shouldn’t be disabled."));
61 INSERT(
62 Settings, memory_layout_mode, tr("Memory Layout"),
63 tr("Increases the amount of emulated RAM from the stock 4GB of the retail Switch to the "
64 "developer kit's 8/6GB.\nIt’s doesn’t improve stability or performance and is intended "
65 "to let big texture mods fit in emulated RAM.\nEnabling it will increase memory "
66 "use. It is not recommended to enable unless a specific game with a texture mod needs "
67 "it."));
59 INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral()); 68 INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral());
60 INSERT(Settings, speed_limit, tr("Limit Speed Percent"), QStringLiteral()); 69 INSERT(Settings, speed_limit, tr("Limit Speed Percent"),
70 tr("Controls the game's maximum rendering speed, but it’s up to each game if it runs "
71 "faster or not.\n200% for a 30 FPS game is 60 FPS, and for a "
72 "60 FPS game it will be 120 FPS.\nDisabling it means unlocking the framerate to the "
73 "maximum your PC can reach."));
61 74
62 // Cpu 75 // Cpu
63 INSERT(Settings, cpu_accuracy, tr("Accuracy:"), QStringLiteral()); 76 INSERT(Settings, cpu_accuracy, tr("Accuracy:"),
77 tr("This setting controls the accuracy of the emulated CPU.\nDon't change this unless "
78 "you know what you are doing."));
64 INSERT(Settings, cpu_backend, tr("Backend:"), QStringLiteral()); 79 INSERT(Settings, cpu_backend, tr("Backend:"), QStringLiteral());
65 80
66 // Cpu Debug 81 // Cpu Debug
@@ -80,34 +95,75 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
80 tr("This option improves the speed of 32 bits ASIMD floating-point functions by running " 95 tr("This option improves the speed of 32 bits ASIMD floating-point functions by running "
81 "with incorrect rounding modes.")); 96 "with incorrect rounding modes."));
82 INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"), 97 INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"),
83 tr("This option improves speed by removing NaN checking. Please note this also reduces " 98 tr("This option improves speed by removing NaN checking.\nPlease note this also reduces "
84 "accuracy of certain floating-point instructions.")); 99 "accuracy of certain floating-point instructions."));
85 INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"), 100 INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"),
86 tr("This option improves speed by eliminating a safety check before every memory " 101 tr("This option improves speed by eliminating a safety check before every memory "
87 "read/write " 102 "read/write in guest.\nDisabling it may allow a game to read/write the emulator's "
88 "in guest. Disabling it may allow a game to read/write the emulator's memory.")); 103 "memory."));
89 INSERT( 104 INSERT(
90 Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"), 105 Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"),
91 tr("This option improves speed by relying only on the semantics of cmpxchg to ensure " 106 tr("This option improves speed by relying only on the semantics of cmpxchg to ensure "
92 "safety of exclusive access instructions. Please note this may result in deadlocks and " 107 "safety of exclusive access instructions.\nPlease note this may result in deadlocks and "
93 "other race conditions.")); 108 "other race conditions."));
94 109
95 // Renderer 110 // Renderer
96 INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral()); 111 INSERT(
97 INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral()); 112 Settings, renderer_backend, tr("API:"),
98 INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral()); 113 tr("Switches between the available graphics APIs.\nVulkan is recommended in most cases."));
99 INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral()); 114 INSERT(Settings, vulkan_device, tr("Device:"),
115 tr("This setting selects the GPU to use with the Vulkan backend."));
116 INSERT(Settings, shader_backend, tr("Shader Backend:"),
117 tr("The shader backend to use for the OpenGL renderer.\nGLSL is the fastest in "
118 "performance and the best in rendering accuracy.\n"
119 "GLASM is a deprecated NVIDIA-only backend that offers much better shader building "
120 "performance at the cost of FPS and rendering accuracy.\n"
121 "SPIR-V compiles the fastest, but yields poor results on most GPU drivers."));
122 INSERT(Settings, resolution_setup, tr("Resolution:"),
123 tr("Forces the game to render at a different resolution.\nHigher resolutions require "
124 "much more VRAM and bandwidth.\n"
125 "Options lower than 1X can cause rendering issues."));
100 INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral()); 126 INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral());
101 INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral()); 127 INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"),
102 INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral()); 128 tr("Determines how sharpened the image will look while using FSR’s dynamic contrast."));
103 INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral()); 129 INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"),
104 INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral()); 130 tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA has a "
105 INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral()); 131 "lower performance impact and can produce a better and more stable picture under "
106 INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"), 132 "very low resolutions."));
107 QStringLiteral()); 133 INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"),
108 INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral()); 134 tr("The method used to render the window in fullscreen.\nBorderless offers the best "
109 INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral()); 135 "compatibility with the on-screen keyboard that some games request for "
110 INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), QStringLiteral()); 136 "input.\nExclusive "
137 "fullscreen may offer better performance and better Freesync/Gsync support."));
138 INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"),
139 tr("Stretches the game to fit the specified aspect ratio.\nSwitch games only support "
140 "16:9, so custom game mods are required to get other ratios.\nAlso controls the "
141 "aspect ratio of captured screenshots."));
142 INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"),
143 tr("Allows saving shaders to storage for faster loading on following game "
144 "boots.\nDisabling "
145 "it is only intended for debugging."));
146 INSERT(
147 Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
148 tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
149 INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"),
150 tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for "
151 "decoding, or perform no decoding at all (black screen on videos).\n"
152 "In most cases, GPU decoding provides the best performance."));
153 INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"),
154 tr("This option controls how ASTC textures should be decoded.\n"
155 "CPU: Use the CPU for decoding, slowest but safest method.\n"
156 "GPU: Use the GPU's compute shaders to decode ASTC textures, recommended for most "
157 "games and users.\n"
158 "CPU Asynchronously: Use the CPU to decode ASTC textures as they arrive. Completely "
159 "eliminates ASTC decoding\nstuttering at the cost of rendering issues while the "
160 "texture is being decoded."));
161 INSERT(
162 Settings, astc_recompression, tr("ASTC Recompression Method:"),
163 tr("Almost all desktop and laptop dedicated GPUs lack support for ASTC textures, forcing "
164 "the emulator to decompress to an intermediate format any card supports, RGBA8.\n"
165 "This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but "
166 "negatively affecting image quality."));
111 INSERT( 167 INSERT(
112 Settings, vsync_mode, tr("VSync Mode:"), 168 Settings, vsync_mode, tr("VSync Mode:"),
113 tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " 169 tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
@@ -121,22 +177,29 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
121 177
122 // Renderer (Advanced Graphics) 178 // Renderer (Advanced Graphics)
123 INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"), 179 INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"),
124 QStringLiteral()); 180 tr("Slightly improves performance by moving presentation to a separate CPU thread."));
125 INSERT( 181 INSERT(
126 Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"), 182 Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"),
127 tr("Runs work in the background while waiting for graphics commands to keep the GPU from " 183 tr("Runs work in the background while waiting for graphics commands to keep the GPU from "
128 "lowering its clock speed.")); 184 "lowering its clock speed."));
129 INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral()); 185 INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"),
130 INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral()); 186 tr("Controls the quality of texture rendering at oblique angles.\nIt’s a light setting "
131 INSERT( 187 "and safe to set at 16x on most GPUs."));
132 Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"), 188 INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"),
133 tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature " 189 tr("GPU emulation accuracy.\nMost games render fine with Normal, but High is still "
134 "is experimental.")); 190 "required for some.\nParticles tend to only render correctly with High "
191 "accuracy.\nExtreme should only be used for debugging.\nThis option can "
192 "be changed while playing.\nSome games may require booting on high to render "
193 "properly."));
194 INSERT(Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"),
195 tr("Enables asynchronous shader compilation, which may reduce shader stutter.\nThis "
196 "feature "
197 "is experimental."));
135 INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"), 198 INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"),
136 tr("Enables Fast GPU Time. This option will force most games to run at their highest " 199 tr("Enables Fast GPU Time. This option will force most games to run at their highest "
137 "native resolution.")); 200 "native resolution."));
138 INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"), 201 INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"),
139 tr("Enables GPU vendor-specific pipeline cache. This option can improve shader loading " 202 tr("Enables GPU vendor-specific pipeline cache.\nThis option can improve shader loading "
140 "time significantly in cases where the Vulkan driver does not store pipeline cache " 203 "time significantly in cases where the Vulkan driver does not store pipeline cache "
141 "files internally.")); 204 "files internally."));
142 INSERT( 205 INSERT(
@@ -157,19 +220,27 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
157 // Renderer (Debug) 220 // Renderer (Debug)
158 221
159 // System 222 // System
160 INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral()); 223 INSERT(Settings, rng_seed, tr("RNG Seed"),
224 tr("Controls the seed of the random number generator.\nMainly used for speedrunning "
225 "purposes."));
161 INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral()); 226 INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral());
162 INSERT(Settings, device_name, tr("Device Name"), QStringLiteral()); 227 INSERT(Settings, device_name, tr("Device Name"), tr("The name of the emulated Switch."));
163 INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), QStringLiteral()); 228 INSERT(Settings, custom_rtc, tr("Custom RTC Date:"),
229 tr("This option allows to change the emulated clock of the Switch.\n"
230 "Can be used to manipulate time in games."));
164 INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral()); 231 INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral());
165 INSERT(Settings, custom_rtc_offset, QStringLiteral(" "), 232 INSERT(Settings, custom_rtc_offset, QStringLiteral(" "),
166 QStringLiteral("The number of seconds from the current unix time")); 233 QStringLiteral("The number of seconds from the current unix time"));
167 INSERT(Settings, language_index, tr("Language:"), 234 INSERT(Settings, language_index, tr("Language:"),
168 tr("Note: this can be overridden when region setting is auto-select")); 235 tr("Note: this can be overridden when region setting is auto-select"));
169 INSERT(Settings, region_index, tr("Region:"), QStringLiteral()); 236 INSERT(Settings, region_index, tr("Region:"), tr("The region of the emulated Switch."));
170 INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral()); 237 INSERT(Settings, time_zone_index, tr("Time Zone:"),
238 tr("The time zone of the emulated Switch."));
171 INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral()); 239 INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral());
172 INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral()); 240 INSERT(Settings, use_docked_mode, tr("Console Mode:"),
241 tr("Selects if the console is emulated in Docked or Handheld mode.\nGames will change "
242 "their resolution, details and supported controllers and depending on this setting.\n"
243 "Setting to Handheld can help improve performance for low end systems."));
173 INSERT(Settings, current_user, QStringLiteral(), QStringLiteral()); 244 INSERT(Settings, current_user, QStringLiteral(), QStringLiteral());
174 245
175 // Controls 246 // Controls
@@ -187,14 +258,19 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
187 // Ui 258 // Ui
188 259
189 // Ui General 260 // Ui General
190 INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), QStringLiteral()); 261 INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"),
262 tr("Ask to select a user profile on each boot, useful if multiple people use yuzu on "
263 "the same PC."));
191 INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"), 264 INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"),
192 QStringLiteral()); 265 tr("This setting pauses yuzu when focusing other windows."));
193 INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"), 266 INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"),
194 QStringLiteral()); 267 tr("This setting overrides game prompts asking to confirm stopping the game.\nEnabling "
195 INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral()); 268 "it bypasses such prompts and directly exits the emulation."));
269 INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"),
270 tr("This setting hides the mouse after 2.5s of inactivity."));
196 INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), 271 INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
197 QStringLiteral()); 272 tr("Forcibly disables the use of the controller applet by guests.\nWhen a guest "
273 "attempts to open the controller applet, it is immediately closed."));
198 274
199 // Linux 275 // Linux
200 INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral()); 276 INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral());
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 170f14684..1931dcd1f 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -190,10 +190,8 @@ void ControllerShortcut::ControllerUpdateEvent(Core::HID::ControllerTriggerType
190 if (type != Core::HID::ControllerTriggerType::Button) { 190 if (type != Core::HID::ControllerTriggerType::Button) {
191 return; 191 return;
192 } 192 }
193 if (!Settings::values.controller_navigation) { 193 if (button_sequence.npad.raw == Core::HID::NpadButton::None &&
194 return; 194 button_sequence.capture.raw == 0 && button_sequence.home.raw == 0) {
195 }
196 if (button_sequence.npad.raw == Core::HID::NpadButton::None) {
197 return; 195 return;
198 } 196 }
199 197