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; } |