diff options
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 {      THEME_MODE("theme_mode"),      OVERLAY_SCALE("control_scale"),      OVERLAY_OPACITY("control_opacity"), -    LOCK_DRAWER("lock_drawer"); +    LOCK_DRAWER("lock_drawer"), +    VERTICAL_ALIGNMENT("vertical_alignment"), +    FSR_SHARPENING_SLIDER("fsr_sharpening_slider");      override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) 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 {                  entries.firstOrNull { it.int == int } ?: Unspecified          }      } + +    enum class EmulationVerticalAlignment(val int: Int) { +        Top(1), +        Center(0), +        Bottom(2); + +        companion object { +            fun from(int: Int): EmulationVerticalAlignment = +                entries.firstOrNull { it.int == int } ?: Center +        } +    }  } 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(                  )              )              put( +                SliderSetting( +                    IntSetting.FSR_SHARPENING_SLIDER, +                    R.string.fsr_sharpness, +                    R.string.fsr_sharpness_description, +                    0, +                    100, +                    "%" +                ) +            ) +            put(                  SingleChoiceSetting(                      IntSetting.RENDERER_ANTI_ALIASING,                      R.string.renderer_anti_aliasing, @@ -216,6 +226,15 @@ abstract class SettingsItem(                  )              )              put( +                SingleChoiceSetting( +                    IntSetting.VERTICAL_ALIGNMENT, +                    R.string.vertical_alignment, +                    0, +                    R.array.verticalAlignmentEntries, +                    R.array.verticalAlignmentValues +                ) +            ) +            put(                  SwitchSetting(                      BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE,                      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(              add(IntSetting.RENDERER_RESOLUTION.key)              add(IntSetting.RENDERER_VSYNC.key)              add(IntSetting.RENDERER_SCALING_FILTER.key) +            add(IntSetting.FSR_SHARPENING_SLIDER.key)              add(IntSetting.RENDERER_ANTI_ALIASING.key)              add(IntSetting.MAX_ANISOTROPY.key)              add(IntSetting.RENDERER_SCREEN_LAYOUT.key)              add(IntSetting.RENDERER_ASPECT_RATIO.key) +            add(IntSetting.VERTICAL_ALIGNMENT.key)              add(BooleanSetting.PICTURE_IN_PICTURE.key)              add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)              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() {                                  requireActivity(),                                  titleId = R.string.addon_notice,                                  descriptionId = R.string.addon_notice_description, -                                positiveAction = { addonViewModel.showModInstallPicker(true) } +                                dismissible = false, +                                positiveAction = { addonViewModel.showModInstallPicker(true) }, +                                negativeAction = {}, +                                negativeButtonTitleId = R.string.close                              ).show(parentFragmentManager, MessageDialogFragment.TAG)                              addonViewModel.showModNoticeDialog(false)                          } @@ -119,7 +122,8 @@ class AddonsFragment : Fragment() {                                  requireActivity(),                                  titleId = R.string.confirm_uninstall,                                  descriptionId = R.string.confirm_uninstall_description, -                                positiveAction = { addonViewModel.onDeleteAddon(it) } +                                positiveAction = { addonViewModel.onDeleteAddon(it) }, +                                negativeAction = {}                              ).show(parentFragmentManager, MessageDialogFragment.TAG)                              addonViewModel.setAddonToDelete(null)                          } 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  import android.os.Looper  import android.os.PowerManager  import android.os.SystemClock +import android.util.Rational  import android.view.* +import android.widget.FrameLayout  import android.widget.TextView  import android.widget.Toast  import androidx.activity.OnBackPressedCallback @@ -24,6 +26,7 @@ import androidx.core.content.res.ResourcesCompat  import androidx.core.graphics.Insets  import androidx.core.view.ViewCompat  import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams  import androidx.core.view.updatePadding  import androidx.drawerlayout.widget.DrawerLayout  import androidx.drawerlayout.widget.DrawerLayout.DrawerListener @@ -52,6 +55,7 @@ import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting  import org.yuzu.yuzu_emu.features.settings.model.IntSetting  import org.yuzu.yuzu_emu.features.settings.model.Settings  import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation +import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationVerticalAlignment  import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile  import org.yuzu.yuzu_emu.model.DriverViewModel  import org.yuzu.yuzu_emu.model.Game @@ -617,7 +621,46 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {      }      private fun updateScreenLayout() { -        binding.surfaceEmulation.setAspectRatio(null) +        val verticalAlignment = +            EmulationVerticalAlignment.from(IntSetting.VERTICAL_ALIGNMENT.getInt()) +        val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.getInt()) { +            0 -> Rational(16, 9) +            1 -> Rational(4, 3) +            2 -> Rational(21, 9) +            3 -> Rational(16, 10) +            else -> null // Best fit +        } +        when (verticalAlignment) { +            EmulationVerticalAlignment.Top -> { +                binding.surfaceEmulation.setAspectRatio(aspectRatio) +                val params = FrameLayout.LayoutParams( +                    ViewGroup.LayoutParams.MATCH_PARENT, +                    ViewGroup.LayoutParams.WRAP_CONTENT +                ) +                params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL +                binding.surfaceEmulation.layoutParams = params +            } + +            EmulationVerticalAlignment.Center -> { +                binding.surfaceEmulation.setAspectRatio(null) +                binding.surfaceEmulation.updateLayoutParams { +                    width = ViewGroup.LayoutParams.MATCH_PARENT +                    height = ViewGroup.LayoutParams.MATCH_PARENT +                } +            } + +            EmulationVerticalAlignment.Bottom -> { +                binding.surfaceEmulation.setAspectRatio(aspectRatio) +                val params = +                    FrameLayout.LayoutParams( +                        ViewGroup.LayoutParams.MATCH_PARENT, +                        ViewGroup.LayoutParams.WRAP_CONTENT +                    ) +                params.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL +                binding.surfaceEmulation.layoutParams = params +            } +        } +        emulationState.updateSurface()          emulationActivity?.buildPictureInPictureParams()          updateOrientation()      } 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() {                                      requireActivity(),                                      titleId = R.string.delete_save_data,                                      descriptionId = R.string.delete_save_data_warning_description, -                                    positiveAction = { +                                    positiveButtonTitleId = android.R.string.cancel, +                                    negativeButtonTitleId = android.R.string.ok, +                                    negativeAction = {                                          File(args.game.saveDir).deleteRecursively()                                          Toast.makeText(                                              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 @@  package org.yuzu.yuzu_emu.fragments  import android.app.Dialog -import android.content.DialogInterface  import android.content.Intent  import android.net.Uri  import android.os.Bundle @@ -16,18 +15,52 @@ import androidx.lifecycle.ViewModelProvider  import com.google.android.material.dialog.MaterialAlertDialogBuilder  import org.yuzu.yuzu_emu.R  import org.yuzu.yuzu_emu.model.MessageDialogViewModel +import org.yuzu.yuzu_emu.utils.Log  class MessageDialogFragment : DialogFragment() {      private val messageDialogViewModel: MessageDialogViewModel by activityViewModels()      override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {          val titleId = requireArguments().getInt(TITLE_ID) -        val titleString = requireArguments().getString(TITLE_STRING)!! +        val title = if (titleId != 0) { +            getString(titleId) +        } else { +            requireArguments().getString(TITLE_STRING)!! +        } +          val descriptionId = requireArguments().getInt(DESCRIPTION_ID) -        val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!! +        val description = if (descriptionId != 0) { +            getString(descriptionId) +        } else { +            requireArguments().getString(DESCRIPTION_STRING)!! +        } + +        val positiveButtonId = requireArguments().getInt(POSITIVE_BUTTON_TITLE_ID) +        val positiveButtonString = requireArguments().getString(POSITIVE_BUTTON_TITLE_STRING)!! +        val positiveButton = if (positiveButtonId != 0) { +            getString(positiveButtonId) +        } else if (positiveButtonString.isNotEmpty()) { +            positiveButtonString +        } else if (messageDialogViewModel.positiveAction != null) { +            getString(android.R.string.ok) +        } else { +            getString(R.string.close) +        } + +        val negativeButtonId = requireArguments().getInt(NEGATIVE_BUTTON_TITLE_ID) +        val negativeButtonString = requireArguments().getString(NEGATIVE_BUTTON_TITLE_STRING)!! +        val negativeButton = if (negativeButtonId != 0) { +            getString(negativeButtonId) +        } else if (negativeButtonString.isNotEmpty()) { +            negativeButtonString +        } else { +            getString(android.R.string.cancel) +        } +          val helpLinkId = requireArguments().getInt(HELP_LINK)          val dismissible = requireArguments().getBoolean(DISMISSIBLE) -        val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION) +        val clearPositiveAction = requireArguments().getBoolean(CLEAR_ACTIONS) +        val showNegativeButton = requireArguments().getBoolean(SHOW_NEGATIVE_BUTTON)          val builder = MaterialAlertDialogBuilder(requireContext()) @@ -35,21 +68,19 @@ class MessageDialogFragment : DialogFragment() {              messageDialogViewModel.positiveAction = null          } -        if (messageDialogViewModel.positiveAction == null) { -            builder.setPositiveButton(R.string.close, null) -        } else { -            builder.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> -                messageDialogViewModel.positiveAction?.invoke() -            }.setNegativeButton(android.R.string.cancel, null) +        builder.setPositiveButton(positiveButton) { _, _ -> +            messageDialogViewModel.positiveAction?.invoke() +        } +        if (messageDialogViewModel.negativeAction != null || showNegativeButton) { +            builder.setNegativeButton(negativeButton) { _, _ -> +                messageDialogViewModel.negativeAction?.invoke() +            }          } -        if (titleId != 0) builder.setTitle(titleId) -        if (titleString.isNotEmpty()) builder.setTitle(titleString) - -        if (descriptionId != 0) { -            builder.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY)) +        if (title.isNotEmpty()) builder.setTitle(title) +        if (description.isNotEmpty()) { +            builder.setMessage(Html.fromHtml(description, Html.FROM_HTML_MODE_LEGACY))          } -        if (descriptionString.isNotEmpty()) builder.setMessage(descriptionString)          if (helpLinkId != 0) {              builder.setNeutralButton(R.string.learn_more) { _, _ -> @@ -76,8 +107,41 @@ class MessageDialogFragment : DialogFragment() {          private const val DESCRIPTION_STRING = "DescriptionString"          private const val HELP_LINK = "Link"          private const val DISMISSIBLE = "Dismissible" -        private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction" - +        private const val CLEAR_ACTIONS = "ClearActions" +        private const val POSITIVE_BUTTON_TITLE_ID = "PositiveButtonTitleId" +        private const val POSITIVE_BUTTON_TITLE_STRING = "PositiveButtonTitleString" +        private const val SHOW_NEGATIVE_BUTTON = "ShowNegativeButton" +        private const val NEGATIVE_BUTTON_TITLE_ID = "NegativeButtonTitleId" +        private const val NEGATIVE_BUTTON_TITLE_STRING = "NegativeButtonTitleString" + +        /** +         * Creates a new [MessageDialogFragment] instance. +         * @param activity Activity that will hold a [MessageDialogViewModel] instance if using +         * [positiveAction] or [negativeAction]. +         * @param titleId String resource ID that will be used for the title. [titleString] used if 0. +         * @param titleString String that will be used for the title. No title is set if empty. +         * @param descriptionId String resource ID that will be used for the description. +         * [descriptionString] used if 0. +         * @param descriptionString String that will be used for the description. +         * No description is set if empty. +         * @param helpLinkId String resource ID that contains a help link. Will be added as a neutral +         * button with the title R.string.help. +         * @param dismissible Whether the dialog is dismissible or not. Typically used to ensure that +         * the user clicks on one of the dialog buttons before closing. +         * @param positiveButtonTitleId String resource ID that will be used for the positive button. +         * [positiveButtonTitleString] used if 0. +         * @param positiveButtonTitleString String that will be used for the positive button. +         * android.R.string.close used if empty. android.R.string.ok will be used if [positiveAction] +         * is not null. +         * @param positiveAction Lambda to run when the positive button is clicked. +         * @param showNegativeButton Normally the negative button isn't shown if there is no +         * [negativeAction] set. This can override that behavior to always show a button. +         * @param negativeButtonTitleId String resource ID that will be used for the negative button. +         * [negativeButtonTitleString] used if 0. +         * @param negativeButtonTitleString String that will be used for the negative button. +         * android.R.string.cancel used if empty. +         * @param negativeAction Lambda to run when the negative button is clicked +         */          fun newInstance(              activity: FragmentActivity? = null,              titleId: Int = 0, @@ -86,16 +150,27 @@ class MessageDialogFragment : DialogFragment() {              descriptionString: String = "",              helpLinkId: Int = 0,              dismissible: Boolean = true, -            positiveAction: (() -> Unit)? = null +            positiveButtonTitleId: Int = 0, +            positiveButtonTitleString: String = "", +            positiveAction: (() -> Unit)? = null, +            showNegativeButton: Boolean = false, +            negativeButtonTitleId: Int = 0, +            negativeButtonTitleString: String = "", +            negativeAction: (() -> Unit)? = null          ): MessageDialogFragment { -            var clearPositiveAction = false +            var clearActions = false              if (activity != null) {                  ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {                      clear()                      this.positiveAction = positiveAction +                    this.negativeAction = negativeAction                  }              } else { -                clearPositiveAction = true +                clearActions = true +            } + +            if (activity == null && (positiveAction == null || negativeAction == null)) { +                Log.warning("[$TAG] Tried to set action with no activity!")              }              val dialog = MessageDialogFragment() @@ -106,7 +181,12 @@ class MessageDialogFragment : DialogFragment() {                  putString(DESCRIPTION_STRING, descriptionString)                  putInt(HELP_LINK, helpLinkId)                  putBoolean(DISMISSIBLE, dismissible) -                putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction) +                putBoolean(CLEAR_ACTIONS, clearActions) +                putInt(POSITIVE_BUTTON_TITLE_ID, positiveButtonTitleId) +                putString(POSITIVE_BUTTON_TITLE_STRING, positiveButtonTitleString) +                putBoolean(SHOW_NEGATIVE_BUTTON, showNegativeButton) +                putInt(NEGATIVE_BUTTON_TITLE_ID, negativeButtonTitleId) +                putString(NEGATIVE_BUTTON_TITLE_STRING, negativeButtonTitleString)              }              dialog.arguments = bundle              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  class MessageDialogViewModel : ViewModel() {      var positiveAction: (() -> Unit)? = null +    var negativeAction: (() -> Unit)? = null      fun clear() {          positiveAction = null +        negativeAction = null      }  } 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 {                                           Settings::Specialization::Default,                                           true,                                           true}; +    Settings::Setting<s32> vertical_alignment{linkage, +                                              0, +                                              "vertical_alignment", +                                              Settings::Category::Android, +                                              Settings::Specialization::Default, +                                              true, +                                              true};      Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",                                                                  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 @@          <item>5</item>      </integer-array> +    <string-array name="verticalAlignmentEntries"> +        <item>@string/top</item> +        <item>@string/center</item> +        <item>@string/bottom</item> +    </string-array> +    <integer-array name="verticalAlignmentValues"> +        <item>1</item> +        <item>0</item> +        <item>2</item> +    </integer-array> +  </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 @@      <string name="renderer_screen_layout">Orientation</string>      <string name="renderer_aspect_ratio">Aspect ratio</string>      <string name="renderer_scaling_filter">Window adapting filter</string> +    <string name="fsr_sharpness">FSR sharpness</string> +    <string name="fsr_sharpness_description">Determines how sharpened the image will look while using FSR\'s dynamic contrast</string>      <string name="renderer_anti_aliasing">Anti-aliasing method</string>      <string name="renderer_force_max_clock">Force maximum clocks (Adreno only)</string>      <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 @@      <string name="mute">Mute</string>      <string name="unmute">Unmute</string> +    <!-- Emulation vertical alignment --> +    <string name="vertical_alignment">Vertical alignment</string> +    <string name="top">Top</string> +    <string name="center">Center</string> +    <string name="bottom">Bottom</string> +      <!-- Licenses screen strings -->      <string name="licenses">Licenses</string>      <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      hle/service/ldn/ldn.h      hle/service/ldn/ldn_results.h      hle/service/ldn/ldn_types.h +    hle/service/ldn/monitor_service.cpp +    hle/service/ldn/monitor_service.h +    hle/service/ldn/sf_monitor_service.cpp +    hle/service/ldn/sf_monitor_service.h +    hle/service/ldn/sf_service.cpp +    hle/service/ldn/sf_service.h +    hle/service/ldn/sf_service_monitor.cpp +    hle/service/ldn/sf_service_monitor.h +    hle/service/ldn/system_local_communication_service.cpp +    hle/service/ldn/system_local_communication_service.h +    hle/service/ldn/user_local_communication_service.cpp +    hle/service/ldn/user_local_communication_service.h      hle/service/ldr/ldr.cpp      hle/service/ldr/ldr.h      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 {  }  Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network, -                                    std::vector<NodeLatestUpdate>& out_updates, -                                    std::size_t buffer_count) { -    if (buffer_count > NodeCountMax) { +                                    std::span<NodeLatestUpdate> out_updates) { +    if (out_updates.size() > NodeCountMax) {          return ResultInvalidBufferCount;      }      if (state == State::AccessPointCreated || state == State::StationConnected) {          std::memcpy(&out_network, &network_info, sizeof(network_info)); -        for (std::size_t i = 0; i < buffer_count; i++) { +        for (std::size_t i = 0; i < out_updates.size(); i++) {              out_updates[i].state_change = node_changes[i].state_change;              node_changes[i].state_change = NodeStateChange::None;          } @@ -107,15 +106,8 @@ DisconnectReason LANDiscovery::GetDisconnectReason() const {      return disconnect_reason;  } -Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count, +Result LANDiscovery::Scan(std::span<NetworkInfo> out_networks, s16& out_count,                            const ScanFilter& filter) { -    if (!IsFlagSet(filter.flag, ScanFilterFlag::NetworkType) || -        filter.network_type <= NetworkType::All) { -        if (!IsFlagSet(filter.flag, ScanFilterFlag::Ssid) && filter.ssid.length >= SsidLengthMax) { -            return ResultBadInput; -        } -    } -      {          std::scoped_lock lock{packet_mutex};          scan_results.clear(); @@ -128,7 +120,7 @@ Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count,      std::scoped_lock lock{packet_mutex};      for (const auto& [key, info] : scan_results) { -        if (count >= networks.size()) { +        if (out_count >= static_cast<s16>(out_networks.size())) {              break;          } @@ -159,7 +151,7 @@ Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count,              }          } -        networks[count++] = info; +        out_networks[out_count++] = info;      }      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:      void SetState(State new_state);      Result GetNetworkInfo(NetworkInfo& out_network) const; -    Result GetNetworkInfo(NetworkInfo& out_network, std::vector<NodeLatestUpdate>& out_updates, -                          std::size_t buffer_count); +    Result GetNetworkInfo(NetworkInfo& out_network, std::span<NodeLatestUpdate> out_updates);      DisconnectReason GetDisconnectReason() const; -    Result Scan(std::vector<NetworkInfo>& networks, u16& count, const ScanFilter& filter); +    Result Scan(std::span<NetworkInfo> out_networks, s16& out_count, const ScanFilter& filter);      Result SetAdvertiseData(std::span<const u8> data);      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 @@  // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later -#include <memory> -  #include "core/core.h" -#include "core/hle/service/ldn/lan_discovery.h" +#include "core/hle/service/cmif_serialization.h"  #include "core/hle/service/ldn/ldn.h" -#include "core/hle/service/ldn/ldn_results.h" -#include "core/hle/service/ldn/ldn_types.h" -#include "core/hle/service/server_manager.h" -#include "core/internal_network/network.h" -#include "core/internal_network/network_interface.h" -#include "network/network.h" - -// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent -#undef CreateEvent +#include "core/hle/service/ldn/monitor_service.h" +#include "core/hle/service/ldn/sf_monitor_service.h" +#include "core/hle/service/ldn/sf_service.h" +#include "core/hle/service/ldn/sf_service_monitor.h" +#include "core/hle/service/ldn/system_local_communication_service.h" +#include "core/hle/service/ldn/user_local_communication_service.h"  namespace Service::LDN { -class IMonitorService final : public ServiceFramework<IMonitorService> { +class IMonitorServiceCreator final : public ServiceFramework<IMonitorServiceCreator> {  public: -    explicit IMonitorService(Core::System& system_) : ServiceFramework{system_, "IMonitorService"} { +    explicit IMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:m"} {          // clang-format off          static const FunctionInfo functions[] = { -            {0, &IMonitorService::GetStateForMonitor, "GetStateForMonitor"}, -            {1, nullptr, "GetNetworkInfoForMonitor"}, -            {2, nullptr, "GetIpv4AddressForMonitor"}, -            {3, nullptr, "GetDisconnectReasonForMonitor"}, -            {4, nullptr, "GetSecurityParameterForMonitor"}, -            {5, nullptr, "GetNetworkConfigForMonitor"}, -            {100, &IMonitorService::InitializeMonitor, "InitializeMonitor"}, -            {101, nullptr, "FinalizeMonitor"}, +            {0, C<&IMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"}          };          // clang-format on @@ -38,84 +26,20 @@ public:      }  private: -    void GetStateForMonitor(HLERequestContext& ctx) { -        LOG_INFO(Service_LDN, "called"); - -        IPC::ResponseBuilder rb{ctx, 3}; -        rb.Push(ResultSuccess); -        rb.PushEnum(state); -    } - -    void InitializeMonitor(HLERequestContext& ctx) { -        LOG_INFO(Service_LDN, "called"); - -        state = State::Initialized; - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); -    } - -    State state{State::None}; -}; - -class LDNM final : public ServiceFramework<LDNM> { -public: -    explicit LDNM(Core::System& system_) : ServiceFramework{system_, "ldn:m"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {0, &LDNM::CreateMonitorService, "CreateMonitorService"} -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } - -    void CreateMonitorService(HLERequestContext& ctx) { +    Result CreateMonitorService(OutInterface<IMonitorService> out_interface) {          LOG_DEBUG(Service_LDN, "called"); -        IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -        rb.Push(ResultSuccess); -        rb.PushIpcInterface<IMonitorService>(system); +        *out_interface = std::make_shared<IMonitorService>(system); +        R_SUCCEED();      }  }; -class ISystemLocalCommunicationService final -    : public ServiceFramework<ISystemLocalCommunicationService> { +class ISystemServiceCreator final : public ServiceFramework<ISystemServiceCreator> {  public: -    explicit ISystemLocalCommunicationService(Core::System& system_) -        : ServiceFramework{system_, "ISystemLocalCommunicationService"} { +    explicit ISystemServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:s"} {          // clang-format off          static const FunctionInfo functions[] = { -            {0, nullptr, "GetState"}, -            {1, nullptr, "GetNetworkInfo"}, -            {2, nullptr, "GetIpv4Address"}, -            {3, nullptr, "GetDisconnectReason"}, -            {4, nullptr, "GetSecurityParameter"}, -            {5, nullptr, "GetNetworkConfig"}, -            {100, nullptr, "AttachStateChangeEvent"}, -            {101, nullptr, "GetNetworkInfoLatestUpdate"}, -            {102, nullptr, "Scan"}, -            {103, nullptr, "ScanPrivate"}, -            {104, nullptr, "SetWirelessControllerRestriction"}, -            {200, nullptr, "OpenAccessPoint"}, -            {201, nullptr, "CloseAccessPoint"}, -            {202, nullptr, "CreateNetwork"}, -            {203, nullptr, "CreateNetworkPrivate"}, -            {204, nullptr, "DestroyNetwork"}, -            {205, nullptr, "Reject"}, -            {206, nullptr, "SetAdvertiseData"}, -            {207, nullptr, "SetStationAcceptPolicy"}, -            {208, nullptr, "AddAcceptFilterEntry"}, -            {209, nullptr, "ClearAcceptFilter"}, -            {300, nullptr, "OpenStation"}, -            {301, nullptr, "CloseStation"}, -            {302, nullptr, "Connect"}, -            {303, nullptr, "ConnectPrivate"}, -            {304, nullptr, "Disconnect"}, -            {400, nullptr, "InitializeSystem"}, -            {401, nullptr, "FinalizeSystem"}, -            {402, nullptr, "SetOperationMode"}, -            {403, &ISystemLocalCommunicationService::InitializeSystem2, "InitializeSystem2"}, +            {0, C<&ISystemServiceCreator::CreateSystemLocalCommunicationService>, "CreateSystemLocalCommunicationService"},          };          // clang-format on @@ -123,687 +47,78 @@ public:      }  private: -    void InitializeSystem2(HLERequestContext& ctx) { -        LOG_WARNING(Service_LDN, "(STUBBED) called"); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); -    } -}; - -class IUserLocalCommunicationService final -    : public ServiceFramework<IUserLocalCommunicationService> { -public: -    explicit IUserLocalCommunicationService(Core::System& system_) -        : ServiceFramework{system_, "IUserLocalCommunicationService"}, -          service_context{system, "IUserLocalCommunicationService"}, -          room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {0, &IUserLocalCommunicationService::GetState, "GetState"}, -            {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, -            {2, &IUserLocalCommunicationService::GetIpv4Address, "GetIpv4Address"}, -            {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"}, -            {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"}, -            {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"}, -            {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"}, -            {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"}, -            {102, &IUserLocalCommunicationService::Scan, "Scan"}, -            {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"}, -            {104, &IUserLocalCommunicationService::SetWirelessControllerRestriction, "SetWirelessControllerRestriction"}, -            {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, -            {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, -            {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, -            {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"}, -            {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"}, -            {205, nullptr, "Reject"}, -            {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"}, -            {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"}, -            {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"}, -            {209, nullptr, "ClearAcceptFilter"}, -            {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"}, -            {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"}, -            {302, &IUserLocalCommunicationService::Connect, "Connect"}, -            {303, nullptr, "ConnectPrivate"}, -            {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"}, -            {400, &IUserLocalCommunicationService::Initialize, "Initialize"}, -            {401, &IUserLocalCommunicationService::Finalize, "Finalize"}, -            {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); - -        state_change_event = -            service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent"); -    } - -    ~IUserLocalCommunicationService() { -        if (is_initialized) { -            if (auto room_member = room_network.GetRoomMember().lock()) { -                room_member->Unbind(ldn_packet_received); -            } -        } - -        service_context.CloseEvent(state_change_event); -    } - -    /// Callback to parse and handle a received LDN packet. -    void OnLDNPacketReceived(const Network::LDNPacket& packet) { -        lan_discovery.ReceivePacket(packet); -    } - -    void OnEventFired() { -        state_change_event->Signal(); -    } - -    void GetState(HLERequestContext& ctx) { -        State state = State::Error; - -        if (is_initialized) { -            state = lan_discovery.GetState(); -        } - -        IPC::ResponseBuilder rb{ctx, 3}; -        rb.Push(ResultSuccess); -        rb.PushEnum(state); -    } - -    void GetNetworkInfo(HLERequestContext& ctx) { -        const auto write_buffer_size = ctx.GetWriteBufferSize(); - -        if (write_buffer_size != sizeof(NetworkInfo)) { -            LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(ResultBadInput); -            return; -        } - -        NetworkInfo network_info{}; -        const auto rc = lan_discovery.GetNetworkInfo(network_info); -        if (rc.IsError()) { -            LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(rc); -            return; -        } - -        ctx.WriteBuffer<NetworkInfo>(network_info); -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); -    } - -    void GetIpv4Address(HLERequestContext& ctx) { -        const auto network_interface = Network::GetSelectedNetworkInterface(); - -        if (!network_interface) { -            LOG_ERROR(Service_LDN, "No network interface available"); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(ResultNoIpAddress); -            return; -        } - -        Ipv4Address current_address{Network::TranslateIPv4(network_interface->ip_address)}; -        Ipv4Address subnet_mask{Network::TranslateIPv4(network_interface->subnet_mask)}; - -        // When we're connected to a room, spoof the hosts IP address -        if (auto room_member = room_network.GetRoomMember().lock()) { -            if (room_member->IsConnected()) { -                current_address = room_member->GetFakeIpAddress(); -            } -        } - -        std::reverse(std::begin(current_address), std::end(current_address)); // ntohl -        std::reverse(std::begin(subnet_mask), std::end(subnet_mask));         // ntohl - -        IPC::ResponseBuilder rb{ctx, 4}; -        rb.Push(ResultSuccess); -        rb.PushRaw(current_address); -        rb.PushRaw(subnet_mask); -    } - -    void GetDisconnectReason(HLERequestContext& ctx) { -        IPC::ResponseBuilder rb{ctx, 3}; -        rb.Push(ResultSuccess); -        rb.PushEnum(lan_discovery.GetDisconnectReason()); -    } - -    void GetSecurityParameter(HLERequestContext& ctx) { -        SecurityParameter security_parameter{}; -        NetworkInfo info{}; -        const Result rc = lan_discovery.GetNetworkInfo(info); - -        if (rc.IsError()) { -            LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(rc); -            return; -        } - -        security_parameter.session_id = info.network_id.session_id; -        std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(), -                    sizeof(SecurityParameter::data)); - -        IPC::ResponseBuilder rb{ctx, 10}; -        rb.Push(rc); -        rb.PushRaw<SecurityParameter>(security_parameter); -    } - -    void GetNetworkConfig(HLERequestContext& ctx) { -        NetworkConfig config{}; -        NetworkInfo info{}; -        const Result rc = lan_discovery.GetNetworkInfo(info); - -        if (rc.IsError()) { -            LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(rc); -            return; -        } - -        config.intent_id = info.network_id.intent_id; -        config.channel = info.common.channel; -        config.node_count_max = info.ldn.node_count_max; -        config.local_communication_version = info.ldn.nodes[0].local_communication_version; - -        IPC::ResponseBuilder rb{ctx, 10}; -        rb.Push(rc); -        rb.PushRaw<NetworkConfig>(config); -    } - -    void AttachStateChangeEvent(HLERequestContext& ctx) { -        LOG_INFO(Service_LDN, "called"); - -        IPC::ResponseBuilder rb{ctx, 2, 1}; -        rb.Push(ResultSuccess); -        rb.PushCopyObjects(state_change_event->GetReadableEvent()); -    } - -    void GetNetworkInfoLatestUpdate(HLERequestContext& ctx) { -        const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0); -        const std::size_t node_buffer_count = ctx.GetWriteBufferNumElements<NodeLatestUpdate>(1); - -        if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { -            LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size, -                      node_buffer_count); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(ResultBadInput); -            return; -        } - -        NetworkInfo info{}; -        std::vector<NodeLatestUpdate> latest_update(node_buffer_count); - -        const auto rc = lan_discovery.GetNetworkInfo(info, latest_update, latest_update.size()); -        if (rc.IsError()) { -            LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(rc); -            return; -        } - -        ctx.WriteBuffer(info, 0); -        ctx.WriteBuffer(latest_update, 1); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); -    } - -    void Scan(HLERequestContext& ctx) { -        ScanImpl(ctx); -    } - -    void ScanPrivate(HLERequestContext& ctx) { -        ScanImpl(ctx, true); -    } - -    void ScanImpl(HLERequestContext& ctx, bool is_private = false) { -        IPC::RequestParser rp{ctx}; -        const auto channel{rp.PopEnum<WifiChannel>()}; -        const auto scan_filter{rp.PopRaw<ScanFilter>()}; - -        const std::size_t network_info_size = ctx.GetWriteBufferNumElements<NetworkInfo>(); - -        if (network_info_size == 0) { -            LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(ResultBadInput); -            return; -        } - -        u16 count = 0; -        std::vector<NetworkInfo> network_infos(network_info_size); -        Result rc = lan_discovery.Scan(network_infos, count, scan_filter); - -        LOG_INFO(Service_LDN, -                 "called, channel={}, filter_scan_flag={}, filter_network_type={}, is_private={}", -                 channel, scan_filter.flag, scan_filter.network_type, is_private); - -        ctx.WriteBuffer(network_infos); - -        IPC::ResponseBuilder rb{ctx, 3}; -        rb.Push(rc); -        rb.Push<u32>(count); -    } - -    void SetWirelessControllerRestriction(HLERequestContext& ctx) { -        LOG_WARNING(Service_LDN, "(STUBBED) called"); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); -    } - -    void OpenAccessPoint(HLERequestContext& ctx) { -        LOG_INFO(Service_LDN, "called"); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(lan_discovery.OpenAccessPoint()); -    } - -    void CloseAccessPoint(HLERequestContext& ctx) { -        LOG_INFO(Service_LDN, "called"); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(lan_discovery.CloseAccessPoint()); -    } - -    void CreateNetwork(HLERequestContext& ctx) { -        LOG_INFO(Service_LDN, "called"); - -        CreateNetworkImpl(ctx); -    } - -    void CreateNetworkPrivate(HLERequestContext& ctx) { -        LOG_INFO(Service_LDN, "called"); - -        CreateNetworkImpl(ctx, true); -    } - -    void CreateNetworkImpl(HLERequestContext& ctx, bool is_private = false) { -        IPC::RequestParser rp{ctx}; - -        const auto security_config{rp.PopRaw<SecurityConfig>()}; -        [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw<SecurityParameter>() -                                                                  : SecurityParameter{}}; -        const auto user_config{rp.PopRaw<UserConfig>()}; -        rp.Pop<u32>(); // Padding -        const auto network_Config{rp.PopRaw<NetworkConfig>()}; - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(lan_discovery.CreateNetwork(security_config, user_config, network_Config)); -    } - -    void DestroyNetwork(HLERequestContext& ctx) { -        LOG_INFO(Service_LDN, "called"); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(lan_discovery.DestroyNetwork()); -    } - -    void SetAdvertiseData(HLERequestContext& ctx) { -        const auto read_buffer = ctx.ReadBuffer(); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(lan_discovery.SetAdvertiseData(read_buffer)); -    } - -    void SetStationAcceptPolicy(HLERequestContext& ctx) { -        LOG_WARNING(Service_LDN, "(STUBBED) called"); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); -    } - -    void AddAcceptFilterEntry(HLERequestContext& ctx) { -        LOG_WARNING(Service_LDN, "(STUBBED) called"); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); -    } - -    void OpenStation(HLERequestContext& ctx) { -        LOG_INFO(Service_LDN, "called"); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(lan_discovery.OpenStation()); -    } - -    void CloseStation(HLERequestContext& ctx) { -        LOG_INFO(Service_LDN, "called"); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(lan_discovery.CloseStation()); -    } - -    void Connect(HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        struct Parameters { -            SecurityConfig security_config; -            UserConfig user_config; -            u32 local_communication_version; -            u32 option; -        }; -        static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size."); - -        const auto parameters{rp.PopRaw<Parameters>()}; - -        LOG_INFO(Service_LDN, -                 "called, passphrase_size={}, security_mode={}, " -                 "local_communication_version={}", -                 parameters.security_config.passphrase_size, -                 parameters.security_config.security_mode, parameters.local_communication_version); - -        const auto read_buffer = ctx.ReadBuffer(); -        if (read_buffer.size() != sizeof(NetworkInfo)) { -            LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(ResultBadInput); -            return; -        } - -        NetworkInfo network_info{}; -        std::memcpy(&network_info, read_buffer.data(), read_buffer.size()); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(lan_discovery.Connect(network_info, parameters.user_config, -                                      static_cast<u16>(parameters.local_communication_version))); -    } - -    void Disconnect(HLERequestContext& ctx) { -        LOG_INFO(Service_LDN, "called"); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(lan_discovery.Disconnect()); -    } - -    void Initialize(HLERequestContext& ctx) { -        const auto rc = InitializeImpl(ctx); -        if (rc.IsError()) { -            LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); -        } - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(rc); -    } - -    void Finalize(HLERequestContext& ctx) { -        if (auto room_member = room_network.GetRoomMember().lock()) { -            room_member->Unbind(ldn_packet_received); -        } - -        is_initialized = false; - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(lan_discovery.Finalize()); -    } - -    void Initialize2(HLERequestContext& ctx) { -        const auto rc = InitializeImpl(ctx); -        if (rc.IsError()) { -            LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); -        } - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(rc); -    } - -    Result InitializeImpl(HLERequestContext& ctx) { -        const auto network_interface = Network::GetSelectedNetworkInterface(); -        if (!network_interface) { -            LOG_ERROR(Service_LDN, "No network interface is set"); -            return ResultAirplaneModeEnabled; -        } - -        if (auto room_member = room_network.GetRoomMember().lock()) { -            ldn_packet_received = room_member->BindOnLdnPacketReceived( -                [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); }); -        } else { -            LOG_ERROR(Service_LDN, "Couldn't bind callback!"); -            return ResultAirplaneModeEnabled; -        } - -        lan_discovery.Initialize([&]() { OnEventFired(); }); -        is_initialized = true; -        return ResultSuccess; -    } - -    KernelHelpers::ServiceContext service_context; -    Kernel::KEvent* state_change_event; -    Network::RoomNetwork& room_network; -    LANDiscovery lan_discovery; - -    // Callback identifier for the OnLDNPacketReceived event. -    Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received; - -    bool is_initialized{}; -}; - -class LDNS final : public ServiceFramework<LDNS> { -public: -    explicit LDNS(Core::System& system_) : ServiceFramework{system_, "ldn:s"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {0, &LDNS::CreateSystemLocalCommunicationService, "CreateSystemLocalCommunicationService"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } - -    void CreateSystemLocalCommunicationService(HLERequestContext& ctx) { +    Result CreateSystemLocalCommunicationService( +        OutInterface<ISystemLocalCommunicationService> out_interface) {          LOG_DEBUG(Service_LDN, "called"); -        IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -        rb.Push(ResultSuccess); -        rb.PushIpcInterface<ISystemLocalCommunicationService>(system); +        *out_interface = std::make_shared<ISystemLocalCommunicationService>(system); +        R_SUCCEED();      }  }; -class LDNU final : public ServiceFramework<LDNU> { +class IUserServiceCreator final : public ServiceFramework<IUserServiceCreator> {  public: -    explicit LDNU(Core::System& system_) : ServiceFramework{system_, "ldn:u"} { +    explicit IUserServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:u"} {          // clang-format off          static const FunctionInfo functions[] = { -            {0, &LDNU::CreateUserLocalCommunicationService, "CreateUserLocalCommunicationService"}, +            {0, C<&IUserServiceCreator::CreateUserLocalCommunicationService>, "CreateUserLocalCommunicationService"},          };          // clang-format on          RegisterHandlers(functions);      } -    void CreateUserLocalCommunicationService(HLERequestContext& ctx) { +private: +    Result CreateUserLocalCommunicationService( +        OutInterface<IUserLocalCommunicationService> out_interface) {          LOG_DEBUG(Service_LDN, "called"); -        IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -        rb.Push(ResultSuccess); -        rb.PushIpcInterface<IUserLocalCommunicationService>(system); +        *out_interface = std::make_shared<IUserLocalCommunicationService>(system); +        R_SUCCEED();      }  }; -class INetworkService final : public ServiceFramework<INetworkService> { +class ISfServiceCreator final : public ServiceFramework<ISfServiceCreator> {  public: -    explicit INetworkService(Core::System& system_) : ServiceFramework{system_, "INetworkService"} { +    explicit ISfServiceCreator(Core::System& system_, bool is_system_, const char* name_) +        : ServiceFramework{system_, name_}, is_system{is_system_} {          // clang-format off          static const FunctionInfo functions[] = { -            {0, nullptr, "Initialize"}, -            {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"}, -            {264, nullptr, "GetNetworkInterfaceLastError"}, -            {272, nullptr, "GetRole"}, -            {280, nullptr, "GetAdvertiseData"}, -            {288, nullptr, "GetGroupInfo"}, -            {296, nullptr, "GetGroupInfo2"}, -            {304, nullptr, "GetGroupOwner"}, -            {312, nullptr, "GetIpConfig"}, -            {320, nullptr, "GetLinkLevel"}, -            {512, nullptr, "Scan"}, -            {768, nullptr, "CreateGroup"}, -            {776, nullptr, "DestroyGroup"}, -            {784, nullptr, "SetAdvertiseData"}, -            {1536, nullptr, "SendToOtherGroup"}, -            {1544, nullptr, "RecvFromOtherGroup"}, -            {1552, nullptr, "AddAcceptableGroupId"}, -            {1560, nullptr, "ClearAcceptableGroupId"}, +            {0, C<&ISfServiceCreator::CreateNetworkService>, "CreateNetworkService"}, +            {8, C<&ISfServiceCreator::CreateNetworkServiceMonitor>, "CreateNetworkServiceMonitor"},          };          // clang-format on          RegisterHandlers(functions);      } -}; - -class INetworkServiceMonitor final : public ServiceFramework<INetworkServiceMonitor> { -public: -    explicit INetworkServiceMonitor(Core::System& system_) -        : ServiceFramework{system_, "INetworkServiceMonitor"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {0, &INetworkServiceMonitor::Initialize, "Initialize"}, -            {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"}, -            {264, nullptr, "GetNetworkInterfaceLastError"}, -            {272, nullptr, "GetRole"}, -            {280, nullptr, "GetAdvertiseData"}, -            {281, nullptr, "GetAdvertiseData2"}, -            {288, nullptr, "GetGroupInfo"}, -            {296, nullptr, "GetGroupInfo2"}, -            {304, nullptr, "GetGroupOwner"}, -            {312, nullptr, "GetIpConfig"}, -            {320, nullptr, "GetLinkLevel"}, -            {328, nullptr, "AttachJoinEvent"}, -            {336, nullptr, "GetMembers"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } - -    void Initialize(HLERequestContext& ctx) { -        LOG_WARNING(Service_LDN, "(STUBBED) called"); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultDisabled); -    } -}; - -class LP2PAPP final : public ServiceFramework<LP2PAPP> { -public: -    explicit LP2PAPP(Core::System& system_) : ServiceFramework{system_, "lp2p:app"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {0, &LP2PAPP::CreateMonitorService, "CreateNetworkService"}, -            {8, &LP2PAPP::CreateMonitorService, "CreateNetworkServiceMonitor"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } - -    void CreateNetworkervice(HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        const u64 reserved_input = rp.Pop<u64>(); -        const u32 input = rp.Pop<u32>(); - -        LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input, -                    input); - -        IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -        rb.Push(ResultSuccess); -        rb.PushIpcInterface<INetworkService>(system); -    } - -    void CreateMonitorService(HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        const u64 reserved_input = rp.Pop<u64>(); - -        LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input); - -        IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -        rb.Push(ResultSuccess); -        rb.PushIpcInterface<INetworkServiceMonitor>(system); -    } -}; - -class LP2PSYS final : public ServiceFramework<LP2PSYS> { -public: -    explicit LP2PSYS(Core::System& system_) : ServiceFramework{system_, "lp2p:sys"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {0, &LP2PSYS::CreateMonitorService, "CreateNetworkService"}, -            {8, &LP2PSYS::CreateMonitorService, "CreateNetworkServiceMonitor"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } - -    void CreateNetworkervice(HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        const u64 reserved_input = rp.Pop<u64>(); -        const u32 input = rp.Pop<u32>(); +private: +    Result CreateNetworkService(OutInterface<ISfService> out_interface, u32 input, +                                u64 reserved_input) {          LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input,                      input); -        IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -        rb.Push(ResultSuccess); -        rb.PushIpcInterface<INetworkService>(system); +        *out_interface = std::make_shared<ISfService>(system); +        R_SUCCEED();      } -    void CreateMonitorService(HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        const u64 reserved_input = rp.Pop<u64>(); - +    Result CreateNetworkServiceMonitor(OutInterface<ISfServiceMonitor> out_interface, +                                       u64 reserved_input) {          LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input); -        IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -        rb.Push(ResultSuccess); -        rb.PushIpcInterface<INetworkServiceMonitor>(system); +        *out_interface = std::make_shared<ISfServiceMonitor>(system); +        R_SUCCEED();      } -}; -class ISfMonitorService final : public ServiceFramework<ISfMonitorService> { -public: -    explicit ISfMonitorService(Core::System& system_) -        : ServiceFramework{system_, "ISfMonitorService"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {0, &ISfMonitorService::Initialize, "Initialize"}, -            {288, &ISfMonitorService::GetGroupInfo, "GetGroupInfo"}, -            {320, nullptr, "GetLinkLevel"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } - -private: -    void Initialize(HLERequestContext& ctx) { -        LOG_WARNING(Service_LDN, "(STUBBED) called"); - -        IPC::ResponseBuilder rb{ctx, 3}; -        rb.Push(ResultSuccess); -        rb.Push(0); -    } - -    void GetGroupInfo(HLERequestContext& ctx) { -        LOG_WARNING(Service_LDN, "(STUBBED) called"); - -        struct GroupInfo { -            std::array<u8, 0x200> info; -        }; - -        GroupInfo group_info{}; - -        ctx.WriteBuffer(group_info); -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); -    } +    bool is_system{};  }; -class LP2PM final : public ServiceFramework<LP2PM> { +class ISfMonitorServiceCreator final : public ServiceFramework<ISfMonitorServiceCreator> {  public: -    explicit LP2PM(Core::System& system_) : ServiceFramework{system_, "lp2p:m"} { +    explicit ISfMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "lp2p:m"} {          // clang-format off          static const FunctionInfo functions[] = { -            {0, &LP2PM::CreateMonitorService, "CreateMonitorService"}, +            {0, C<&ISfMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"},          };          // clang-format on @@ -811,28 +126,27 @@ public:      }  private: -    void CreateMonitorService(HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        const u64 reserved_input = rp.Pop<u64>(); - +    Result CreateMonitorService(OutInterface<ISfMonitorService> out_interface, u64 reserved_input) {          LOG_INFO(Service_LDN, "called, reserved_input={}", reserved_input); -        IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -        rb.Push(ResultSuccess); -        rb.PushIpcInterface<ISfMonitorService>(system); +        *out_interface = std::make_shared<ISfMonitorService>(system); +        R_SUCCEED();      }  };  void LoopProcess(Core::System& system) {      auto server_manager = std::make_unique<ServerManager>(system); -    server_manager->RegisterNamedService("ldn:m", std::make_shared<LDNM>(system)); -    server_manager->RegisterNamedService("ldn:s", std::make_shared<LDNS>(system)); -    server_manager->RegisterNamedService("ldn:u", std::make_shared<LDNU>(system)); +    server_manager->RegisterNamedService("ldn:m", std::make_shared<IMonitorServiceCreator>(system)); +    server_manager->RegisterNamedService("ldn:s", std::make_shared<ISystemServiceCreator>(system)); +    server_manager->RegisterNamedService("ldn:u", std::make_shared<IUserServiceCreator>(system)); -    server_manager->RegisterNamedService("lp2p:app", std::make_shared<LP2PAPP>(system)); -    server_manager->RegisterNamedService("lp2p:sys", std::make_shared<LP2PSYS>(system)); -    server_manager->RegisterNamedService("lp2p:m", std::make_shared<LP2PM>(system)); +    server_manager->RegisterNamedService( +        "lp2p:app", std::make_shared<ISfServiceCreator>(system, false, "lp2p:app")); +    server_manager->RegisterNamedService( +        "lp2p:sys", std::make_shared<ISfServiceCreator>(system, true, "lp2p:sys")); +    server_manager->RegisterNamedService("lp2p:m", +                                         std::make_shared<ISfMonitorServiceCreator>(system));      ServerManager::RunServer(std::move(server_manager));  } 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 @@  #pragma once -#include "core/hle/kernel/k_event.h" -#include "core/hle/result.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/sm/sm.h" -  namespace Core {  class System;  } 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 {      Connected,  }; +enum class WirelessControllerRestriction : u32 { +    None, +    Default, +}; + +struct ConnectOption { +    union { +        u32 raw; +    }; +}; +static_assert(sizeof(ConnectOption) == 0x4, "ConnectOption is an invalid size"); +  struct NodeLatestUpdate {      NodeStateChange state_change;      INSERT_PADDING_BYTES(0x7); // Unknown @@ -139,9 +151,9 @@ static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size");  struct IntentId {      u64 local_communication_id; -    INSERT_PADDING_BYTES(0x2); // Reserved +    INSERT_PADDING_BYTES_NOINIT(0x2); // Reserved      u16 scene_id; -    INSERT_PADDING_BYTES(0x4); // Reserved +    INSERT_PADDING_BYTES_NOINIT(0x4); // Reserved  };  static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size"); @@ -152,13 +164,14 @@ struct NetworkId {  static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size");  struct Ssid { -    u8 length{}; -    std::array<char, SsidLengthMax + 1> raw{}; +    u8 length; +    std::array<char, SsidLengthMax + 1> raw;      Ssid() = default;      constexpr explicit Ssid(std::string_view data) {          length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); +        raw = {};          data.copy(raw.data(), length);          raw[length] = 0;      } @@ -181,7 +194,7 @@ using Ipv4Address = std::array<u8, 4>;  static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");  struct MacAddress { -    std::array<u8, 6> raw{}; +    std::array<u8, 6> raw;      friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default;  }; @@ -211,7 +224,7 @@ struct CommonNetworkInfo {      WifiChannel channel;      LinkLevel link_level;      PackedNetworkType network_type; -    INSERT_PADDING_BYTES(0x4); +    INSERT_PADDING_BYTES_NOINIT(0x4);  };  static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size"); @@ -221,9 +234,9 @@ struct NodeInfo {      s8 node_id;      u8 is_connected;      std::array<u8, UserNameBytesMax + 1> user_name; -    INSERT_PADDING_BYTES(0x1); // Reserved +    INSERT_PADDING_BYTES_NOINIT(0x1); // Reserved      s16 local_communication_version; -    INSERT_PADDING_BYTES(0x10); // Reserved +    INSERT_PADDING_BYTES_NOINIT(0x10); // Reserved  };  static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size"); @@ -232,14 +245,14 @@ struct LdnNetworkInfo {      SecurityMode security_mode;      AcceptPolicy station_accept_policy;      u8 has_action_frame; -    INSERT_PADDING_BYTES(0x2); // Padding +    INSERT_PADDING_BYTES_NOINIT(0x2); // Padding      u8 node_count_max;      u8 node_count;      std::array<NodeInfo, NodeCountMax> nodes; -    INSERT_PADDING_BYTES(0x2); // Reserved +    INSERT_PADDING_BYTES_NOINIT(0x2); // Reserved      u16 advertise_data_size;      std::array<u8, AdvertiseDataSizeMax> advertise_data; -    INSERT_PADDING_BYTES(0x8C); // Reserved +    INSERT_PADDING_BYTES_NOINIT(0x8C); // Reserved      u64 random_authentication_id;  };  static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size"); @@ -250,6 +263,7 @@ struct NetworkInfo {      LdnNetworkInfo ldn;  };  static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size"); +static_assert(std::is_trivial_v<NetworkInfo>, "NetworkInfo type must be trivially copyable.");  struct SecurityConfig {      SecurityMode security_mode; @@ -303,4 +317,36 @@ struct AddressList {  };  static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size"); +struct GroupInfo { +    std::array<u8, 0x200> info; +}; + +struct CreateNetworkConfig { +    SecurityConfig security_config; +    UserConfig user_config; +    INSERT_PADDING_BYTES(0x4); +    NetworkConfig network_config; +}; +static_assert(sizeof(CreateNetworkConfig) == 0x98, "CreateNetworkConfig is an invalid size"); + +#pragma pack(push, 4) +struct CreateNetworkConfigPrivate { +    SecurityConfig security_config; +    SecurityParameter security_parameter; +    UserConfig user_config; +    INSERT_PADDING_BYTES(0x4); +    NetworkConfig network_config; +}; +#pragma pack(pop) +static_assert(sizeof(CreateNetworkConfigPrivate) == 0xB8, +              "CreateNetworkConfigPrivate is an invalid size"); + +struct ConnectNetworkData { +    SecurityConfig security_config; +    UserConfig user_config; +    s32 local_communication_version; +    ConnectOption option; +}; +static_assert(sizeof(ConnectNetworkData) == 0x7c, "ConnectNetworkData is an invalid size"); +  } // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ldn/monitor_service.h" + +namespace Service::LDN { + +IMonitorService::IMonitorService(Core::System& system_) +    : ServiceFramework{system_, "IMonitorService"} { +    // clang-format off +    static const FunctionInfo functions[] = { +        {0, C<&IMonitorService::GetStateForMonitor>, "GetStateForMonitor"}, +        {1, nullptr, "GetNetworkInfoForMonitor"}, +        {2, nullptr, "GetIpv4AddressForMonitor"}, +        {3, nullptr, "GetDisconnectReasonForMonitor"}, +        {4, nullptr, "GetSecurityParameterForMonitor"}, +        {5, nullptr, "GetNetworkConfigForMonitor"}, +        {100, C<&IMonitorService::InitializeMonitor>, "InitializeMonitor"}, +        {101, nullptr, "FinalizeMonitor"}, +    }; +    // clang-format on + +    RegisterHandlers(functions); +} + +IMonitorService::~IMonitorService() = default; + +Result IMonitorService::GetStateForMonitor(Out<State> out_state) { +    LOG_INFO(Service_LDN, "called"); + +    *out_state = state; +    R_SUCCEED(); +} + +Result IMonitorService::InitializeMonitor() { +    LOG_INFO(Service_LDN, "called"); + +    state = State::Initialized; +    R_SUCCEED(); +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/ldn/ldn_types.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::LDN { + +class IMonitorService final : public ServiceFramework<IMonitorService> { +public: +    explicit IMonitorService(Core::System& system_); +    ~IMonitorService() override; + +private: +    Result GetStateForMonitor(Out<State> out_state); +    Result InitializeMonitor(); + +    State state{State::None}; +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ldn/ldn_types.h" +#include "core/hle/service/ldn/sf_monitor_service.h" + +namespace Service::LDN { + +ISfMonitorService::ISfMonitorService(Core::System& system_) +    : ServiceFramework{system_, "ISfMonitorService"} { +    // clang-format off +        static const FunctionInfo functions[] = { +            {0, C<&ISfMonitorService::Initialize>, "Initialize"}, +            {288, C<&ISfMonitorService::GetGroupInfo>, "GetGroupInfo"}, +            {320, nullptr, "GetLinkLevel"}, +        }; +    // clang-format on + +    RegisterHandlers(functions); +} + +ISfMonitorService::~ISfMonitorService() = default; + +Result ISfMonitorService::Initialize(Out<u32> out_value) { +    LOG_WARNING(Service_LDN, "(STUBBED) called"); + +    *out_value = 0; +    R_SUCCEED(); +} + +Result ISfMonitorService::GetGroupInfo( +    OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) { +    LOG_WARNING(Service_LDN, "(STUBBED) called"); + +    *out_group_info = GroupInfo{}; +    R_SUCCEED(); +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::LDN { +struct GroupInfo; + +class ISfMonitorService final : public ServiceFramework<ISfMonitorService> { +public: +    explicit ISfMonitorService(Core::System& system_); +    ~ISfMonitorService() override; + +private: +    Result Initialize(Out<u32> out_value); +    Result GetGroupInfo(OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info); +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/ldn/sf_service.h" + +namespace Service::LDN { + +ISfService::ISfService(Core::System& system_) : ServiceFramework{system_, "ISfService"} { +    // clang-format off +    static const FunctionInfo functions[] = { +            {0, nullptr, "Initialize"}, +            {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"}, +            {264, nullptr, "GetNetworkInterfaceLastError"}, +            {272, nullptr, "GetRole"}, +            {280, nullptr, "GetAdvertiseData"}, +            {288, nullptr, "GetGroupInfo"}, +            {296, nullptr, "GetGroupInfo2"}, +            {304, nullptr, "GetGroupOwner"}, +            {312, nullptr, "GetIpConfig"}, +            {320, nullptr, "GetLinkLevel"}, +            {512, nullptr, "Scan"}, +            {768, nullptr, "CreateGroup"}, +            {776, nullptr, "DestroyGroup"}, +            {784, nullptr, "SetAdvertiseData"}, +            {1536, nullptr, "SendToOtherGroup"}, +            {1544, nullptr, "RecvFromOtherGroup"}, +            {1552, nullptr, "AddAcceptableGroupId"}, +            {1560, nullptr, "ClearAcceptableGroupId"}, +    }; +    // clang-format on + +    RegisterHandlers(functions); +} + +ISfService::~ISfService() = default; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::LDN { + +class ISfService final : public ServiceFramework<ISfService> { +public: +    explicit ISfService(Core::System& system_); +    ~ISfService() override; +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ldn/ldn_types.h" +#include "core/hle/service/ldn/sf_service_monitor.h" + +namespace Service::LDN { + +ISfServiceMonitor::ISfServiceMonitor(Core::System& system_) +    : ServiceFramework{system_, "ISfServiceMonitor"} { +    // clang-format off +        static const FunctionInfo functions[] = { +            {0, C<&ISfServiceMonitor::Initialize>, "Initialize"}, +            {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"}, +            {264, nullptr, "GetNetworkInterfaceLastError"}, +            {272, nullptr, "GetRole"}, +            {280, nullptr, "GetAdvertiseData"}, +            {281, nullptr, "GetAdvertiseData2"}, +            {288, C<&ISfServiceMonitor::GetGroupInfo>, "GetGroupInfo"}, +            {296, nullptr, "GetGroupInfo2"}, +            {304, nullptr, "GetGroupOwner"}, +            {312, nullptr, "GetIpConfig"}, +            {320, nullptr, "GetLinkLevel"}, +            {328, nullptr, "AttachJoinEvent"}, +            {336, nullptr, "GetMembers"}, +        }; +    // clang-format on + +    RegisterHandlers(functions); +} + +ISfServiceMonitor::~ISfServiceMonitor() = default; + +Result ISfServiceMonitor::Initialize(Out<u32> out_value) { +    LOG_WARNING(Service_LDN, "(STUBBED) called"); + +    *out_value = 0; +    R_SUCCEED(); +} + +Result ISfServiceMonitor::GetGroupInfo( +    OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) { +    LOG_WARNING(Service_LDN, "(STUBBED) called"); + +    *out_group_info = GroupInfo{}; +    R_SUCCEED(); +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::LDN { +struct GroupInfo; + +class ISfServiceMonitor final : public ServiceFramework<ISfServiceMonitor> { +public: +    explicit ISfServiceMonitor(Core::System& system_); +    ~ISfServiceMonitor() override; + +private: +    Result Initialize(Out<u32> out_value); +    Result GetGroupInfo(OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info); +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ldn/system_local_communication_service.h" + +namespace Service::LDN { + +ISystemLocalCommunicationService::ISystemLocalCommunicationService(Core::System& system_) +    : ServiceFramework{system_, "ISystemLocalCommunicationService"} { +    // clang-format off +        static const FunctionInfo functions[] = { +            {0, nullptr, "GetState"}, +            {1, nullptr, "GetNetworkInfo"}, +            {2, nullptr, "GetIpv4Address"}, +            {3, nullptr, "GetDisconnectReason"}, +            {4, nullptr, "GetSecurityParameter"}, +            {5, nullptr, "GetNetworkConfig"}, +            {100, nullptr, "AttachStateChangeEvent"}, +            {101, nullptr, "GetNetworkInfoLatestUpdate"}, +            {102, nullptr, "Scan"}, +            {103, nullptr, "ScanPrivate"}, +            {104, nullptr, "SetWirelessControllerRestriction"}, +            {200, nullptr, "OpenAccessPoint"}, +            {201, nullptr, "CloseAccessPoint"}, +            {202, nullptr, "CreateNetwork"}, +            {203, nullptr, "CreateNetworkPrivate"}, +            {204, nullptr, "DestroyNetwork"}, +            {205, nullptr, "Reject"}, +            {206, nullptr, "SetAdvertiseData"}, +            {207, nullptr, "SetStationAcceptPolicy"}, +            {208, nullptr, "AddAcceptFilterEntry"}, +            {209, nullptr, "ClearAcceptFilter"}, +            {300, nullptr, "OpenStation"}, +            {301, nullptr, "CloseStation"}, +            {302, nullptr, "Connect"}, +            {303, nullptr, "ConnectPrivate"}, +            {304, nullptr, "Disconnect"}, +            {400, nullptr, "InitializeSystem"}, +            {401, nullptr, "FinalizeSystem"}, +            {402, nullptr, "SetOperationMode"}, +            {403, C<&ISystemLocalCommunicationService::InitializeSystem2>, "InitializeSystem2"}, +        }; +    // clang-format on + +    RegisterHandlers(functions); +} + +ISystemLocalCommunicationService::~ISystemLocalCommunicationService() = default; + +Result ISystemLocalCommunicationService::InitializeSystem2() { +    LOG_WARNING(Service_LDN, "(STUBBED) called"); +    R_SUCCEED(); +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::LDN { + +class ISystemLocalCommunicationService final +    : public ServiceFramework<ISystemLocalCommunicationService> { +public: +    explicit ISystemLocalCommunicationService(Core::System& system_); +    ~ISystemLocalCommunicationService() override; + +private: +    Result InitializeSystem2(); +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <memory> + +#include "core/core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ldn/ldn_results.h" +#include "core/hle/service/ldn/ldn_types.h" +#include "core/hle/service/ldn/user_local_communication_service.h" +#include "core/hle/service/server_manager.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" +#include "network/network.h" + +// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent +#undef CreateEvent + +namespace Service::LDN { + +IUserLocalCommunicationService::IUserLocalCommunicationService(Core::System& system_) +    : ServiceFramework{system_, "IUserLocalCommunicationService"}, +      service_context{system, "IUserLocalCommunicationService"}, +      room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} { +    // clang-format off +        static const FunctionInfo functions[] = { +            {0, C<&IUserLocalCommunicationService::GetState>, "GetState"}, +            {1, C<&IUserLocalCommunicationService::GetNetworkInfo>, "GetNetworkInfo"}, +            {2, C<&IUserLocalCommunicationService::GetIpv4Address>, "GetIpv4Address"}, +            {3, C<&IUserLocalCommunicationService::GetDisconnectReason>, "GetDisconnectReason"}, +            {4, C<&IUserLocalCommunicationService::GetSecurityParameter>, "GetSecurityParameter"}, +            {5, C<&IUserLocalCommunicationService::GetNetworkConfig>, "GetNetworkConfig"}, +            {100, C<&IUserLocalCommunicationService::AttachStateChangeEvent>, "AttachStateChangeEvent"}, +            {101, C<&IUserLocalCommunicationService::GetNetworkInfoLatestUpdate>, "GetNetworkInfoLatestUpdate"}, +            {102, C<&IUserLocalCommunicationService::Scan>, "Scan"}, +            {103, C<&IUserLocalCommunicationService::ScanPrivate>, "ScanPrivate"}, +            {104, C<&IUserLocalCommunicationService::SetWirelessControllerRestriction>, "SetWirelessControllerRestriction"}, +            {200, C<&IUserLocalCommunicationService::OpenAccessPoint>, "OpenAccessPoint"}, +            {201, C<&IUserLocalCommunicationService::CloseAccessPoint>, "CloseAccessPoint"}, +            {202, C<&IUserLocalCommunicationService::CreateNetwork>, "CreateNetwork"}, +            {203, C<&IUserLocalCommunicationService::CreateNetworkPrivate>, "CreateNetworkPrivate"}, +            {204, C<&IUserLocalCommunicationService::DestroyNetwork>, "DestroyNetwork"}, +            {205, nullptr, "Reject"}, +            {206, C<&IUserLocalCommunicationService::SetAdvertiseData>, "SetAdvertiseData"}, +            {207, C<&IUserLocalCommunicationService::SetStationAcceptPolicy>, "SetStationAcceptPolicy"}, +            {208, C<&IUserLocalCommunicationService::AddAcceptFilterEntry>, "AddAcceptFilterEntry"}, +            {209, nullptr, "ClearAcceptFilter"}, +            {300, C<&IUserLocalCommunicationService::OpenStation>, "OpenStation"}, +            {301, C<&IUserLocalCommunicationService::CloseStation>, "CloseStation"}, +            {302, C<&IUserLocalCommunicationService::Connect>, "Connect"}, +            {303, nullptr, "ConnectPrivate"}, +            {304, C<&IUserLocalCommunicationService::Disconnect>, "Disconnect"}, +            {400, C<&IUserLocalCommunicationService::Initialize>, "Initialize"}, +            {401, C<&IUserLocalCommunicationService::Finalize>, "Finalize"}, +            {402, C<&IUserLocalCommunicationService::Initialize2>, "Initialize2"}, +        }; +    // clang-format on + +    RegisterHandlers(functions); + +    state_change_event = +        service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent"); +} + +IUserLocalCommunicationService::~IUserLocalCommunicationService() { +    if (is_initialized) { +        if (auto room_member = room_network.GetRoomMember().lock()) { +            room_member->Unbind(ldn_packet_received); +        } +    } + +    service_context.CloseEvent(state_change_event); +} + +Result IUserLocalCommunicationService::GetState(Out<State> out_state) { +    *out_state = State::Error; + +    if (is_initialized) { +        *out_state = lan_discovery.GetState(); +    } + +    LOG_INFO(Service_LDN, "called, state={}", *out_state); + +    R_SUCCEED(); +} + +Result IUserLocalCommunicationService::GetNetworkInfo( +    OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info) { +    LOG_INFO(Service_LDN, "called"); + +    R_RETURN(lan_discovery.GetNetworkInfo(*out_network_info)); +} + +Result IUserLocalCommunicationService::GetIpv4Address(Out<Ipv4Address> out_current_address, +                                                      Out<Ipv4Address> out_subnet_mask) { +    LOG_INFO(Service_LDN, "called"); +    const auto network_interface = Network::GetSelectedNetworkInterface(); + +    R_UNLESS(network_interface.has_value(), ResultNoIpAddress); + +    *out_current_address = {Network::TranslateIPv4(network_interface->ip_address)}; +    *out_subnet_mask = {Network::TranslateIPv4(network_interface->subnet_mask)}; + +    // When we're connected to a room, spoof the hosts IP address +    if (auto room_member = room_network.GetRoomMember().lock()) { +        if (room_member->IsConnected()) { +            *out_current_address = room_member->GetFakeIpAddress(); +        } +    } + +    std::reverse(std::begin(*out_current_address), std::end(*out_current_address)); // ntohl +    std::reverse(std::begin(*out_subnet_mask), std::end(*out_subnet_mask));         // ntohl +    R_SUCCEED(); +} + +Result IUserLocalCommunicationService::GetDisconnectReason( +    Out<DisconnectReason> out_disconnect_reason) { +    LOG_INFO(Service_LDN, "called"); + +    *out_disconnect_reason = lan_discovery.GetDisconnectReason(); +    R_SUCCEED(); +} + +Result IUserLocalCommunicationService::GetSecurityParameter( +    Out<SecurityParameter> out_security_parameter) { +    LOG_INFO(Service_LDN, "called"); + +    NetworkInfo info{}; +    R_TRY(lan_discovery.GetNetworkInfo(info)); + +    out_security_parameter->session_id = info.network_id.session_id; +    std::memcpy(out_security_parameter->data.data(), info.ldn.security_parameter.data(), +                sizeof(SecurityParameter::data)); +    R_SUCCEED(); +} + +Result IUserLocalCommunicationService::GetNetworkConfig(Out<NetworkConfig> out_network_config) { +    LOG_INFO(Service_LDN, "called"); + +    NetworkInfo info{}; +    R_TRY(lan_discovery.GetNetworkInfo(info)); + +    out_network_config->intent_id = info.network_id.intent_id; +    out_network_config->channel = info.common.channel; +    out_network_config->node_count_max = info.ldn.node_count_max; +    out_network_config->local_communication_version = info.ldn.nodes[0].local_communication_version; +    R_SUCCEED(); +} + +Result IUserLocalCommunicationService::AttachStateChangeEvent( +    OutCopyHandle<Kernel::KReadableEvent> out_event) { +    LOG_INFO(Service_LDN, "called"); + +    *out_event = &state_change_event->GetReadableEvent(); +    R_SUCCEED(); +} + +Result IUserLocalCommunicationService::GetNetworkInfoLatestUpdate( +    OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info, +    OutArray<NodeLatestUpdate, BufferAttr_HipcPointer> out_node_latest_update) { +    LOG_INFO(Service_LDN, "called"); + +    R_UNLESS(!out_node_latest_update.empty(), ResultBadInput); + +    R_RETURN(lan_discovery.GetNetworkInfo(*out_network_info, out_node_latest_update)); +} + +Result IUserLocalCommunicationService::Scan( +    Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter, +    OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info) { +    LOG_INFO(Service_LDN, "called, channel={}, filter_scan_flag={}, filter_network_type={}", +             channel, scan_filter.flag, scan_filter.network_type); + +    R_UNLESS(!out_network_info.empty(), ResultBadInput); +    R_RETURN(lan_discovery.Scan(out_network_info, *network_count, scan_filter)); +} + +Result IUserLocalCommunicationService::ScanPrivate( +    Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter, +    OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info) { +    LOG_INFO(Service_LDN, "called, channel={}, filter_scan_flag={}, filter_network_type={}", +             channel, scan_filter.flag, scan_filter.network_type); + +    R_UNLESS(out_network_info.empty(), ResultBadInput); +    R_RETURN(lan_discovery.Scan(out_network_info, *network_count, scan_filter)); +} + +Result IUserLocalCommunicationService::SetWirelessControllerRestriction( +    WirelessControllerRestriction wireless_restriction) { +    LOG_WARNING(Service_LDN, "(STUBBED) called"); +    R_SUCCEED(); +} + +Result IUserLocalCommunicationService::OpenAccessPoint() { +    LOG_INFO(Service_LDN, "called"); + +    R_RETURN(lan_discovery.OpenAccessPoint()); +} + +Result IUserLocalCommunicationService::CloseAccessPoint() { +    LOG_INFO(Service_LDN, "called"); + +    R_RETURN(lan_discovery.CloseAccessPoint()); +} + +Result IUserLocalCommunicationService::CreateNetwork(const CreateNetworkConfig& create_config) { +    LOG_INFO(Service_LDN, "called"); + +    R_RETURN(lan_discovery.CreateNetwork(create_config.security_config, create_config.user_config, +                                         create_config.network_config)); +} + +Result IUserLocalCommunicationService::CreateNetworkPrivate( +    const CreateNetworkConfigPrivate& create_config, +    InArray<AddressEntry, BufferAttr_HipcPointer> address_list) { +    LOG_INFO(Service_LDN, "called"); + +    R_RETURN(lan_discovery.CreateNetwork(create_config.security_config, create_config.user_config, +                                         create_config.network_config)); +} + +Result IUserLocalCommunicationService::DestroyNetwork() { +    LOG_INFO(Service_LDN, "called"); + +    R_RETURN(lan_discovery.DestroyNetwork()); +} + +Result IUserLocalCommunicationService::SetAdvertiseData( +    InBuffer<BufferAttr_HipcAutoSelect> buffer_data) { +    LOG_INFO(Service_LDN, "called"); + +    R_RETURN(lan_discovery.SetAdvertiseData(buffer_data)); +} + +Result IUserLocalCommunicationService::SetStationAcceptPolicy(AcceptPolicy accept_policy) { +    LOG_WARNING(Service_LDN, "(STUBBED) called"); +    R_SUCCEED(); +} + +Result IUserLocalCommunicationService::AddAcceptFilterEntry(MacAddress mac_address) { +    LOG_WARNING(Service_LDN, "(STUBBED) called"); +    R_SUCCEED(); +} + +Result IUserLocalCommunicationService::OpenStation() { +    LOG_INFO(Service_LDN, "called"); + +    R_RETURN(lan_discovery.OpenStation()); +} + +Result IUserLocalCommunicationService::CloseStation() { +    LOG_INFO(Service_LDN, "called"); + +    R_RETURN(lan_discovery.CloseStation()); +} + +Result IUserLocalCommunicationService::Connect( +    const ConnectNetworkData& connect_data, +    InLargeData<NetworkInfo, BufferAttr_HipcPointer> network_info) { +    LOG_INFO(Service_LDN, +             "called, passphrase_size={}, security_mode={}, " +             "local_communication_version={}", +             connect_data.security_config.passphrase_size, +             connect_data.security_config.security_mode, connect_data.local_communication_version); + +    R_RETURN(lan_discovery.Connect(*network_info, connect_data.user_config, +                                   static_cast<u16>(connect_data.local_communication_version))); +} + +Result IUserLocalCommunicationService::Disconnect() { +    LOG_INFO(Service_LDN, "called"); + +    R_RETURN(lan_discovery.Disconnect()); +} + +Result IUserLocalCommunicationService::Initialize(ClientProcessId aruid) { +    LOG_INFO(Service_LDN, "called, process_id={}", aruid.pid); + +    const auto network_interface = Network::GetSelectedNetworkInterface(); +    R_UNLESS(network_interface, ResultAirplaneModeEnabled); + +    if (auto room_member = room_network.GetRoomMember().lock()) { +        ldn_packet_received = room_member->BindOnLdnPacketReceived( +            [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); }); +    } else { +        LOG_ERROR(Service_LDN, "Couldn't bind callback!"); +        R_RETURN(ResultAirplaneModeEnabled); +    } + +    lan_discovery.Initialize([&]() { OnEventFired(); }); +    is_initialized = true; +    R_SUCCEED(); +} + +Result IUserLocalCommunicationService::Finalize() { +    LOG_INFO(Service_LDN, "called"); +    if (auto room_member = room_network.GetRoomMember().lock()) { +        room_member->Unbind(ldn_packet_received); +    } + +    is_initialized = false; + +    R_RETURN(lan_discovery.Finalize()); +} + +Result IUserLocalCommunicationService::Initialize2(u32 version, ClientProcessId process_id) { +    LOG_INFO(Service_LDN, "called, version={}, process_id={}", version, process_id.pid); +    R_RETURN(Initialize(process_id)); +} + +void IUserLocalCommunicationService::OnLDNPacketReceived(const Network::LDNPacket& packet) { +    lan_discovery.ReceivePacket(packet); +} + +void IUserLocalCommunicationService::OnEventFired() { +    state_change_event->Signal(); +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/ldn/lan_discovery.h" +#include "core/hle/service/ldn/ldn_types.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Network { +class RoomNetwork; +} + +namespace Service::LDN { + +class IUserLocalCommunicationService final +    : public ServiceFramework<IUserLocalCommunicationService> { +public: +    explicit IUserLocalCommunicationService(Core::System& system_); +    ~IUserLocalCommunicationService() override; + +private: +    Result GetState(Out<State> out_state); + +    Result GetNetworkInfo(OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info); + +    Result GetIpv4Address(Out<Ipv4Address> out_current_address, Out<Ipv4Address> out_subnet_mask); + +    Result GetDisconnectReason(Out<DisconnectReason> out_disconnect_reason); + +    Result GetSecurityParameter(Out<SecurityParameter> out_security_parameter); + +    Result GetNetworkConfig(Out<NetworkConfig> out_network_config); + +    Result AttachStateChangeEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + +    Result GetNetworkInfoLatestUpdate( +        OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info, +        OutArray<NodeLatestUpdate, BufferAttr_HipcPointer> out_node_latest_update); + +    Result Scan(Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter, +                OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info); + +    Result ScanPrivate(Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter, +                       OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info); + +    Result SetWirelessControllerRestriction(WirelessControllerRestriction wireless_restriction); + +    Result OpenAccessPoint(); + +    Result CloseAccessPoint(); + +    Result CreateNetwork(const CreateNetworkConfig& create_network_Config); + +    Result CreateNetworkPrivate(const CreateNetworkConfigPrivate& create_network_Config, +                                InArray<AddressEntry, BufferAttr_HipcPointer> address_list); + +    Result DestroyNetwork(); + +    Result SetAdvertiseData(InBuffer<BufferAttr_HipcAutoSelect> buffer_data); + +    Result SetStationAcceptPolicy(AcceptPolicy accept_policy); + +    Result AddAcceptFilterEntry(MacAddress mac_address); + +    Result OpenStation(); + +    Result CloseStation(); + +    Result Connect(const ConnectNetworkData& connect_data, +                   InLargeData<NetworkInfo, BufferAttr_HipcPointer> network_info); + +    Result Disconnect(); + +    Result Initialize(ClientProcessId aruid); + +    Result Finalize(); + +    Result Initialize2(u32 version, ClientProcessId aruid); + +private: +    /// Callback to parse and handle a received LDN packet. +    void OnLDNPacketReceived(const Network::LDNPacket& packet); +    void OnEventFired(); + +    KernelHelpers::ServiceContext service_context; +    Kernel::KEvent* state_change_event; +    Network::RoomNetwork& room_network; +    LANDiscovery lan_discovery; + +    // Callback identifier for the OnLDNPacketReceived event. +    Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received; + +    bool is_initialized{}; +}; + +} // 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 {          };      }      rescaleable = false; +    is_sparse = config.is_sparse != 0;      tile_width_spacing = config.tile_width_spacing;      if (config.texture_type != TextureType::Texture2D &&          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 {      bool downscaleable = false;      bool forced_flushed = false;      bool dma_downloaded = false; +    bool is_sparse = false;  };  } // 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                              [&](ImageId id, Image&) { deleted_images.push_back(id); });      for (const ImageId id : deleted_images) {          Image& image = slot_images[id]; -        if (True(image.flags & ImageFlagBits::CpuModified)) { -            continue; +        if (False(image.flags & ImageFlagBits::CpuModified)) { +            image.flags |= ImageFlagBits::CpuModified; +            if (True(image.flags & ImageFlagBits::Tracked)) { +                UntrackImage(image, id); +            }          } -        image.flags |= ImageFlagBits::CpuModified; +          if (True(image.flags & ImageFlagBits::Remapped)) {              continue;          }          image.flags |= ImageFlagBits::Remapped; -        if (True(image.flags & ImageFlagBits::Tracked)) { -            UntrackImage(image, id); -        }      }  } @@ -1469,7 +1469,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA      const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr);      Image& new_image = slot_images[new_image_id]; -    if (!gpu_memory->IsContinuousRange(new_image.gpu_addr, new_image.guest_size_bytes)) { +    if (!gpu_memory->IsContinuousRange(new_image.gpu_addr, new_image.guest_size_bytes) && +        new_info.is_sparse) {          new_image.flags |= ImageFlagBits::Sparse;      } 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) {             QStringLiteral());      // Core -    INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), QStringLiteral()); -    INSERT(Settings, memory_layout_mode, tr("Memory Layout"), QStringLiteral()); +    INSERT( +        Settings, use_multi_core, tr("Multicore CPU Emulation"), +        tr("This option increases CPU emulation thread use from 1 to the Switch’s maximum of 4.\n" +           "This is mainly a debug option and shouldn’t be disabled.")); +    INSERT( +        Settings, memory_layout_mode, tr("Memory Layout"), +        tr("Increases the amount of emulated RAM from the stock 4GB of the retail Switch to the " +           "developer kit's 8/6GB.\nIt’s doesn’t improve stability or performance and is intended " +           "to let big texture mods fit in emulated RAM.\nEnabling it will increase memory " +           "use. It is not recommended to enable unless a specific game with a texture mod needs " +           "it."));      INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral()); -    INSERT(Settings, speed_limit, tr("Limit Speed Percent"), QStringLiteral()); +    INSERT(Settings, speed_limit, tr("Limit Speed Percent"), +           tr("Controls the game's maximum rendering speed, but it’s up to each game if it runs " +              "faster or not.\n200% for a 30 FPS game is 60 FPS, and for a " +              "60 FPS game it will be 120 FPS.\nDisabling it means unlocking the framerate to the " +              "maximum your PC can reach."));      // Cpu -    INSERT(Settings, cpu_accuracy, tr("Accuracy:"), QStringLiteral()); +    INSERT(Settings, cpu_accuracy, tr("Accuracy:"), +           tr("This setting controls the accuracy of the emulated CPU.\nDon't change this unless " +              "you know what you are doing."));      INSERT(Settings, cpu_backend, tr("Backend:"), QStringLiteral());      // Cpu Debug @@ -80,34 +95,75 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {             tr("This option improves the speed of 32 bits ASIMD floating-point functions by running "                "with incorrect rounding modes."));      INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"), -           tr("This option improves speed by removing NaN checking. Please note this also reduces " +           tr("This option improves speed by removing NaN checking.\nPlease note this also reduces "                "accuracy of certain floating-point instructions."));      INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"),             tr("This option improves speed by eliminating a safety check before every memory " -              "read/write " -              "in guest. Disabling it may allow a game to read/write the emulator's memory.")); +              "read/write in guest.\nDisabling it may allow a game to read/write the emulator's " +              "memory."));      INSERT(          Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"),          tr("This option improves speed by relying only on the semantics of cmpxchg to ensure " -           "safety of exclusive access instructions. Please note this may result in deadlocks and " +           "safety of exclusive access instructions.\nPlease note this may result in deadlocks and "             "other race conditions."));      // Renderer -    INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral()); -    INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral()); -    INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral()); -    INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral()); +    INSERT( +        Settings, renderer_backend, tr("API:"), +        tr("Switches between the available graphics APIs.\nVulkan is recommended in most cases.")); +    INSERT(Settings, vulkan_device, tr("Device:"), +           tr("This setting selects the GPU to use with the Vulkan backend.")); +    INSERT(Settings, shader_backend, tr("Shader Backend:"), +           tr("The shader backend to use for the OpenGL renderer.\nGLSL is the fastest in " +              "performance and the best in rendering accuracy.\n" +              "GLASM is a deprecated NVIDIA-only backend that offers much better shader building " +              "performance at the cost of FPS and rendering accuracy.\n" +              "SPIR-V compiles the fastest, but yields poor results on most GPU drivers.")); +    INSERT(Settings, resolution_setup, tr("Resolution:"), +           tr("Forces the game to render at a different resolution.\nHigher resolutions require " +              "much more VRAM and bandwidth.\n" +              "Options lower than 1X can cause rendering issues."));      INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral()); -    INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral()); -    INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral()); -    INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral()); -    INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral()); -    INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral()); -    INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"), -           QStringLiteral()); -    INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral()); -    INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral()); -    INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), QStringLiteral()); +    INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), +           tr("Determines how sharpened the image will look while using FSR’s dynamic contrast.")); +    INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), +           tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA has a " +              "lower performance impact and can produce a better and more stable picture under " +              "very low resolutions.")); +    INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), +           tr("The method used to render the window in fullscreen.\nBorderless offers the best " +              "compatibility with the on-screen keyboard that some games request for " +              "input.\nExclusive " +              "fullscreen may offer better performance and better Freesync/Gsync support.")); +    INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), +           tr("Stretches the game to fit the specified aspect ratio.\nSwitch games only support " +              "16:9, so custom game mods are required to get other ratios.\nAlso controls the " +              "aspect ratio of captured screenshots.")); +    INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), +           tr("Allows saving shaders to storage for faster loading on following game " +              "boots.\nDisabling " +              "it is only intended for debugging.")); +    INSERT( +        Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"), +        tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled.")); +    INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), +           tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for " +              "decoding, or perform no decoding at all (black screen on videos).\n" +              "In most cases, GPU decoding provides the best performance.")); +    INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), +           tr("This option controls how ASTC textures should be decoded.\n" +              "CPU: Use the CPU for decoding, slowest but safest method.\n" +              "GPU: Use the GPU's compute shaders to decode ASTC textures, recommended for most " +              "games and users.\n" +              "CPU Asynchronously: Use the CPU to decode ASTC textures as they arrive. Completely " +              "eliminates ASTC decoding\nstuttering at the cost of rendering issues while the " +              "texture is being decoded.")); +    INSERT( +        Settings, astc_recompression, tr("ASTC Recompression Method:"), +        tr("Almost all desktop and laptop dedicated GPUs lack support for ASTC textures, forcing " +           "the emulator to decompress to an intermediate format any card supports, RGBA8.\n" +           "This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but " +           "negatively affecting image quality."));      INSERT(          Settings, vsync_mode, tr("VSync Mode:"),          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) {      // Renderer (Advanced Graphics)      INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"), -           QStringLiteral()); +           tr("Slightly improves performance by moving presentation to a separate CPU thread."));      INSERT(          Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"),          tr("Runs work in the background while waiting for graphics commands to keep the GPU from "             "lowering its clock speed.")); -    INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral()); -    INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral()); -    INSERT( -        Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"), -        tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature " -           "is experimental.")); +    INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), +           tr("Controls the quality of texture rendering at oblique angles.\nIt’s a light setting " +              "and safe to set at 16x on most GPUs.")); +    INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), +           tr("GPU emulation accuracy.\nMost games render fine with Normal, but High is still " +              "required for some.\nParticles tend to only render correctly with High " +              "accuracy.\nExtreme should only be used for debugging.\nThis option can " +              "be changed while playing.\nSome games may require booting on high to render " +              "properly.")); +    INSERT(Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"), +           tr("Enables asynchronous shader compilation, which may reduce shader stutter.\nThis " +              "feature " +              "is experimental."));      INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"),             tr("Enables Fast GPU Time. This option will force most games to run at their highest "                "native resolution."));      INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"), -           tr("Enables GPU vendor-specific pipeline cache. This option can improve shader loading " +           tr("Enables GPU vendor-specific pipeline cache.\nThis option can improve shader loading "                "time significantly in cases where the Vulkan driver does not store pipeline cache "                "files internally."));      INSERT( @@ -157,19 +220,27 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {      // Renderer (Debug)      // System -    INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral()); +    INSERT(Settings, rng_seed, tr("RNG Seed"), +           tr("Controls the seed of the random number generator.\nMainly used for speedrunning " +              "purposes."));      INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral()); -    INSERT(Settings, device_name, tr("Device Name"), QStringLiteral()); -    INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), QStringLiteral()); +    INSERT(Settings, device_name, tr("Device Name"), tr("The name of the emulated Switch.")); +    INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), +           tr("This option allows to change the emulated clock of the Switch.\n" +              "Can be used to manipulate time in games."));      INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral());      INSERT(Settings, custom_rtc_offset, QStringLiteral(" "),             QStringLiteral("The number of seconds from the current unix time"));      INSERT(Settings, language_index, tr("Language:"),             tr("Note: this can be overridden when region setting is auto-select")); -    INSERT(Settings, region_index, tr("Region:"), QStringLiteral()); -    INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral()); +    INSERT(Settings, region_index, tr("Region:"), tr("The region of the emulated Switch.")); +    INSERT(Settings, time_zone_index, tr("Time Zone:"), +           tr("The time zone of the emulated Switch."));      INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral()); -    INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral()); +    INSERT(Settings, use_docked_mode, tr("Console Mode:"), +           tr("Selects if the console is emulated in Docked or Handheld mode.\nGames will change " +              "their resolution, details and supported controllers and depending on this setting.\n" +              "Setting to Handheld can help improve performance for low end systems."));      INSERT(Settings, current_user, QStringLiteral(), QStringLiteral());      // Controls @@ -187,14 +258,19 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {      // Ui      // Ui General -    INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), QStringLiteral()); +    INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), +           tr("Ask to select a user profile on each boot, useful if multiple people use yuzu on " +              "the same PC."));      INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"), -           QStringLiteral()); +           tr("This setting pauses yuzu when focusing other windows."));      INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"), -           QStringLiteral()); -    INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral()); +           tr("This setting overrides game prompts asking to confirm stopping the game.\nEnabling " +              "it bypasses such prompts and directly exits the emulation.")); +    INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), +           tr("This setting hides the mouse after 2.5s of inactivity."));      INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), -           QStringLiteral()); +           tr("Forcibly disables the use of the controller applet by guests.\nWhen a guest " +              "attempts to open the controller applet, it is immediately closed."));      // Linux      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      if (type != Core::HID::ControllerTriggerType::Button) {          return;      } -    if (!Settings::values.controller_navigation) { -        return; -    } -    if (button_sequence.npad.raw == Core::HID::NpadButton::None) { +    if (button_sequence.npad.raw == Core::HID::NpadButton::None && +        button_sequence.capture.raw == 0 && button_sequence.home.raw == 0) {          return;      }  | 
