diff options
Diffstat (limited to 'src')
578 files changed, 23258 insertions, 14615 deletions
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index 7890b30ca..b037fc055 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ SPDX-License-Identifier: GPL-3.0-or-later <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> + <uses-permission android:name="android.permission.VIBRATE" /> <application android:name="org.yuzu.yuzu_emu.YuzuApplication" diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 6ebb46af7..02a20dacf 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -3,24 +3,21 @@ package org.yuzu.yuzu_emu -import android.app.Dialog import android.content.DialogInterface import android.net.Uri -import android.os.Bundle import android.text.Html import android.text.method.LinkMovementMethod import android.view.Surface import android.view.View import android.widget.TextView import androidx.annotation.Keep -import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder import java.lang.ref.WeakReference import org.yuzu.yuzu_emu.activities.EmulationActivity +import org.yuzu.yuzu_emu.fragments.CoreErrorDialogFragment import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.Log -import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable import org.yuzu.yuzu_emu.model.InstallResult import org.yuzu.yuzu_emu.model.Patch import org.yuzu.yuzu_emu.model.GameVerificationResult @@ -30,34 +27,6 @@ import org.yuzu.yuzu_emu.model.GameVerificationResult * with the native side of the Yuzu code. */ object NativeLibrary { - /** - * Default controller id for each device - */ - const val Player1Device = 0 - const val Player2Device = 1 - const val Player3Device = 2 - const val Player4Device = 3 - const val Player5Device = 4 - const val Player6Device = 5 - const val Player7Device = 6 - const val Player8Device = 7 - const val ConsoleDevice = 8 - - /** - * Controller type for each device - */ - const val ProController = 3 - const val Handheld = 4 - const val JoyconDual = 5 - const val JoyconLeft = 6 - const val JoyconRight = 7 - const val GameCube = 8 - const val Pokeball = 9 - const val NES = 10 - const val SNES = 11 - const val N64 = 12 - const val SegaGenesis = 13 - @JvmField var sEmulationActivity = WeakReference<EmulationActivity?>(null) @@ -127,112 +96,6 @@ object NativeLibrary { FileUtil.getFilename(Uri.parse(path)) } - /** - * Returns true if pro controller isn't available and handheld is - */ - external fun isHandheldOnly(): Boolean - - /** - * Changes controller type for a specific device. - * - * @param Device The input descriptor of the gamepad. - * @param Type The NpadStyleIndex of the gamepad. - */ - external fun setDeviceType(Device: Int, Type: Int): Boolean - - /** - * Handles event when a gamepad is connected. - * - * @param Device The input descriptor of the gamepad. - */ - external fun onGamePadConnectEvent(Device: Int): Boolean - - /** - * Handles event when a gamepad is disconnected. - * - * @param Device The input descriptor of the gamepad. - */ - external fun onGamePadDisconnectEvent(Device: Int): Boolean - - /** - * Handles button press events for a gamepad. - * - * @param Device The input descriptor of the gamepad. - * @param Button Key code identifying which button was pressed. - * @param Action Mask identifying which action is happening (button pressed down, or button released). - * @return If we handled the button press. - */ - external fun onGamePadButtonEvent(Device: Int, Button: Int, Action: Int): Boolean - - /** - * Handles joystick movement events. - * - * @param Device The device ID of the gamepad. - * @param Axis The axis ID - * @param x_axis The value of the x-axis represented by the given ID. - * @param y_axis The value of the y-axis represented by the given ID. - */ - external fun onGamePadJoystickEvent( - Device: Int, - Axis: Int, - x_axis: Float, - y_axis: Float - ): Boolean - - /** - * Handles motion events. - * - * @param delta_timestamp The finger id corresponding to this event - * @param gyro_x,gyro_y,gyro_z The value of the accelerometer sensor. - * @param accel_x,accel_y,accel_z The value of the y-axis - */ - external fun onGamePadMotionEvent( - Device: Int, - delta_timestamp: Long, - gyro_x: Float, - gyro_y: Float, - gyro_z: Float, - accel_x: Float, - accel_y: Float, - accel_z: Float - ): Boolean - - /** - * Signals and load a nfc tag - * - * @param data Byte array containing all the data from a nfc tag - */ - external fun onReadNfcTag(data: ByteArray?): Boolean - - /** - * Removes current loaded nfc tag - */ - external fun onRemoveNfcTag(): Boolean - - /** - * Handles touch press events. - * - * @param finger_id The finger id corresponding to this event - * @param x_axis The value of the x-axis. - * @param y_axis The value of the y-axis. - */ - external fun onTouchPressed(finger_id: Int, x_axis: Float, y_axis: Float) - - /** - * Handles touch movement. - * - * @param x_axis The value of the instantaneous x-axis. - * @param y_axis The value of the instantaneous y-axis. - */ - external fun onTouchMoved(finger_id: Int, x_axis: Float, y_axis: Float) - - /** - * Handles touch release events. - * - * @param finger_id The finger id corresponding to this event - */ - external fun onTouchReleased(finger_id: Int) - external fun setAppDirectory(directory: String) /** @@ -318,46 +181,13 @@ object NativeLibrary { ErrorUnknown } - private var coreErrorAlertResult = false - private val coreErrorAlertLock = Object() - - class CoreErrorDialogFragment : DialogFragment() { - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val title = requireArguments().serializable<String>("title") - val message = requireArguments().serializable<String>("message") - - return MaterialAlertDialogBuilder(requireActivity()) - .setTitle(title) - .setMessage(message) - .setPositiveButton(R.string.continue_button, null) - .setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int -> - coreErrorAlertResult = false - synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() } - } - .create() - } - - override fun onDismiss(dialog: DialogInterface) { - coreErrorAlertResult = true - synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() } - } - - companion object { - fun newInstance(title: String?, message: String?): CoreErrorDialogFragment { - val frag = CoreErrorDialogFragment() - val args = Bundle() - args.putString("title", title) - args.putString("message", message) - frag.arguments = args - return frag - } - } - } + var coreErrorAlertResult = false + val coreErrorAlertLock = Object() private fun onCoreErrorImpl(title: String, message: String) { val emulationActivity = sEmulationActivity.get() if (emulationActivity == null) { - error("[NativeLibrary] EmulationActivity not present") + Log.error("[NativeLibrary] EmulationActivity not present") return } @@ -373,7 +203,7 @@ object NativeLibrary { fun onCoreError(error: CoreError?, details: String): Boolean { val emulationActivity = sEmulationActivity.get() if (emulationActivity == null) { - error("[NativeLibrary] EmulationActivity not present") + Log.error("[NativeLibrary] EmulationActivity not present") return false } @@ -404,7 +234,7 @@ object NativeLibrary { } // Show the AlertDialog on the main thread. - emulationActivity.runOnUiThread(Runnable { onCoreErrorImpl(title, message) }) + emulationActivity.runOnUiThread { onCoreErrorImpl(title, message) } // Wait for the lock to notify that it is complete. synchronized(coreErrorAlertLock) { coreErrorAlertLock.wait() } @@ -629,46 +459,4 @@ object NativeLibrary { * Checks if all necessary keys are present for decryption */ external fun areKeysPresent(): Boolean - - /** - * Button type for use in onTouchEvent - */ - object ButtonType { - const val BUTTON_A = 0 - const val BUTTON_B = 1 - const val BUTTON_X = 2 - const val BUTTON_Y = 3 - const val STICK_L = 4 - const val STICK_R = 5 - const val TRIGGER_L = 6 - const val TRIGGER_R = 7 - const val TRIGGER_ZL = 8 - const val TRIGGER_ZR = 9 - const val BUTTON_PLUS = 10 - const val BUTTON_MINUS = 11 - const val DPAD_LEFT = 12 - const val DPAD_UP = 13 - const val DPAD_RIGHT = 14 - const val DPAD_DOWN = 15 - const val BUTTON_SL = 16 - const val BUTTON_SR = 17 - const val BUTTON_HOME = 18 - const val BUTTON_CAPTURE = 19 - } - - /** - * Stick type for use in onTouchEvent - */ - object StickType { - const val STICK_L = 0 - const val STICK_R = 1 - } - - /** - * Button states - */ - object ButtonState { - const val RELEASED = 0 - const val PRESSED = 1 - } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt index 76778c10a..72943f33e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt @@ -7,6 +7,7 @@ import android.app.Application import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context +import org.yuzu.yuzu_emu.features.input.NativeInput import java.io.File import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DocumentsTree @@ -37,6 +38,7 @@ class YuzuApplication : Application() { documentsTree = DocumentsTree() DirectoryInitialization.start() GpuDriverHelper.initializeDriverParameters() + NativeInput.reloadInputDevices() NativeLibrary.logDeviceInfo() Log.logDeviceInfo() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 7a8d03610..c962558a7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -39,6 +39,7 @@ import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding +import org.yuzu.yuzu_emu.features.input.NativeInput 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 @@ -47,7 +48,9 @@ import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.MemoryUtil +import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NfcReader +import org.yuzu.yuzu_emu.utils.ParamPackage import org.yuzu.yuzu_emu.utils.ThemeHelper import java.text.NumberFormat import kotlin.math.roundToInt @@ -63,8 +66,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { private var motionTimestamp: Long = 0 private var flipMotionOrientation: Boolean = false - private var controllerIds = InputHandler.getGameControllerIds() - private val actionPause = "ACTION_EMULATOR_PAUSE" private val actionPlay = "ACTION_EMULATOR_PLAY" private val actionMute = "ACTION_EMULATOR_MUTE" @@ -78,6 +79,33 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { super.onCreate(savedInstanceState) + InputHandler.updateControllerData() + val players = NativeConfig.getInputSettings(true) + var hasConfiguredControllers = false + players.forEach { + if (it.hasMapping()) { + hasConfiguredControllers = true + } + } + if (!hasConfiguredControllers && InputHandler.androidControllers.isNotEmpty()) { + var params: ParamPackage? = null + for (controller in InputHandler.registeredControllers) { + if (controller.get("port", -1) == 0) { + params = controller + break + } + } + + if (params != null) { + NativeInput.updateMappingsWithDefault( + 0, + params, + params.get("display", getString(R.string.unknown)) + ) + NativeConfig.saveGlobalConfig() + } + } + binding = ActivityEmulationBinding.inflate(layoutInflater) setContentView(binding.root) @@ -95,8 +123,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { nfcReader = NfcReader(this) nfcReader.initialize() - InputHandler.initialize() - val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) { @@ -147,7 +173,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { super.onResume() nfcReader.startScanning() startMotionSensorListener() - InputHandler.updateControllerIds() + InputHandler.updateControllerData() buildPictureInPictureParams() } @@ -172,6 +198,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { super.onNewIntent(intent) setIntent(intent) nfcReader.onNewIntent(intent) + InputHandler.updateControllerData() } override fun dispatchKeyEvent(event: KeyEvent): Boolean { @@ -244,8 +271,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { } val deltaTimestamp = (event.timestamp - motionTimestamp) / 1000 motionTimestamp = event.timestamp - NativeLibrary.onGamePadMotionEvent( - NativeLibrary.Player1Device, + NativeInput.onDeviceMotionEvent( + NativeInput.Player1Device, deltaTimestamp, gyro[0], gyro[1], @@ -254,8 +281,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { accel[1], accel[2] ) - NativeLibrary.onGamePadMotionEvent( - NativeLibrary.ConsoleDevice, + NativeInput.onDeviceMotionEvent( + NativeInput.ConsoleDevice, deltaTimestamp, gyro[0], gyro[1], diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt index f218c76ef..50663ad91 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt @@ -3,15 +3,15 @@ package org.yuzu.yuzu_emu.adapters -import android.text.TextUtils import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.model.Driver import org.yuzu.yuzu_emu.model.DriverViewModel +import org.yuzu.yuzu_emu.utils.ViewUtils.marquee +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class DriverAdapter(private val driverViewModel: DriverViewModel) : @@ -44,25 +44,15 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) : } // Delay marquee by 3s - title.postDelayed( - { - title.isSelected = true - title.ellipsize = TextUtils.TruncateAt.MARQUEE - version.isSelected = true - version.ellipsize = TextUtils.TruncateAt.MARQUEE - description.isSelected = true - description.ellipsize = TextUtils.TruncateAt.MARQUEE - }, - 3000 - ) + title.marquee() + version.marquee() + description.marquee() title.text = model.title version.text = model.version description.text = model.description - if (model.title != binding.root.context.getString(R.string.system_gpu_driver)) { - buttonDelete.visibility = View.VISIBLE - } else { - buttonDelete.visibility = View.GONE - } + buttonDelete.setVisible( + model.title != binding.root.context.getString(R.string.system_gpu_driver) + ) } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt index 3d8f0bda8..5cbd15d2a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt @@ -4,7 +4,6 @@ package org.yuzu.yuzu_emu.adapters import android.net.Uri -import android.text.TextUtils import android.view.LayoutInflater import android.view.ViewGroup import androidx.fragment.app.FragmentActivity @@ -12,6 +11,7 @@ import org.yuzu.yuzu_emu.databinding.CardFolderBinding import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment import org.yuzu.yuzu_emu.model.GameDir import org.yuzu.yuzu_emu.model.GamesViewModel +import org.yuzu.yuzu_emu.utils.ViewUtils.marquee import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) : @@ -29,13 +29,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie override fun bind(model: GameDir) { binding.apply { path.text = Uri.parse(model.uriString).path - path.postDelayed( - { - path.isSelected = true - path.ellipsize = TextUtils.TruncateAt.MARQUEE - }, - 3000 - ) + path.marquee() buttonEdit.setOnClickListener { GameFolderPropertiesDialogFragment.newInstance(model) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index 85c8249e6..b1f247ac3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -4,7 +4,6 @@ package org.yuzu.yuzu_emu.adapters import android.net.Uri -import android.text.TextUtils import android.view.LayoutInflater import android.view.ViewGroup import android.widget.ImageView @@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.CardGameBinding import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.utils.GameIconUtils +import org.yuzu.yuzu_emu.utils.ViewUtils.marquee import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class GameAdapter(private val activity: AppCompatActivity) : @@ -44,14 +44,7 @@ class GameAdapter(private val activity: AppCompatActivity) : binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ") - binding.textGameTitle.postDelayed( - { - binding.textGameTitle.ellipsize = TextUtils.TruncateAt.MARQUEE - binding.textGameTitle.isSelected = true - }, - 3000 - ) - + binding.textGameTitle.marquee() binding.cardGame.setOnClickListener { onClick(model) } binding.cardGame.setOnLongClickListener { onLongClick(model) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt index 0046d5314..7366e2c77 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt @@ -3,21 +3,18 @@ package org.yuzu.yuzu_emu.adapters -import android.text.TextUtils import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.core.content.res.ResourcesCompat -import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding import org.yuzu.yuzu_emu.model.GameProperty import org.yuzu.yuzu_emu.model.InstallableProperty import org.yuzu.yuzu_emu.model.SubmenuProperty +import org.yuzu.yuzu_emu.utils.ViewUtils.marquee +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible +import org.yuzu.yuzu_emu.utils.collect import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class GamePropertiesAdapter( @@ -76,23 +73,15 @@ class GamePropertiesAdapter( ) ) - binding.details.postDelayed({ - binding.details.isSelected = true - binding.details.ellipsize = TextUtils.TruncateAt.MARQUEE - }, 3000) - + binding.details.marquee() if (submenuProperty.details != null) { - binding.details.visibility = View.VISIBLE + binding.details.setVisible(true) binding.details.text = submenuProperty.details.invoke() } else if (submenuProperty.detailsFlow != null) { - binding.details.visibility = View.VISIBLE - viewLifecycle.lifecycleScope.launch { - viewLifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - submenuProperty.detailsFlow.collect { binding.details.text = it } - } - } + binding.details.setVisible(true) + submenuProperty.detailsFlow.collect(viewLifecycle) { binding.details.text = it } } else { - binding.details.visibility = View.GONE + binding.details.setVisible(false) } } } @@ -112,14 +101,10 @@ class GamePropertiesAdapter( ) ) - if (installableProperty.install != null) { - binding.buttonInstall.visibility = View.VISIBLE - binding.buttonInstall.setOnClickListener { installableProperty.install.invoke() } - } - if (installableProperty.export != null) { - binding.buttonExport.visibility = View.VISIBLE - binding.buttonExport.setOnClickListener { installableProperty.export.invoke() } - } + binding.buttonInstall.setVisible(installableProperty.install != null) + binding.buttonInstall.setOnClickListener { installableProperty.install?.invoke() } + binding.buttonExport.setVisible(installableProperty.export != null) + binding.buttonExport.setOnClickListener { installableProperty.export?.invoke() } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt index b512845d5..0bd196673 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt @@ -3,22 +3,19 @@ package org.yuzu.yuzu_emu.adapters -import android.text.TextUtils import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat -import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.model.HomeSetting +import org.yuzu.yuzu_emu.utils.ViewUtils.marquee +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible +import org.yuzu.yuzu_emu.utils.collect import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class HomeSettingAdapter( @@ -59,18 +56,8 @@ class HomeSettingAdapter( binding.optionIcon.alpha = 0.5f } - viewLifecycle.lifecycleScope.launch { - viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { - model.details.collect { updateOptionDetails(it) } - } - } - binding.optionDetail.postDelayed( - { - binding.optionDetail.ellipsize = TextUtils.TruncateAt.MARQUEE - binding.optionDetail.isSelected = true - }, - 3000 - ) + model.details.collect(viewLifecycle) { updateOptionDetails(it) } + binding.optionDetail.marquee() binding.root.setOnClickListener { onClick(model) } } @@ -90,7 +77,7 @@ class HomeSettingAdapter( private fun updateOptionDetails(detailString: String) { if (detailString.isNotEmpty()) { binding.optionDetail.text = detailString - binding.optionDetail.visibility = View.VISIBLE + binding.optionDetail.setVisible(true) } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt index 4218c4e52..1ba75fa2f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt @@ -4,10 +4,10 @@ package org.yuzu.yuzu_emu.adapters import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import org.yuzu.yuzu_emu.databinding.CardInstallableBinding import org.yuzu.yuzu_emu.model.Installable +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class InstallableAdapter(installables: List<Installable>) : @@ -26,14 +26,10 @@ class InstallableAdapter(installables: List<Installable>) : binding.title.setText(model.titleId) binding.description.setText(model.descriptionId) - if (model.install != null) { - binding.buttonInstall.visibility = View.VISIBLE - binding.buttonInstall.setOnClickListener { model.install.invoke() } - } - if (model.export != null) { - binding.buttonExport.visibility = View.VISIBLE - binding.buttonExport.setOnClickListener { model.export.invoke() } - } + binding.buttonInstall.setVisible(model.install != null) + binding.buttonInstall.setOnClickListener { model.install?.invoke() } + binding.buttonExport.setVisible(model.export != null) + binding.buttonExport.setOnClickListener { model.export?.invoke() } } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt index 38bb1f96f..1379968f9 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt @@ -4,12 +4,12 @@ package org.yuzu.yuzu_emu.adapters import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment import org.yuzu.yuzu_emu.model.License +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) : @@ -25,7 +25,7 @@ class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<Lic binding.apply { textSettingName.text = root.context.getString(model.titleId) textSettingDescription.text = root.context.getString(model.descriptionId) - textSettingValue.visibility = View.GONE + textSettingValue.setVisible(false) root.setOnClickListener { onClick(model) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt index 02118e1a8..a5f610b31 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt @@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.adapters import android.text.Html import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat @@ -17,6 +16,7 @@ import org.yuzu.yuzu_emu.model.SetupCallback import org.yuzu.yuzu_emu.model.SetupPage import org.yuzu.yuzu_emu.model.StepState import org.yuzu.yuzu_emu.utils.ViewUtils +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) : @@ -30,8 +30,8 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) : AbstractViewHolder<SetupPage>(binding), SetupCallback { override fun bind(model: SetupPage) { if (model.stepCompleted.invoke() == StepState.COMPLETE) { - binding.buttonAction.visibility = View.INVISIBLE - binding.textConfirmation.visibility = View.VISIBLE + binding.buttonAction.setVisible(visible = false, gone = false) + binding.textConfirmation.setVisible(true) } binding.icon.setImageDrawable( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/NativeInput.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/NativeInput.kt new file mode 100644 index 000000000..15d776311 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/NativeInput.kt @@ -0,0 +1,416 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.input + +import org.yuzu.yuzu_emu.features.input.model.NativeButton +import org.yuzu.yuzu_emu.features.input.model.NativeAnalog +import org.yuzu.yuzu_emu.features.input.model.InputType +import org.yuzu.yuzu_emu.features.input.model.ButtonName +import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex +import org.yuzu.yuzu_emu.utils.NativeConfig +import org.yuzu.yuzu_emu.utils.ParamPackage +import android.view.InputDevice + +object NativeInput { + /** + * Default controller id for each device + */ + const val Player1Device = 0 + const val Player2Device = 1 + const val Player3Device = 2 + const val Player4Device = 3 + const val Player5Device = 4 + const val Player6Device = 5 + const val Player7Device = 6 + const val Player8Device = 7 + const val ConsoleDevice = 8 + + /** + * Button states + */ + object ButtonState { + const val RELEASED = 0 + const val PRESSED = 1 + } + + /** + * Returns true if pro controller isn't available and handheld is. + * Intended to check where the input overlay should direct its inputs. + */ + external fun isHandheldOnly(): Boolean + + /** + * Handles button press events for a gamepad. + * @param guid 32 character hexadecimal string consisting of the controller's PID+VID. + * @param port Port determined by controller connection order. + * @param buttonId The Android Keycode corresponding to this event. + * @param action Mask identifying which action is happening (button pressed down, or button released). + */ + external fun onGamePadButtonEvent( + guid: String, + port: Int, + buttonId: Int, + action: Int + ) + + /** + * Handles axis movement events. + * @param guid 32 character hexadecimal string consisting of the controller's PID+VID. + * @param port Port determined by controller connection order. + * @param axis The axis ID. + * @param value Value along the given axis. + */ + external fun onGamePadAxisEvent(guid: String, port: Int, axis: Int, value: Float) + + /** + * Handles motion events. + * @param guid 32 character hexadecimal string consisting of the controller's PID+VID. + * @param port Port determined by controller connection order. + * @param deltaTimestamp The finger id corresponding to this event. + * @param xGyro The value of the x-axis for the gyroscope. + * @param yGyro The value of the y-axis for the gyroscope. + * @param zGyro The value of the z-axis for the gyroscope. + * @param xAccel The value of the x-axis for the accelerometer. + * @param yAccel The value of the y-axis for the accelerometer. + * @param zAccel The value of the z-axis for the accelerometer. + */ + external fun onGamePadMotionEvent( + guid: String, + port: Int, + deltaTimestamp: Long, + xGyro: Float, + yGyro: Float, + zGyro: Float, + xAccel: Float, + yAccel: Float, + zAccel: Float + ) + + /** + * Signals and load a nfc tag + * @param data Byte array containing all the data from a nfc tag. + */ + external fun onReadNfcTag(data: ByteArray?) + + /** + * Removes current loaded nfc tag. + */ + external fun onRemoveNfcTag() + + /** + * Handles touch press events. + * @param fingerId The finger id corresponding to this event. + * @param xAxis The value of the x-axis on the touchscreen. + * @param yAxis The value of the y-axis on the touchscreen. + */ + external fun onTouchPressed(fingerId: Int, xAxis: Float, yAxis: Float) + + /** + * Handles touch movement. + * @param fingerId The finger id corresponding to this event. + * @param xAxis The value of the x-axis on the touchscreen. + * @param yAxis The value of the y-axis on the touchscreen. + */ + external fun onTouchMoved(fingerId: Int, xAxis: Float, yAxis: Float) + + /** + * Handles touch release events. + * @param fingerId The finger id corresponding to this event + */ + external fun onTouchReleased(fingerId: Int) + + /** + * Sends a button input to the global virtual controllers. + * @param port Port determined by controller connection order. + * @param button The [NativeButton] corresponding to this event. + * @param action Mask identifying which action is happening (button pressed down, or button released). + */ + fun onOverlayButtonEvent(port: Int, button: NativeButton, action: Int) = + onOverlayButtonEventImpl(port, button.int, action) + + private external fun onOverlayButtonEventImpl(port: Int, buttonId: Int, action: Int) + + /** + * Sends a joystick input to the global virtual controllers. + * @param port Port determined by controller connection order. + * @param stick The [NativeAnalog] corresponding to this event. + * @param xAxis Value along the X axis. + * @param yAxis Value along the Y axis. + */ + fun onOverlayJoystickEvent(port: Int, stick: NativeAnalog, xAxis: Float, yAxis: Float) = + onOverlayJoystickEventImpl(port, stick.int, xAxis, yAxis) + + private external fun onOverlayJoystickEventImpl( + port: Int, + stickId: Int, + xAxis: Float, + yAxis: Float + ) + + /** + * Handles motion events for the global virtual controllers. + * @param port Port determined by controller connection order + * @param deltaTimestamp The finger id corresponding to this event. + * @param xGyro The value of the x-axis for the gyroscope. + * @param yGyro The value of the y-axis for the gyroscope. + * @param zGyro The value of the z-axis for the gyroscope. + * @param xAccel The value of the x-axis for the accelerometer. + * @param yAccel The value of the y-axis for the accelerometer. + * @param zAccel The value of the z-axis for the accelerometer. + */ + external fun onDeviceMotionEvent( + port: Int, + deltaTimestamp: Long, + xGyro: Float, + yGyro: Float, + zGyro: Float, + xAccel: Float, + yAccel: Float, + zAccel: Float + ) + + /** + * Reloads all input devices from the currently loaded Settings::values.players into HID Core + */ + external fun reloadInputDevices() + + /** + * Registers a controller to be used with mapping + * @param device An [InputDevice] or the input overlay wrapped with [YuzuInputDevice] + */ + external fun registerController(device: YuzuInputDevice) + + /** + * Gets the names of input devices that have been registered with the input subsystem via [registerController] + */ + external fun getInputDevices(): Array<String> + + /** + * Reads all input profiles from disk. Must be called before creating a profile picker. + */ + external fun loadInputProfiles() + + /** + * Gets the names of each available input profile. + */ + external fun getInputProfileNames(): Array<String> + + /** + * Checks if the user-provided name for an input profile is valid. + * @param name User-provided name for an input profile. + * @return Whether [name] is valid or not. + */ + external fun isProfileNameValid(name: String): Boolean + + /** + * Creates a new input profile. + * @param name The new profile's name. + * @param playerIndex Index of the player that's currently being edited. Used to write the profile + * name to this player's config. + * @return Whether creating the profile was successful or not. + */ + external fun createProfile(name: String, playerIndex: Int): Boolean + + /** + * Deletes an input profile. + * @param name Name of the profile to delete. + * @param playerIndex Index of the player that's currently being edited. Used to remove the profile + * name from this player's config if they have it loaded. + * @return Whether deleting this profile was successful or not. + */ + external fun deleteProfile(name: String, playerIndex: Int): Boolean + + /** + * Loads an input profile. + * @param name Name of the input profile to load. + * @param playerIndex Index of the player that will have this profile loaded. + * @return Whether loading this profile was successful or not. + */ + external fun loadProfile(name: String, playerIndex: Int): Boolean + + /** + * Saves an input profile. + * @param name Name of the profile to save. + * @param playerIndex Index of the player that's currently being edited. Used to write the profile + * name to this player's config. + * @return Whether saving the profile was successful or not. + */ + external fun saveProfile(name: String, playerIndex: Int): Boolean + + /** + * Intended to be used immediately before a call to [NativeConfig.saveControlPlayerValues] + * Must be used while per-game config is loaded. + */ + external fun loadPerGameConfiguration( + playerIndex: Int, + selectedIndex: Int, + selectedProfileName: String + ) + + /** + * Tells the input subsystem to start listening for inputs to map. + * @param type Type of input to map as shown by the int property in each [InputType]. + */ + external fun beginMapping(type: Int) + + /** + * Gets an input's [ParamPackage] as a serialized string. Used for input verification before mapping. + * Must be run after [beginMapping] and before [stopMapping]. + */ + external fun getNextInput(): String + + /** + * Tells the input subsystem to stop listening for inputs to map. + */ + external fun stopMapping() + + /** + * Updates a controller's mappings with auto-mapping params. + * @param playerIndex Index of the player to auto-map. + * @param deviceParams [ParamPackage] representing the device to auto-map as received + * from [getInputDevices]. + * @param displayName Name of the device to auto-map as received from the "display" param in [deviceParams]. + * Intended to be a way to provide a default name for a controller if the "display" param is empty. + */ + fun updateMappingsWithDefault( + playerIndex: Int, + deviceParams: ParamPackage, + displayName: String + ) = updateMappingsWithDefaultImpl(playerIndex, deviceParams.serialize(), displayName) + + private external fun updateMappingsWithDefaultImpl( + playerIndex: Int, + deviceParams: String, + displayName: String + ) + + /** + * Gets the params for a specific button. + * @param playerIndex Index of the player to get params from. + * @param button The [NativeButton] to get params for. + * @return A [ParamPackage] representing a player's specific button. + */ + fun getButtonParam(playerIndex: Int, button: NativeButton): ParamPackage = + ParamPackage(getButtonParamImpl(playerIndex, button.int)) + + private external fun getButtonParamImpl(playerIndex: Int, buttonId: Int): String + + /** + * Sets the params for a specific button. + * @param playerIndex Index of the player to set params for. + * @param button The [NativeButton] to set params for. + * @param param A [ParamPackage] to set. + */ + fun setButtonParam(playerIndex: Int, button: NativeButton, param: ParamPackage) = + setButtonParamImpl(playerIndex, button.int, param.serialize()) + + private external fun setButtonParamImpl(playerIndex: Int, buttonId: Int, param: String) + + /** + * Gets the params for a specific stick. + * @param playerIndex Index of the player to get params from. + * @param stick The [NativeAnalog] to get params for. + * @return A [ParamPackage] representing a player's specific stick. + */ + fun getStickParam(playerIndex: Int, stick: NativeAnalog): ParamPackage = + ParamPackage(getStickParamImpl(playerIndex, stick.int)) + + private external fun getStickParamImpl(playerIndex: Int, stickId: Int): String + + /** + * Sets the params for a specific stick. + * @param playerIndex Index of the player to set params for. + * @param stick The [NativeAnalog] to set params for. + * @param param A [ParamPackage] to set. + */ + fun setStickParam(playerIndex: Int, stick: NativeAnalog, param: ParamPackage) = + setStickParamImpl(playerIndex, stick.int, param.serialize()) + + private external fun setStickParamImpl(playerIndex: Int, stickId: Int, param: String) + + /** + * Gets the int representation of a [ButtonName]. Tells you what to show as the mapped input for + * a button/analog/other. + * @param param A [ParamPackage] that represents a specific button's params. + * @return The [ButtonName] for [param]. + */ + fun getButtonName(param: ParamPackage): ButtonName = + ButtonName.from(getButtonNameImpl(param.serialize())) + + private external fun getButtonNameImpl(param: String): Int + + /** + * Gets each supported [NpadStyleIndex] for a given player. + * @param playerIndex Index of the player to get supported indexes for. + * @return List of each supported [NpadStyleIndex]. + */ + fun getSupportedStyleTags(playerIndex: Int): List<NpadStyleIndex> = + getSupportedStyleTagsImpl(playerIndex).map { NpadStyleIndex.from(it) } + + private external fun getSupportedStyleTagsImpl(playerIndex: Int): IntArray + + /** + * Gets the [NpadStyleIndex] for a given player. + * @param playerIndex Index of the player to get an [NpadStyleIndex] from. + * @return The [NpadStyleIndex] for a given player. + */ + fun getStyleIndex(playerIndex: Int): NpadStyleIndex = + NpadStyleIndex.from(getStyleIndexImpl(playerIndex)) + + private external fun getStyleIndexImpl(playerIndex: Int): Int + + /** + * Sets the [NpadStyleIndex] for a given player. + * @param playerIndex Index of the player to change. + * @param style The new style to set. + */ + fun setStyleIndex(playerIndex: Int, style: NpadStyleIndex) = + setStyleIndexImpl(playerIndex, style.int) + + private external fun setStyleIndexImpl(playerIndex: Int, styleIndex: Int) + + /** + * Checks if a device is a controller. + * @param params [ParamPackage] for an input device retrieved from [getInputDevices] + * @return Whether the device is a controller or not. + */ + fun isController(params: ParamPackage): Boolean = isControllerImpl(params.serialize()) + + private external fun isControllerImpl(params: String): Boolean + + /** + * Checks if a controller is connected + * @param playerIndex Index of the player to check. + * @return Whether the player is connected or not. + */ + external fun getIsConnected(playerIndex: Int): Boolean + + /** + * Connects/disconnects a controller and ensures that connection order stays in-tact. + * @param playerIndex Index of the player to connect/disconnect. + * @param connected Whether to connect or disconnect this controller. + */ + fun connectControllers(playerIndex: Int, connected: Boolean = true) { + val connectedControllers = mutableListOf<Boolean>().apply { + if (connected) { + for (i in 0 until 8) { + add(i <= playerIndex) + } + } else { + for (i in 0 until 8) { + add(i < playerIndex) + } + } + } + connectControllersImpl(connectedControllers.toBooleanArray()) + } + + private external fun connectControllersImpl(connected: BooleanArray) + + /** + * Resets all of the button and analog mappings for a player. + * @param playerIndex Index of the player that will have its mappings reset. + */ + external fun resetControllerMappings(playerIndex: Int) +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuInputDevice.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuInputDevice.kt new file mode 100644 index 000000000..15cc38c7f --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuInputDevice.kt @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.input + +import android.view.InputDevice +import androidx.annotation.Keep +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.utils.InputHandler.getGUID + +@Keep +interface YuzuInputDevice { + fun getName(): String + + fun getGUID(): String + + fun getPort(): Int + + fun getSupportsVibration(): Boolean + + fun vibrate(intensity: Float) + + fun getAxes(): Array<Int> = arrayOf() + fun hasKeys(keys: IntArray): BooleanArray = BooleanArray(0) +} + +class YuzuPhysicalDevice( + private val device: InputDevice, + private val port: Int, + useSystemVibrator: Boolean +) : YuzuInputDevice { + private val vibrator = if (useSystemVibrator) { + YuzuVibrator.getSystemVibrator() + } else { + YuzuVibrator.getControllerVibrator(device) + } + + override fun getName(): String { + return device.name + } + + override fun getGUID(): String { + return device.getGUID() + } + + override fun getPort(): Int { + return port + } + + override fun getSupportsVibration(): Boolean { + return vibrator.supportsVibration() + } + + override fun vibrate(intensity: Float) { + vibrator.vibrate(intensity) + } + + override fun getAxes(): Array<Int> = device.motionRanges.map { it.axis }.toTypedArray() + override fun hasKeys(keys: IntArray): BooleanArray = device.hasKeys(*keys) +} + +class YuzuInputOverlayDevice( + private val vibration: Boolean, + private val port: Int +) : YuzuInputDevice { + private val vibrator = YuzuVibrator.getSystemVibrator() + + override fun getName(): String { + return YuzuApplication.appContext.getString(R.string.input_overlay) + } + + override fun getGUID(): String { + return "00000000000000000000000000000000" + } + + override fun getPort(): Int { + return port + } + + override fun getSupportsVibration(): Boolean { + if (vibration) { + return vibrator.supportsVibration() + } + return false + } + + override fun vibrate(intensity: Float) { + if (vibration) { + vibrator.vibrate(intensity) + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuVibrator.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuVibrator.kt new file mode 100644 index 000000000..aac49ecae --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuVibrator.kt @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.input + +import android.content.Context +import android.os.Build +import android.os.CombinedVibration +import android.os.VibrationEffect +import android.os.Vibrator +import android.os.VibratorManager +import android.view.InputDevice +import androidx.annotation.Keep +import androidx.annotation.RequiresApi +import org.yuzu.yuzu_emu.YuzuApplication + +@Keep +@Suppress("DEPRECATION") +interface YuzuVibrator { + fun supportsVibration(): Boolean + + fun vibrate(intensity: Float) + + companion object { + fun getControllerVibrator(device: InputDevice): YuzuVibrator = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + YuzuVibratorManager(device.vibratorManager) + } else { + YuzuVibratorManagerCompat(device.vibrator) + } + + fun getSystemVibrator(): YuzuVibrator = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val vibratorManager = YuzuApplication.appContext + .getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager + YuzuVibratorManager(vibratorManager) + } else { + val vibrator = YuzuApplication.appContext + .getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + YuzuVibratorManagerCompat(vibrator) + } + + fun getVibrationEffect(intensity: Float): VibrationEffect? { + if (intensity > 0f) { + return VibrationEffect.createOneShot( + 50, + (255.0 * intensity).toInt().coerceIn(1, 255) + ) + } + return null + } + } +} + +@RequiresApi(Build.VERSION_CODES.S) +class YuzuVibratorManager(private val vibratorManager: VibratorManager) : YuzuVibrator { + override fun supportsVibration(): Boolean { + return vibratorManager.vibratorIds.isNotEmpty() + } + + override fun vibrate(intensity: Float) { + val vibration = YuzuVibrator.getVibrationEffect(intensity) ?: return + vibratorManager.vibrate(CombinedVibration.createParallel(vibration)) + } +} + +class YuzuVibratorManagerCompat(private val vibrator: Vibrator) : YuzuVibrator { + override fun supportsVibration(): Boolean { + return vibrator.hasVibrator() + } + + override fun vibrate(intensity: Float) { + val vibration = YuzuVibrator.getVibrationEffect(intensity) ?: return + vibrator.vibrate(vibration) + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/AnalogDirection.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/AnalogDirection.kt new file mode 100644 index 000000000..0a5fab2ae --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/AnalogDirection.kt @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.input.model + +enum class AnalogDirection(val int: Int, val param: String) { + Up(0, "up"), + Down(1, "down"), + Left(2, "left"), + Right(3, "right") +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/ButtonName.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/ButtonName.kt new file mode 100644 index 000000000..b8846ecad --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/ButtonName.kt @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.input.model + +// Loosely matches the enum in common/input.h +enum class ButtonName(val int: Int) { + Invalid(1), + + // This will display the engine name instead of the button name + Engine(2), + + // This will display the button by value instead of the button name + Value(3); + + companion object { + fun from(int: Int): ButtonName = entries.firstOrNull { it.int == int } ?: Invalid + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/InputType.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/InputType.kt new file mode 100644 index 000000000..f725231cb --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/InputType.kt @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.input.model + +// Must match the corresponding enum in input_common/main.h +enum class InputType(val int: Int) { + None(0), + Button(1), + Stick(2), + Motion(3), + Touch(4) +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeAnalog.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeAnalog.kt new file mode 100644 index 000000000..c3b7a785d --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeAnalog.kt @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.input.model + +// Must match enum in src/common/settings_input.h +enum class NativeAnalog(val int: Int) { + LStick(0), + RStick(1); + + companion object { + fun from(int: Int): NativeAnalog = entries.firstOrNull { it.int == int } ?: LStick + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeButton.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeButton.kt new file mode 100644 index 000000000..c5ccd7115 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeButton.kt @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.input.model + +// Must match enum in src/common/settings_input.h +enum class NativeButton(val int: Int) { + A(0), + B(1), + X(2), + Y(3), + LStick(4), + RStick(5), + L(6), + R(7), + ZL(8), + ZR(9), + Plus(10), + Minus(11), + + DLeft(12), + DUp(13), + DRight(14), + DDown(15), + + SLLeft(16), + SRLeft(17), + + Home(18), + Capture(19), + + SLRight(20), + SRRight(21); + + companion object { + fun from(int: Int): NativeButton = entries.firstOrNull { it.int == int } ?: A + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeTrigger.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeTrigger.kt new file mode 100644 index 000000000..625f352b4 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeTrigger.kt @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.input.model + +// Must match enum in src/common/settings_input.h +enum class NativeTrigger(val int: Int) { + LTrigger(0), + RTrigger(1) +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NpadStyleIndex.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NpadStyleIndex.kt new file mode 100644 index 000000000..e2a3d7aff --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NpadStyleIndex.kt @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.input.model + +import androidx.annotation.StringRes +import org.yuzu.yuzu_emu.R + +// Must match enum in src/core/hid/hid_types.h +enum class NpadStyleIndex(val int: Int, @StringRes val nameId: Int = 0) { + None(0), + Fullkey(3, R.string.pro_controller), + Handheld(4, R.string.handheld), + HandheldNES(4), + JoyconDual(5, R.string.dual_joycons), + JoyconLeft(6, R.string.left_joycon), + JoyconRight(7, R.string.right_joycon), + GameCube(8, R.string.gamecube_controller), + Pokeball(9), + NES(10), + SNES(12), + N64(13), + SegaGenesis(14), + SystemExt(32), + System(33); + + companion object { + fun from(int: Int): NpadStyleIndex = entries.firstOrNull { it.int == int } ?: None + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt new file mode 100644 index 000000000..d35de80c4 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.input.model + +import androidx.annotation.Keep + +@Keep +data class PlayerInput( + var connected: Boolean, + var buttons: Array<String>, + var analogs: Array<String>, + var motions: Array<String>, + + var vibrationEnabled: Boolean, + var vibrationStrength: Int, + + var bodyColorLeft: Long, + var bodyColorRight: Long, + var buttonColorLeft: Long, + var buttonColorRight: Long, + var profileName: String, + + var useSystemVibrator: Boolean +) { + // It's recommended to use the generated equals() and hashCode() methods + // when using arrays in a data class + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as PlayerInput + + if (connected != other.connected) return false + if (!buttons.contentEquals(other.buttons)) return false + if (!analogs.contentEquals(other.analogs)) return false + if (!motions.contentEquals(other.motions)) return false + if (vibrationEnabled != other.vibrationEnabled) return false + if (vibrationStrength != other.vibrationStrength) return false + if (bodyColorLeft != other.bodyColorLeft) return false + if (bodyColorRight != other.bodyColorRight) return false + if (buttonColorLeft != other.buttonColorLeft) return false + if (buttonColorRight != other.buttonColorRight) return false + if (profileName != other.profileName) return false + return useSystemVibrator == other.useSystemVibrator + } + + override fun hashCode(): Int { + var result = connected.hashCode() + result = 31 * result + buttons.contentHashCode() + result = 31 * result + analogs.contentHashCode() + result = 31 * result + motions.contentHashCode() + result = 31 * result + vibrationEnabled.hashCode() + result = 31 * result + vibrationStrength + result = 31 * result + bodyColorLeft.hashCode() + result = 31 * result + bodyColorRight.hashCode() + result = 31 * result + buttonColorLeft.hashCode() + result = 31 * result + buttonColorRight.hashCode() + result = 31 * result + profileName.hashCode() + result = 31 * result + useSystemVibrator.hashCode() + return result + } + + fun hasMapping(): Boolean { + var hasMapping = false + buttons.forEach { + if (it != "[empty]") { + hasMapping = true + } + } + analogs.forEach { + if (it != "[empty]") { + hasMapping = true + } + } + motions.forEach { + if (it != "[empty]") { + hasMapping = true + } + } + return hasMapping + } +} 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..4f6b93bd2 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 @@ -4,17 +4,30 @@ package org.yuzu.yuzu_emu.features.settings.model import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication object Settings { - enum class MenuTag(val titleId: Int) { + enum class MenuTag(val titleId: Int = 0) { SECTION_ROOT(R.string.advanced_settings), SECTION_SYSTEM(R.string.preferences_system), SECTION_RENDERER(R.string.preferences_graphics), SECTION_AUDIO(R.string.preferences_audio), + SECTION_INPUT(R.string.preferences_controls), + SECTION_INPUT_PLAYER_ONE, + SECTION_INPUT_PLAYER_TWO, + SECTION_INPUT_PLAYER_THREE, + SECTION_INPUT_PLAYER_FOUR, + SECTION_INPUT_PLAYER_FIVE, + SECTION_INPUT_PLAYER_SIX, + SECTION_INPUT_PLAYER_SEVEN, + SECTION_INPUT_PLAYER_EIGHT, SECTION_THEME(R.string.preferences_theme), SECTION_DEBUG(R.string.preferences_debug); } + fun getPlayerString(player: Int): String = + YuzuApplication.appContext.getString(R.string.preferences_player, player) + const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch" const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" @@ -93,4 +106,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/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt index a0d8cfede..6f16cf5b1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt @@ -6,7 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.model import org.yuzu.yuzu_emu.utils.NativeConfig enum class StringSetting(override val key: String) : AbstractStringSetting { - DRIVER_PATH("driver_path"); + DRIVER_PATH("driver_path"), + DEVICE_NAME("device_name"); override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/AnalogInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/AnalogInputSetting.kt new file mode 100644 index 000000000..a2996725e --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/AnalogInputSetting.kt @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.model.view + +import androidx.annotation.StringRes +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.features.input.model.AnalogDirection +import org.yuzu.yuzu_emu.features.input.model.InputType +import org.yuzu.yuzu_emu.features.input.model.NativeAnalog +import org.yuzu.yuzu_emu.utils.ParamPackage + +class AnalogInputSetting( + override val playerIndex: Int, + val nativeAnalog: NativeAnalog, + val analogDirection: AnalogDirection, + @StringRes titleId: Int = 0, + titleString: String = "" +) : InputSetting(titleId, titleString) { + override val type = TYPE_INPUT + override val inputType = InputType.Stick + + override fun getSelectedValue(): String { + val params = NativeInput.getStickParam(playerIndex, nativeAnalog) + val analog = analogToText(params, analogDirection.param) + return getDisplayString(params, analog) + } + + override fun setSelectedValue(param: ParamPackage) = + NativeInput.setStickParam(playerIndex, nativeAnalog, param) +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ButtonInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ButtonInputSetting.kt new file mode 100644 index 000000000..786d09a7a --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ButtonInputSetting.kt @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.model.view + +import androidx.annotation.StringRes +import org.yuzu.yuzu_emu.utils.ParamPackage +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.features.input.model.InputType +import org.yuzu.yuzu_emu.features.input.model.NativeButton + +class ButtonInputSetting( + override val playerIndex: Int, + val nativeButton: NativeButton, + @StringRes titleId: Int = 0, + titleString: String = "" +) : InputSetting(titleId, titleString) { + override val type = TYPE_INPUT + override val inputType = InputType.Button + + override fun getSelectedValue(): String { + val params = NativeInput.getButtonParam(playerIndex, nativeButton) + val button = buttonToText(params) + return getDisplayString(params, button) + } + + override fun setSelectedValue(param: ParamPackage) = + NativeInput.setButtonParam(playerIndex, nativeButton, param) +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt index 1d81f5f2b..58febff1d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt @@ -3,13 +3,16 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import androidx.annotation.StringRes import org.yuzu.yuzu_emu.features.settings.model.AbstractLongSetting class DateTimeSetting( private val longSetting: AbstractLongSetting, - titleId: Int, - descriptionId: Int -) : SettingsItem(longSetting, titleId, descriptionId) { + @StringRes titleId: Int = 0, + titleString: String = "", + @StringRes descriptionId: Int = 0, + descriptionString: String = "" +) : SettingsItem(longSetting, titleId, titleString, descriptionId, descriptionString) { override val type = TYPE_DATETIME_SETTING fun getValue(needsGlobal: Boolean = false): Long = longSetting.getLong(needsGlobal) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt index d31ce1c31..8a6a51d5c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt @@ -3,8 +3,11 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import androidx.annotation.StringRes + class HeaderSetting( - titleId: Int -) : SettingsItem(emptySetting, titleId, 0) { + @StringRes titleId: Int = 0, + titleString: String = "" +) : SettingsItem(emptySetting, titleId, titleString, 0, "") { override val type = TYPE_HEADER } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputProfileSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputProfileSetting.kt new file mode 100644 index 000000000..c46de08c5 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputProfileSetting.kt @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.model.view + +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.utils.NativeConfig + +class InputProfileSetting(private val playerIndex: Int) : + SettingsItem(emptySetting, R.string.profile, "", 0, "") { + override val type = TYPE_INPUT_PROFILE + + fun getCurrentProfile(): String = + NativeConfig.getInputSettings(true)[playerIndex].profileName + + fun getProfileNames(): Array<String> = NativeInput.getInputProfileNames() + + fun isProfileNameValid(name: String): Boolean = NativeInput.isProfileNameValid(name) + + fun createProfile(name: String): Boolean = NativeInput.createProfile(name, playerIndex) + + fun deleteProfile(name: String): Boolean = NativeInput.deleteProfile(name, playerIndex) + + fun loadProfile(name: String): Boolean { + val result = NativeInput.loadProfile(name, playerIndex) + NativeInput.reloadInputDevices() + return result + } + + fun saveProfile(name: String): Boolean = NativeInput.saveProfile(name, playerIndex) +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputSetting.kt new file mode 100644 index 000000000..2d118bff3 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputSetting.kt @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.model.view + +import androidx.annotation.StringRes +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.features.input.model.ButtonName +import org.yuzu.yuzu_emu.features.input.model.InputType +import org.yuzu.yuzu_emu.utils.ParamPackage + +sealed class InputSetting( + @StringRes titleId: Int, + titleString: String +) : SettingsItem(emptySetting, titleId, titleString, 0, "") { + override val type = TYPE_INPUT + abstract val inputType: InputType + abstract val playerIndex: Int + + protected val context get() = YuzuApplication.appContext + + abstract fun getSelectedValue(): String + + abstract fun setSelectedValue(param: ParamPackage) + + protected fun getDisplayString(params: ParamPackage, control: String): String { + val deviceName = params.get("display", "") + deviceName.ifEmpty { + return context.getString(R.string.not_set) + } + return "$deviceName: $control" + } + + private fun getDirectionName(direction: String): String = + when (direction) { + "up" -> context.getString(R.string.up) + "down" -> context.getString(R.string.down) + "left" -> context.getString(R.string.left) + "right" -> context.getString(R.string.right) + else -> direction + } + + protected fun buttonToText(param: ParamPackage): String { + if (!param.has("engine")) { + return context.getString(R.string.not_set) + } + + val toggle = if (param.get("toggle", false)) "~" else "" + val inverted = if (param.get("inverted", false)) "!" else "" + val invert = if (param.get("invert", "+") == "-") "-" else "" + val turbo = if (param.get("turbo", false)) "$" else "" + val commonButtonName = NativeInput.getButtonName(param) + + if (commonButtonName == ButtonName.Invalid) { + return context.getString(R.string.invalid) + } + + if (commonButtonName == ButtonName.Engine) { + return param.get("engine", "") + } + + if (commonButtonName == ButtonName.Value) { + if (param.has("hat")) { + val hat = getDirectionName(param.get("direction", "")) + return context.getString(R.string.qualified_hat, turbo, toggle, inverted, hat) + } + if (param.has("axis")) { + val axis = param.get("axis", "") + return context.getString( + R.string.qualified_button_stick_axis, + toggle, + inverted, + invert, + axis + ) + } + if (param.has("button")) { + val button = param.get("button", "") + return context.getString(R.string.qualified_button, turbo, toggle, inverted, button) + } + } + + return context.getString(R.string.unknown) + } + + protected fun analogToText(param: ParamPackage, direction: String): String { + if (!param.has("engine")) { + return context.getString(R.string.not_set) + } + + if (param.get("engine", "") == "analog_from_button") { + return buttonToText(ParamPackage(param.get(direction, ""))) + } + + if (!param.has("axis_x") || !param.has("axis_y")) { + return context.getString(R.string.unknown) + } + + val xAxis = param.get("axis_x", "") + val yAxis = param.get("axis_y", "") + val xInvert = param.get("invert_x", "+") == "-" + val yInvert = param.get("invert_y", "+") == "-" + + if (direction == "modifier") { + return context.getString(R.string.unused) + } + + when (direction) { + "up" -> { + val yInvertString = if (yInvert) "+" else "-" + return context.getString(R.string.qualified_axis, yAxis, yInvertString) + } + + "down" -> { + val yInvertString = if (yInvert) "-" else "+" + return context.getString(R.string.qualified_axis, yAxis, yInvertString) + } + + "left" -> { + val xInvertString = if (xInvert) "+" else "-" + return context.getString(R.string.qualified_axis, xAxis, xInvertString) + } + + "right" -> { + val xInvertString = if (xInvert) "-" else "+" + return context.getString(R.string.qualified_axis, xAxis, xInvertString) + } + } + + return context.getString(R.string.unknown) + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/IntSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/IntSingleChoiceSetting.kt new file mode 100644 index 000000000..e024c793a --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/IntSingleChoiceSetting.kt @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.model.view + +import androidx.annotation.StringRes +import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting + +class IntSingleChoiceSetting( + private val intSetting: AbstractIntSetting, + @StringRes titleId: Int = 0, + titleString: String = "", + @StringRes descriptionId: Int = 0, + descriptionString: String = "", + val choices: Array<String>, + val values: Array<Int> +) : SettingsItem(intSetting, titleId, titleString, descriptionId, descriptionString) { + override val type = TYPE_INT_SINGLE_CHOICE + + fun getValueAt(index: Int): Int = + if (values.indices.contains(index)) values[index] else -1 + + fun getChoiceAt(index: Int): String = + if (choices.indices.contains(index)) choices[index] else "" + + fun getSelectedValue(needsGlobal: Boolean = false) = intSetting.getInt(needsGlobal) + fun setSelectedValue(value: Int) = intSetting.setInt(value) + + val selectedValueIndex: Int + get() { + for (i in values.indices) { + if (values[i] == getSelectedValue()) { + return i + } + } + return -1 + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ModifierInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ModifierInputSetting.kt new file mode 100644 index 000000000..a1db3cc87 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ModifierInputSetting.kt @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.model.view + +import androidx.annotation.StringRes +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.features.input.model.InputType +import org.yuzu.yuzu_emu.features.input.model.NativeAnalog +import org.yuzu.yuzu_emu.utils.ParamPackage + +class ModifierInputSetting( + override val playerIndex: Int, + val nativeAnalog: NativeAnalog, + @StringRes titleId: Int = 0, + titleString: String = "" +) : InputSetting(titleId, titleString) { + override val inputType = InputType.Button + + override fun getSelectedValue(): String { + val analogParam = NativeInput.getStickParam(playerIndex, nativeAnalog) + val modifierParam = ParamPackage(analogParam.get("modifier", "")) + return buttonToText(modifierParam) + } + + override fun setSelectedValue(param: ParamPackage) { + val newParam = NativeInput.getStickParam(playerIndex, nativeAnalog) + newParam.set("modifier", param.serialize()) + NativeInput.setStickParam(playerIndex, nativeAnalog, newParam) + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt index 425160024..06f607424 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt @@ -4,13 +4,16 @@ package org.yuzu.yuzu_emu.features.settings.model.view import androidx.annotation.DrawableRes +import androidx.annotation.StringRes class RunnableSetting( - titleId: Int, - descriptionId: Int, - val isRuntimeRunnable: Boolean, + @StringRes titleId: Int = 0, + titleString: String = "", + @StringRes descriptionId: Int = 0, + descriptionString: String = "", + val isRunnable: Boolean, @DrawableRes val iconId: Int = 0, val runnable: () -> Unit -) : SettingsItem(emptySetting, titleId, descriptionId) { +) : SettingsItem(emptySetting, titleId, titleString, descriptionId, descriptionString) { override val type = TYPE_RUNNABLE } 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..5fdf98318 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 @@ -3,8 +3,12 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import androidx.annotation.StringRes import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting @@ -12,6 +16,7 @@ import org.yuzu.yuzu_emu.features.settings.model.ByteSetting import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.LongSetting import org.yuzu.yuzu_emu.features.settings.model.ShortSetting +import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.utils.NativeConfig /** @@ -23,13 +28,34 @@ import org.yuzu.yuzu_emu.utils.NativeConfig */ abstract class SettingsItem( val setting: AbstractSetting, - val nameId: Int, - val descriptionId: Int + @StringRes val titleId: Int, + val titleString: String, + @StringRes val descriptionId: Int, + val descriptionString: String ) { abstract val type: Int + val title: String by lazy { + if (titleId != 0) { + return@lazy YuzuApplication.appContext.getString(titleId) + } + return@lazy titleString + } + + val description: String by lazy { + if (descriptionId != 0) { + return@lazy YuzuApplication.appContext.getString(descriptionId) + } + return@lazy descriptionString + } + val isEditable: Boolean get() { + // Can't change docked mode toggle when using handheld mode + if (setting.key == BooleanSetting.USE_DOCKED_MODE.key) { + return NativeInput.getStyleIndex(0) != NpadStyleIndex.Handheld + } + // Can't edit settings that aren't saveable in per-game config even if they are switchable if (NativeConfig.isPerGameConfigLoaded() && !setting.isSaveable) { return false @@ -50,6 +76,9 @@ abstract class SettingsItem( get() = NativeLibrary.isRunning() && !setting.global && !NativeConfig.isPerGameConfigLoaded() + val clearable: Boolean + get() = !setting.global && NativeConfig.isPerGameConfigLoaded() + companion object { const val TYPE_HEADER = 0 const val TYPE_SWITCH = 1 @@ -59,6 +88,10 @@ abstract class SettingsItem( const val TYPE_STRING_SINGLE_CHOICE = 5 const val TYPE_DATETIME_SETTING = 6 const val TYPE_RUNNABLE = 7 + const val TYPE_INPUT = 8 + const val TYPE_INT_SINGLE_CHOICE = 9 + const val TYPE_INPUT_PROFILE = 10 + const val TYPE_STRING_INPUT = 11 const val FASTMEM_COMBINED = "fastmem_combined" @@ -77,221 +110,246 @@ abstract class SettingsItem( // List of all general val settingsItems = HashMap<String, SettingsItem>().apply { + put(StringInputSetting(StringSetting.DEVICE_NAME, titleId = R.string.device_name)) put( SwitchSetting( BooleanSetting.RENDERER_USE_SPEED_LIMIT, - R.string.frame_limit_enable, - R.string.frame_limit_enable_description + titleId = R.string.frame_limit_enable, + descriptionId = R.string.frame_limit_enable_description ) ) put( SliderSetting( ShortSetting.RENDERER_SPEED_LIMIT, - R.string.frame_limit_slider, - R.string.frame_limit_slider_description, - 1, - 400, - "%" + titleId = R.string.frame_limit_slider, + descriptionId = R.string.frame_limit_slider_description, + min = 1, + max = 400, + units = "%" ) ) put( SingleChoiceSetting( IntSetting.CPU_BACKEND, - R.string.cpu_backend, - 0, - R.array.cpuBackendArm64Names, - R.array.cpuBackendArm64Values + titleId = R.string.cpu_backend, + choicesId = R.array.cpuBackendArm64Names, + valuesId = R.array.cpuBackendArm64Values ) ) put( SingleChoiceSetting( IntSetting.CPU_ACCURACY, - R.string.cpu_accuracy, - 0, - R.array.cpuAccuracyNames, - R.array.cpuAccuracyValues + titleId = R.string.cpu_accuracy, + choicesId = R.array.cpuAccuracyNames, + valuesId = R.array.cpuAccuracyValues ) ) put( SwitchSetting( BooleanSetting.PICTURE_IN_PICTURE, - R.string.picture_in_picture, - R.string.picture_in_picture_description + titleId = R.string.picture_in_picture, + descriptionId = R.string.picture_in_picture_description ) ) + + val dockedModeSetting = object : AbstractBooleanSetting { + override val key = BooleanSetting.USE_DOCKED_MODE.key + + override fun getBoolean(needsGlobal: Boolean): Boolean { + if (NativeInput.getStyleIndex(0) == NpadStyleIndex.Handheld) { + return false + } + return BooleanSetting.USE_DOCKED_MODE.getBoolean(needsGlobal) + } + + override fun setBoolean(value: Boolean) = + BooleanSetting.USE_DOCKED_MODE.setBoolean(value) + + override val defaultValue = BooleanSetting.USE_DOCKED_MODE.defaultValue + + override fun getValueAsString(needsGlobal: Boolean): String = + BooleanSetting.USE_DOCKED_MODE.getValueAsString(needsGlobal) + + override fun reset() = BooleanSetting.USE_DOCKED_MODE.reset() + } put( SwitchSetting( - BooleanSetting.USE_DOCKED_MODE, - R.string.use_docked_mode, - R.string.use_docked_mode_description + dockedModeSetting, + titleId = R.string.use_docked_mode, + descriptionId = R.string.use_docked_mode_description ) ) + put( SingleChoiceSetting( IntSetting.REGION_INDEX, - R.string.emulated_region, - 0, - R.array.regionNames, - R.array.regionValues + titleId = R.string.emulated_region, + choicesId = R.array.regionNames, + valuesId = R.array.regionValues ) ) put( SingleChoiceSetting( IntSetting.LANGUAGE_INDEX, - R.string.emulated_language, - 0, - R.array.languageNames, - R.array.languageValues + titleId = R.string.emulated_language, + choicesId = R.array.languageNames, + valuesId = R.array.languageValues ) ) put( SwitchSetting( BooleanSetting.USE_CUSTOM_RTC, - R.string.use_custom_rtc, - R.string.use_custom_rtc_description + titleId = R.string.use_custom_rtc, + descriptionId = R.string.use_custom_rtc_description ) ) - put(DateTimeSetting(LongSetting.CUSTOM_RTC, R.string.set_custom_rtc, 0)) + put(DateTimeSetting(LongSetting.CUSTOM_RTC, titleId = R.string.set_custom_rtc)) put( SingleChoiceSetting( IntSetting.RENDERER_ACCURACY, - R.string.renderer_accuracy, - 0, - R.array.rendererAccuracyNames, - R.array.rendererAccuracyValues + titleId = R.string.renderer_accuracy, + choicesId = R.array.rendererAccuracyNames, + valuesId = R.array.rendererAccuracyValues ) ) put( SingleChoiceSetting( IntSetting.RENDERER_RESOLUTION, - R.string.renderer_resolution, - 0, - R.array.rendererResolutionNames, - R.array.rendererResolutionValues + titleId = R.string.renderer_resolution, + choicesId = R.array.rendererResolutionNames, + valuesId = R.array.rendererResolutionValues ) ) put( SingleChoiceSetting( IntSetting.RENDERER_VSYNC, - R.string.renderer_vsync, - 0, - R.array.rendererVSyncNames, - R.array.rendererVSyncValues + titleId = R.string.renderer_vsync, + choicesId = R.array.rendererVSyncNames, + valuesId = R.array.rendererVSyncValues ) ) put( SingleChoiceSetting( IntSetting.RENDERER_SCALING_FILTER, - R.string.renderer_scaling_filter, - 0, - R.array.rendererScalingFilterNames, - R.array.rendererScalingFilterValues + titleId = R.string.renderer_scaling_filter, + choicesId = R.array.rendererScalingFilterNames, + valuesId = R.array.rendererScalingFilterValues + ) + ) + put( + SliderSetting( + IntSetting.FSR_SHARPENING_SLIDER, + titleId = R.string.fsr_sharpness, + descriptionId = R.string.fsr_sharpness_description, + units = "%" ) ) put( SingleChoiceSetting( IntSetting.RENDERER_ANTI_ALIASING, - R.string.renderer_anti_aliasing, - 0, - R.array.rendererAntiAliasingNames, - R.array.rendererAntiAliasingValues + titleId = R.string.renderer_anti_aliasing, + choicesId = R.array.rendererAntiAliasingNames, + valuesId = R.array.rendererAntiAliasingValues ) ) put( SingleChoiceSetting( IntSetting.RENDERER_SCREEN_LAYOUT, - R.string.renderer_screen_layout, - 0, - R.array.rendererScreenLayoutNames, - R.array.rendererScreenLayoutValues + titleId = R.string.renderer_screen_layout, + choicesId = R.array.rendererScreenLayoutNames, + valuesId = R.array.rendererScreenLayoutValues ) ) put( SingleChoiceSetting( IntSetting.RENDERER_ASPECT_RATIO, - R.string.renderer_aspect_ratio, - 0, - R.array.rendererAspectRatioNames, - R.array.rendererAspectRatioValues + titleId = R.string.renderer_aspect_ratio, + choicesId = R.array.rendererAspectRatioNames, + valuesId = R.array.rendererAspectRatioValues + ) + ) + put( + SingleChoiceSetting( + IntSetting.VERTICAL_ALIGNMENT, + titleId = R.string.vertical_alignment, + descriptionId = 0, + choicesId = R.array.verticalAlignmentEntries, + valuesId = R.array.verticalAlignmentValues ) ) put( SwitchSetting( BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE, - R.string.use_disk_shader_cache, - R.string.use_disk_shader_cache_description + titleId = R.string.use_disk_shader_cache, + descriptionId = R.string.use_disk_shader_cache_description ) ) put( SwitchSetting( BooleanSetting.RENDERER_FORCE_MAX_CLOCK, - R.string.renderer_force_max_clock, - R.string.renderer_force_max_clock_description + titleId = R.string.renderer_force_max_clock, + descriptionId = R.string.renderer_force_max_clock_description ) ) put( SwitchSetting( BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS, - R.string.renderer_asynchronous_shaders, - R.string.renderer_asynchronous_shaders_description + titleId = R.string.renderer_asynchronous_shaders, + descriptionId = R.string.renderer_asynchronous_shaders_description ) ) put( SwitchSetting( BooleanSetting.RENDERER_REACTIVE_FLUSHING, - R.string.renderer_reactive_flushing, - R.string.renderer_reactive_flushing_description + titleId = R.string.renderer_reactive_flushing, + descriptionId = R.string.renderer_reactive_flushing_description ) ) put( SingleChoiceSetting( IntSetting.MAX_ANISOTROPY, - R.string.anisotropic_filtering, - R.string.anisotropic_filtering_description, - R.array.anisoEntries, - R.array.anisoValues + titleId = R.string.anisotropic_filtering, + descriptionId = R.string.anisotropic_filtering_description, + choicesId = R.array.anisoEntries, + valuesId = R.array.anisoValues ) ) put( SingleChoiceSetting( IntSetting.AUDIO_OUTPUT_ENGINE, - R.string.audio_output_engine, - 0, - R.array.outputEngineEntries, - R.array.outputEngineValues + titleId = R.string.audio_output_engine, + choicesId = R.array.outputEngineEntries, + valuesId = R.array.outputEngineValues ) ) put( SliderSetting( ByteSetting.AUDIO_VOLUME, - R.string.audio_volume, - R.string.audio_volume_description, - 0, - 100, - "%" + titleId = R.string.audio_volume, + descriptionId = R.string.audio_volume_description, + units = "%" ) ) put( SingleChoiceSetting( IntSetting.RENDERER_BACKEND, - R.string.renderer_api, - 0, - R.array.rendererApiNames, - R.array.rendererApiValues + titleId = R.string.renderer_api, + choicesId = R.array.rendererApiNames, + valuesId = R.array.rendererApiValues ) ) put( SwitchSetting( BooleanSetting.RENDERER_DEBUG, - R.string.renderer_debug, - R.string.renderer_debug_description + titleId = R.string.renderer_debug, + descriptionId = R.string.renderer_debug_description ) ) put( SwitchSetting( BooleanSetting.CPU_DEBUG_MODE, - R.string.cpu_debug_mode, - R.string.cpu_debug_mode_description + titleId = R.string.cpu_debug_mode, + descriptionId = R.string.cpu_debug_mode_description ) ) @@ -327,7 +385,7 @@ abstract class SettingsItem( override fun reset() = setBoolean(defaultValue) } - put(SwitchSetting(fastmem, R.string.fastmem, 0)) + put(SwitchSetting(fastmem, R.string.fastmem)) } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt index 97a5a9e59..ea5e099ed 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt @@ -3,16 +3,20 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import androidx.annotation.ArrayRes +import androidx.annotation.StringRes import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting class SingleChoiceSetting( setting: AbstractSetting, - titleId: Int, - descriptionId: Int, - val choicesId: Int, - val valuesId: Int -) : SettingsItem(setting, titleId, descriptionId) { + @StringRes titleId: Int = 0, + titleString: String = "", + @StringRes descriptionId: Int = 0, + descriptionString: String = "", + @ArrayRes val choicesId: Int, + @ArrayRes val valuesId: Int +) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) { override val type = TYPE_SINGLE_CHOICE fun getSelectedValue(needsGlobal: Boolean = false) = diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt index b9b709bf7..6a5cdf48b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt @@ -3,6 +3,7 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import androidx.annotation.StringRes import org.yuzu.yuzu_emu.features.settings.model.AbstractByteSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting @@ -12,12 +13,14 @@ import kotlin.math.roundToInt class SliderSetting( setting: AbstractSetting, - titleId: Int, - descriptionId: Int, - val min: Int, - val max: Int, - val units: String -) : SettingsItem(setting, titleId, descriptionId) { + @StringRes titleId: Int = 0, + titleString: String = "", + @StringRes descriptionId: Int = 0, + descriptionString: String = "", + val min: Int = 0, + val max: Int = 100, + val units: String = "" +) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) { override val type = TYPE_SLIDER fun getSelectedValue(needsGlobal: Boolean = false) = diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt new file mode 100644 index 000000000..1eb999416 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.model.view + +import androidx.annotation.StringRes +import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting + +class StringInputSetting( + setting: AbstractStringSetting, + @StringRes titleId: Int = 0, + titleString: String = "", + @StringRes descriptionId: Int = 0, + descriptionString: String = "" +) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) { + override val type = TYPE_STRING_INPUT + + fun getSelectedValue(needsGlobal: Boolean = false) = setting.getValueAsString(needsGlobal) + + fun setSelectedValue(selection: String) = + (setting as AbstractStringSetting).setString(selection) +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt index ba7920f50..5260ff4dc 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt @@ -3,15 +3,18 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import androidx.annotation.StringRes import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting class StringSingleChoiceSetting( private val stringSetting: AbstractStringSetting, - titleId: Int, - descriptionId: Int, + @StringRes titleId: Int = 0, + titleString: String = "", + @StringRes descriptionId: Int = 0, + descriptionString: String = "", val choices: Array<String>, val values: Array<String> -) : SettingsItem(stringSetting, titleId, descriptionId) { +) : SettingsItem(stringSetting, titleId, titleString, descriptionId, descriptionString) { override val type = TYPE_STRING_SINGLE_CHOICE fun getValueAt(index: Int): String = @@ -20,7 +23,7 @@ class StringSingleChoiceSetting( fun getSelectedValue(needsGlobal: Boolean = false) = stringSetting.getString(needsGlobal) fun setSelectedValue(value: String) = stringSetting.setString(value) - val selectValueIndex: Int + val selectedValueIndex: Int get() { for (i in values.indices) { if (values[i] == getSelectedValue()) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt index 94953b18a..c722393dd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt @@ -8,10 +8,12 @@ import androidx.annotation.StringRes import org.yuzu.yuzu_emu.features.settings.model.Settings class SubmenuSetting( - @StringRes titleId: Int, - @StringRes descriptionId: Int, - @DrawableRes val iconId: Int, + @StringRes titleId: Int = 0, + titleString: String = "", + @StringRes descriptionId: Int = 0, + descriptionString: String = "", + @DrawableRes val iconId: Int = 0, val menuKey: Settings.MenuTag -) : SettingsItem(emptySetting, titleId, descriptionId) { +) : SettingsItem(emptySetting, titleId, titleString, descriptionId, descriptionString) { override val type = TYPE_SUBMENU } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt index 44d47dd69..4984bf52e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt @@ -3,15 +3,18 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import androidx.annotation.StringRes import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting class SwitchSetting( setting: AbstractSetting, - titleId: Int, - descriptionId: Int -) : SettingsItem(setting, titleId, descriptionId) { + @StringRes titleId: Int = 0, + titleString: String = "", + @StringRes descriptionId: Int = 0, + descriptionString: String = "" +) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) { override val type = TYPE_SWITCH fun getIsChecked(needsGlobal: Boolean = false): Boolean { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputDialogFragment.kt new file mode 100644 index 000000000..16a1d0504 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputDialogFragment.kt @@ -0,0 +1,300 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui + +import android.app.Dialog +import android.graphics.drawable.Animatable2 +import android.graphics.drawable.AnimatedVectorDrawable +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.view.InputDevice +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.databinding.DialogMappingBinding +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.features.input.model.NativeAnalog +import org.yuzu.yuzu_emu.features.input.model.NativeButton +import org.yuzu.yuzu_emu.features.settings.model.view.AnalogInputSetting +import org.yuzu.yuzu_emu.features.settings.model.view.ButtonInputSetting +import org.yuzu.yuzu_emu.features.settings.model.view.InputSetting +import org.yuzu.yuzu_emu.features.settings.model.view.ModifierInputSetting +import org.yuzu.yuzu_emu.utils.InputHandler +import org.yuzu.yuzu_emu.utils.ParamPackage + +class InputDialogFragment : DialogFragment() { + private var inputAccepted = false + + private var position: Int = 0 + + private lateinit var inputSetting: InputSetting + + private lateinit var binding: DialogMappingBinding + + private val settingsViewModel: SettingsViewModel by activityViewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (settingsViewModel.clickedItem == null) dismiss() + + position = requireArguments().getInt(POSITION) + + InputHandler.updateControllerData() + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + inputSetting = settingsViewModel.clickedItem as InputSetting + binding = DialogMappingBinding.inflate(layoutInflater) + + val builder = MaterialAlertDialogBuilder(requireContext()) + .setPositiveButton(android.R.string.cancel) { _, _ -> + NativeInput.stopMapping() + dismiss() + } + .setView(binding.root) + + val playButtonMapAnimation = { twoDirections: Boolean -> + val stickAnimation: AnimatedVectorDrawable + val buttonAnimation: AnimatedVectorDrawable + binding.imageStickAnimation.apply { + val anim = if (twoDirections) { + R.drawable.stick_two_direction_anim + } else { + R.drawable.stick_one_direction_anim + } + setBackgroundResource(anim) + stickAnimation = background as AnimatedVectorDrawable + } + binding.imageButtonAnimation.apply { + setBackgroundResource(R.drawable.button_anim) + buttonAnimation = background as AnimatedVectorDrawable + } + stickAnimation.registerAnimationCallback(object : Animatable2.AnimationCallback() { + override fun onAnimationEnd(drawable: Drawable?) { + buttonAnimation.start() + } + }) + buttonAnimation.registerAnimationCallback(object : Animatable2.AnimationCallback() { + override fun onAnimationEnd(drawable: Drawable?) { + stickAnimation.start() + } + }) + stickAnimation.start() + } + + when (val setting = inputSetting) { + is AnalogInputSetting -> { + when (setting.nativeAnalog) { + NativeAnalog.LStick -> builder.setTitle( + getString(R.string.map_control, getString(R.string.left_stick)) + ) + + NativeAnalog.RStick -> builder.setTitle( + getString(R.string.map_control, getString(R.string.right_stick)) + ) + } + + builder.setMessage(R.string.stick_map_description) + + playButtonMapAnimation.invoke(true) + } + + is ModifierInputSetting -> { + builder.setTitle(getString(R.string.map_control, setting.title)) + .setMessage(R.string.button_map_description) + playButtonMapAnimation.invoke(false) + } + + is ButtonInputSetting -> { + if (setting.nativeButton == NativeButton.DUp || + setting.nativeButton == NativeButton.DDown || + setting.nativeButton == NativeButton.DLeft || + setting.nativeButton == NativeButton.DRight + ) { + builder.setTitle(getString(R.string.map_dpad_direction, setting.title)) + } else { + builder.setTitle(getString(R.string.map_control, setting.title)) + } + builder.setMessage(R.string.button_map_description) + playButtonMapAnimation.invoke(false) + } + } + + return builder.create() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + view.requestFocus() + view.setOnFocusChangeListener { v, hasFocus -> if (!hasFocus) v.requestFocus() } + dialog?.setOnKeyListener { _, _, keyEvent -> onKeyEvent(keyEvent) } + binding.root.setOnGenericMotionListener { _, motionEvent -> onMotionEvent(motionEvent) } + NativeInput.beginMapping(inputSetting.inputType.int) + } + + private fun onKeyEvent(event: KeyEvent): Boolean { + if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK && + event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD + ) { + return false + } + + val action = when (event.action) { + KeyEvent.ACTION_DOWN -> NativeInput.ButtonState.PRESSED + KeyEvent.ACTION_UP -> NativeInput.ButtonState.RELEASED + else -> return false + } + val controllerData = + InputHandler.androidControllers[event.device.controllerNumber] ?: return false + NativeInput.onGamePadButtonEvent( + controllerData.getGUID(), + controllerData.getPort(), + event.keyCode, + action + ) + onInputReceived(event.device) + return true + } + + private fun onMotionEvent(event: MotionEvent): Boolean { + if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK && + event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD + ) { + return false + } + + // Temp workaround for DPads that give both axis and button input. The input system can't + // take in a specific axis direction for a binding so you lose half of the directions for a DPad. + + val controllerData = + InputHandler.androidControllers[event.device.controllerNumber] ?: return false + event.device.motionRanges.forEach { + NativeInput.onGamePadAxisEvent( + controllerData.getGUID(), + controllerData.getPort(), + it.axis, + event.getAxisValue(it.axis) + ) + onInputReceived(event.device) + } + return true + } + + private fun onInputReceived(device: InputDevice) { + val params = ParamPackage(NativeInput.getNextInput()) + if (params.has("engine") && isInputAcceptable(params) && !inputAccepted) { + inputAccepted = true + setResult(params, device) + } + } + + private fun setResult(params: ParamPackage, device: InputDevice) { + NativeInput.stopMapping() + params.set("display", "${device.name} ${params.get("port", 0)}") + when (val item = settingsViewModel.clickedItem as InputSetting) { + is ModifierInputSetting, + is ButtonInputSetting -> { + // Invert DPad up and left bindings by default + val tempSetting = inputSetting as? ButtonInputSetting + if (tempSetting != null) { + if (tempSetting.nativeButton == NativeButton.DUp || + tempSetting.nativeButton == NativeButton.DLeft && + params.has("axis") + ) { + params.set("invert", "-") + } + } + + item.setSelectedValue(params) + settingsViewModel.setAdapterItemChanged(position) + } + + is AnalogInputSetting -> { + var analogParam = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog) + analogParam = adjustAnalogParam(params, analogParam, item.analogDirection.param) + + // Invert Y-Axis by default + analogParam.set("invert_y", "-") + + item.setSelectedValue(analogParam) + settingsViewModel.setReloadListAndNotifyDataset(true) + } + } + dismiss() + } + + private fun adjustAnalogParam( + inputParam: ParamPackage, + analogParam: ParamPackage, + buttonName: String + ): ParamPackage { + // The poller returned a complete axis, so set all the buttons + if (inputParam.has("axis_x") && inputParam.has("axis_y")) { + return inputParam + } + + // Check if the current configuration has either no engine or an axis binding. + // Clears out the old binding and adds one with analog_from_button. + if (!analogParam.has("engine") || analogParam.has("axis_x") || analogParam.has("axis_y")) { + analogParam.clear() + analogParam.set("engine", "analog_from_button") + } + analogParam.set(buttonName, inputParam.serialize()) + return analogParam + } + + private fun isInputAcceptable(params: ParamPackage): Boolean { + if (InputHandler.registeredControllers.size == 1) { + return true + } + + if (params.has("motion")) { + return true + } + + val currentDevice = settingsViewModel.getCurrentDeviceParams(params) + if (currentDevice.get("engine", "any") == "any") { + return true + } + + val guidMatch = params.get("guid", "") == currentDevice.get("guid", "") || + params.get("guid", "") == currentDevice.get("guid2", "") + return params.get("engine", "") == currentDevice.get("engine", "") && + guidMatch && + params.get("port", 0) == currentDevice.get("port", 0) + } + + companion object { + const val TAG = "InputDialogFragment" + + const val POSITION = "Position" + + fun newInstance( + inputMappingViewModel: SettingsViewModel, + setting: InputSetting, + position: Int + ): InputDialogFragment { + inputMappingViewModel.clickedItem = setting + val args = Bundle() + args.putInt(POSITION, position) + val fragment = InputDialogFragment() + fragment.arguments = args + return fragment + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileAdapter.kt new file mode 100644 index 000000000..5656e9d8d --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileAdapter.kt @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.adapters.AbstractListAdapter +import org.yuzu.yuzu_emu.databinding.ListItemInputProfileBinding +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder +import org.yuzu.yuzu_emu.R + +class InputProfileAdapter(options: List<ProfileItem>) : + AbstractListAdapter<ProfileItem, AbstractViewHolder<ProfileItem>>(options) { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): AbstractViewHolder<ProfileItem> { + ListItemInputProfileBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return InputProfileViewHolder(it) } + } + + inner class InputProfileViewHolder(val binding: ListItemInputProfileBinding) : + AbstractViewHolder<ProfileItem>(binding) { + override fun bind(model: ProfileItem) { + when (model) { + is ExistingProfileItem -> { + binding.title.text = model.name + binding.buttonNew.visibility = View.GONE + binding.buttonDelete.visibility = View.VISIBLE + binding.buttonDelete.setOnClickListener { model.deleteProfile.invoke() } + binding.buttonSave.visibility = View.VISIBLE + binding.buttonSave.setOnClickListener { model.saveProfile.invoke() } + binding.buttonLoad.visibility = View.VISIBLE + binding.buttonLoad.setOnClickListener { model.loadProfile.invoke() } + } + + is NewProfileItem -> { + binding.title.text = model.name + binding.buttonNew.visibility = View.VISIBLE + binding.buttonNew.setOnClickListener { model.createNewProfile.invoke() } + binding.buttonSave.visibility = View.GONE + binding.buttonDelete.visibility = View.GONE + binding.buttonLoad.visibility = View.GONE + } + } + } + } +} + +sealed interface ProfileItem { + val name: String +} + +data class NewProfileItem( + val createNewProfile: () -> Unit +) : ProfileItem { + override val name: String = YuzuApplication.appContext.getString(R.string.create_new_profile) +} + +data class ExistingProfileItem( + override val name: String, + val deleteProfile: () -> Unit, + val saveProfile: () -> Unit, + val loadProfile: () -> Unit +) : ProfileItem diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt new file mode 100644 index 000000000..1bae593ae --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt @@ -0,0 +1,148 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui + +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.databinding.DialogInputProfilesBinding +import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting +import org.yuzu.yuzu_emu.fragments.MessageDialogFragment +import org.yuzu.yuzu_emu.utils.collect + +class InputProfileDialogFragment : DialogFragment() { + private var position = 0 + + private val settingsViewModel: SettingsViewModel by activityViewModels() + + private lateinit var binding: DialogInputProfilesBinding + + private lateinit var setting: InputProfileSetting + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + position = requireArguments().getInt(POSITION) + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogInputProfilesBinding.inflate(layoutInflater) + + setting = settingsViewModel.clickedItem as InputProfileSetting + val options = mutableListOf<ProfileItem>().apply { + add( + NewProfileItem( + createNewProfile = { + NewInputProfileDialogFragment.newInstance( + settingsViewModel, + setting, + position + ).show(parentFragmentManager, NewInputProfileDialogFragment.TAG) + dismiss() + } + ) + ) + + val onActionDismiss = { + settingsViewModel.setReloadListAndNotifyDataset(true) + dismiss() + } + setting.getProfileNames().forEach { + add( + ExistingProfileItem( + it, + deleteProfile = { + settingsViewModel.setShouldShowDeleteProfileDialog(it) + }, + saveProfile = { + if (!setting.saveProfile(it)) { + Toast.makeText( + requireContext(), + R.string.failed_to_save_profile, + Toast.LENGTH_SHORT + ).show() + } + onActionDismiss.invoke() + }, + loadProfile = { + if (!setting.loadProfile(it)) { + Toast.makeText( + requireContext(), + R.string.failed_to_load_profile, + Toast.LENGTH_SHORT + ).show() + } + onActionDismiss.invoke() + } + ) + ) + } + } + binding.listProfiles.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = InputProfileAdapter(options) + } + + return MaterialAlertDialogBuilder(requireContext()) + .setView(binding.root) + .create() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + settingsViewModel.shouldShowDeleteProfileDialog.collect(viewLifecycleOwner) { + if (it.isNotEmpty()) { + MessageDialogFragment.newInstance( + activity = requireActivity(), + titleId = R.string.delete_input_profile, + descriptionId = R.string.delete_input_profile_description, + positiveAction = { + setting.deleteProfile(it) + settingsViewModel.setReloadListAndNotifyDataset(true) + }, + negativeAction = {}, + negativeButtonTitleId = android.R.string.cancel + ).show(parentFragmentManager, MessageDialogFragment.TAG) + settingsViewModel.setShouldShowDeleteProfileDialog("") + dismiss() + } + } + } + + companion object { + const val TAG = "InputProfileDialogFragment" + + const val POSITION = "Position" + + fun newInstance( + settingsViewModel: SettingsViewModel, + profileSetting: InputProfileSetting, + position: Int + ): InputProfileDialogFragment { + settingsViewModel.clickedItem = profileSetting + + val args = Bundle() + args.putInt(POSITION, position) + val fragment = InputProfileDialogFragment() + fragment.arguments = args + return fragment + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/NewInputProfileDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/NewInputProfileDialogFragment.kt new file mode 100644 index 000000000..6e52bea80 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/NewInputProfileDialogFragment.kt @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui + +import android.app.Dialog +import android.os.Bundle +import android.widget.Toast +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding +import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting +import org.yuzu.yuzu_emu.R + +class NewInputProfileDialogFragment : DialogFragment() { + private var position = 0 + + private val settingsViewModel: SettingsViewModel by activityViewModels() + + private lateinit var binding: DialogEditTextBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + position = requireArguments().getInt(POSITION) + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogEditTextBinding.inflate(layoutInflater) + + val setting = settingsViewModel.clickedItem as InputProfileSetting + return MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.enter_profile_name) + .setPositiveButton(android.R.string.ok) { _, _ -> + val profileName = binding.editText.text.toString() + if (!setting.isProfileNameValid(profileName)) { + Toast.makeText( + requireContext(), + R.string.invalid_profile_name, + Toast.LENGTH_SHORT + ).show() + return@setPositiveButton + } + + if (!setting.createProfile(profileName)) { + Toast.makeText( + requireContext(), + R.string.profile_name_already_exists, + Toast.LENGTH_SHORT + ).show() + } else { + settingsViewModel.setAdapterItemChanged(position) + } + } + .setNegativeButton(android.R.string.cancel, null) + .setView(binding.root) + .show() + } + + companion object { + const val TAG = "NewInputProfileDialogFragment" + + const val POSITION = "Position" + + fun newInstance( + settingsViewModel: SettingsViewModel, + profileSetting: InputProfileSetting, + position: Int + ): NewInputProfileDialogFragment { + settingsViewModel.clickedItem = profileSetting + + val args = Bundle() + args.putInt(POSITION, position) + val fragment = NewInputProfileDialogFragment() + fragment.arguments = args + return fragment + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt index 6f072241a..455b3b5ff 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt @@ -13,21 +13,16 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.NavHostFragment import androidx.navigation.navArgs import com.google.android.material.color.MaterialColors -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.NativeLibrary import java.io.IOException import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding +import org.yuzu.yuzu_emu.features.input.NativeInput import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment -import org.yuzu.yuzu_emu.model.SettingsViewModel import org.yuzu.yuzu_emu.utils.* class SettingsActivity : AppCompatActivity() { @@ -70,39 +65,23 @@ class SettingsActivity : AppCompatActivity() { ) } - lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - settingsViewModel.shouldRecreate.collectLatest { - if (it) { - settingsViewModel.setShouldRecreate(false) - recreate() - } - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - settingsViewModel.shouldNavigateBack.collectLatest { - if (it) { - settingsViewModel.setShouldNavigateBack(false) - navigateBack() - } - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - settingsViewModel.shouldShowResetSettingsDialog.collectLatest { - if (it) { - settingsViewModel.setShouldShowResetSettingsDialog(false) - ResetSettingsDialogFragment().show( - supportFragmentManager, - ResetSettingsDialogFragment.TAG - ) - } - } - } + settingsViewModel.shouldRecreate.collect( + this, + resetState = { settingsViewModel.setShouldRecreate(false) } + ) { if (it) recreate() } + settingsViewModel.shouldNavigateBack.collect( + this, + resetState = { settingsViewModel.setShouldNavigateBack(false) } + ) { if (it) navigateBack() } + settingsViewModel.shouldShowResetSettingsDialog.collect( + this, + resetState = { settingsViewModel.setShouldShowResetSettingsDialog(false) } + ) { + if (it) { + ResetSettingsDialogFragment().show( + supportFragmentManager, + ResetSettingsDialogFragment.TAG + ) } } @@ -137,6 +116,7 @@ class SettingsActivity : AppCompatActivity() { super.onStop() Log.info("[SettingsActivity] Settings activity stopping. Saving settings to INI...") if (isFinishing) { + NativeInput.reloadInputDevices() NativeLibrary.applySettings() if (args.game == null) { NativeConfig.saveGlobalConfig() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt index be9b3031b..500ac6e66 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt @@ -8,12 +8,11 @@ import android.icu.util.Calendar import android.icu.util.TimeZone import android.text.format.DateFormat import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.PopupMenu import androidx.fragment.app.Fragment -import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.recyclerview.widget.AsyncDifferConfig import androidx.recyclerview.widget.DiffUtil @@ -21,16 +20,18 @@ import androidx.recyclerview.widget.ListAdapter import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.timepicker.MaterialTimePicker import com.google.android.material.timepicker.TimeFormat -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.SettingsNavigationDirections import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding +import org.yuzu.yuzu_emu.databinding.ListItemSettingInputBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.features.input.model.AnalogDirection +import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting import org.yuzu.yuzu_emu.features.settings.model.view.* import org.yuzu.yuzu_emu.features.settings.ui.viewholder.* -import org.yuzu.yuzu_emu.fragments.SettingsDialogFragment -import org.yuzu.yuzu_emu.model.SettingsViewModel +import org.yuzu.yuzu_emu.utils.ParamPackage class SettingsAdapter( private val fragment: Fragment, @@ -41,19 +42,6 @@ class SettingsAdapter( private val settingsViewModel: SettingsViewModel get() = ViewModelProvider(fragment.requireActivity())[SettingsViewModel::class.java] - init { - fragment.viewLifecycleOwner.lifecycleScope.launch { - fragment.repeatOnLifecycle(Lifecycle.State.STARTED) { - settingsViewModel.adapterItemChanged.collect { - if (it != -1) { - notifyItemChanged(it) - settingsViewModel.setAdapterItemChanged(-1) - } - } - } - } - } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder { val inflater = LayoutInflater.from(parent.context) return when (viewType) { @@ -85,8 +73,23 @@ class SettingsAdapter( RunnableViewHolder(ListItemSettingBinding.inflate(inflater), this) } + SettingsItem.TYPE_INPUT -> { + InputViewHolder(ListItemSettingInputBinding.inflate(inflater), this) + } + + SettingsItem.TYPE_INT_SINGLE_CHOICE -> { + SingleChoiceViewHolder(ListItemSettingBinding.inflate(inflater), this) + } + + SettingsItem.TYPE_INPUT_PROFILE -> { + InputProfileViewHolder(ListItemSettingBinding.inflate(inflater), this) + } + + SettingsItem.TYPE_STRING_INPUT -> { + StringInputViewHolder(ListItemSettingBinding.inflate(inflater), this) + } + else -> { - // TODO: Create an error view since we can't return null now HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this) } } @@ -126,6 +129,15 @@ class SettingsAdapter( ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG) } + fun onIntSingleChoiceClick(item: IntSingleChoiceSetting, position: Int) { + SettingsDialogFragment.newInstance( + settingsViewModel, + item, + SettingsItem.TYPE_INT_SINGLE_CHOICE, + position + ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG) + } + fun onDateTimeClick(item: DateTimeSetting, position: Int) { val storedTime = item.getValue() * 1000 @@ -185,6 +197,214 @@ class SettingsAdapter( fragment.view?.findNavController()?.navigate(action) } + fun onInputProfileClick(item: InputProfileSetting, position: Int) { + InputProfileDialogFragment.newInstance( + settingsViewModel, + item, + position + ).show(fragment.childFragmentManager, InputProfileDialogFragment.TAG) + } + + fun onInputClick(item: InputSetting, position: Int) { + InputDialogFragment.newInstance( + settingsViewModel, + item, + position + ).show(fragment.childFragmentManager, InputDialogFragment.TAG) + } + + fun onInputOptionsClick(anchor: View, item: InputSetting, position: Int) { + val popup = PopupMenu(context, anchor) + popup.menuInflater.inflate(R.menu.menu_input_options, popup.menu) + + popup.menu.apply { + val invertAxis = findItem(R.id.invert_axis) + val invertButton = findItem(R.id.invert_button) + val toggleButton = findItem(R.id.toggle_button) + val turboButton = findItem(R.id.turbo_button) + val setThreshold = findItem(R.id.set_threshold) + val toggleAxis = findItem(R.id.toggle_axis) + when (item) { + is AnalogInputSetting -> { + val params = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog) + + invertAxis.isVisible = true + invertAxis.isCheckable = true + invertAxis.isChecked = when (item.analogDirection) { + AnalogDirection.Left, AnalogDirection.Right -> { + params.get("invert_x", "+") == "-" + } + + AnalogDirection.Up, AnalogDirection.Down -> { + params.get("invert_y", "+") == "-" + } + } + invertAxis.setOnMenuItemClickListener { + if (item.analogDirection == AnalogDirection.Left || + item.analogDirection == AnalogDirection.Right + ) { + val invertValue = params.get("invert_x", "+") == "-" + val invertString = if (invertValue) "+" else "-" + params.set("invert_x", invertString) + } else if ( + item.analogDirection == AnalogDirection.Up || + item.analogDirection == AnalogDirection.Down + ) { + val invertValue = params.get("invert_y", "+") == "-" + val invertString = if (invertValue) "+" else "-" + params.set("invert_y", invertString) + } + true + } + + popup.setOnDismissListener { + NativeInput.setStickParam(item.playerIndex, item.nativeAnalog, params) + settingsViewModel.setDatasetChanged(true) + } + } + + is ButtonInputSetting -> { + val params = NativeInput.getButtonParam(item.playerIndex, item.nativeButton) + if (params.has("code") || params.has("button") || params.has("hat")) { + val buttonInvert = params.get("inverted", false) + invertButton.isVisible = true + invertButton.isCheckable = true + invertButton.isChecked = buttonInvert + invertButton.setOnMenuItemClickListener { + params.set("inverted", !buttonInvert) + true + } + + val toggle = params.get("toggle", false) + toggleButton.isVisible = true + toggleButton.isCheckable = true + toggleButton.isChecked = toggle + toggleButton.setOnMenuItemClickListener { + params.set("toggle", !toggle) + true + } + + val turbo = params.get("turbo", false) + turboButton.isVisible = true + turboButton.isCheckable = true + turboButton.isChecked = turbo + turboButton.setOnMenuItemClickListener { + params.set("turbo", !turbo) + true + } + } else if (params.has("axis")) { + val axisInvert = params.get("invert", "+") == "-" + invertAxis.isVisible = true + invertAxis.isCheckable = true + invertAxis.isChecked = axisInvert + invertAxis.setOnMenuItemClickListener { + params.set("invert", if (!axisInvert) "-" else "+") + true + } + + val buttonInvert = params.get("inverted", false) + invertButton.isVisible = true + invertButton.isCheckable = true + invertButton.isChecked = buttonInvert + invertButton.setOnMenuItemClickListener { + params.set("inverted", !buttonInvert) + true + } + + setThreshold.isVisible = true + val thresholdSetting = object : AbstractIntSetting { + override val key = "" + + override fun getInt(needsGlobal: Boolean): Int = + (params.get("threshold", 0.5f) * 100).toInt() + + override fun setInt(value: Int) { + params.set("threshold", value.toFloat() / 100) + NativeInput.setButtonParam( + item.playerIndex, + item.nativeButton, + params + ) + } + + override val defaultValue = 50 + + override fun getValueAsString(needsGlobal: Boolean): String = + getInt(needsGlobal).toString() + + override fun reset() = setInt(defaultValue) + } + setThreshold.setOnMenuItemClickListener { + onSliderClick( + SliderSetting(thresholdSetting, R.string.set_threshold), + position + ) + true + } + + val axisToggle = params.get("toggle", false) + toggleAxis.isVisible = true + toggleAxis.isCheckable = true + toggleAxis.isChecked = axisToggle + toggleAxis.setOnMenuItemClickListener { + params.set("toggle", !axisToggle) + true + } + } + + popup.setOnDismissListener { + NativeInput.setButtonParam(item.playerIndex, item.nativeButton, params) + settingsViewModel.setAdapterItemChanged(position) + } + } + + is ModifierInputSetting -> { + val stickParams = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog) + val modifierParams = ParamPackage(stickParams.get("modifier", "")) + + val invert = modifierParams.get("inverted", false) + invertButton.isVisible = true + invertButton.isCheckable = true + invertButton.isChecked = invert + invertButton.setOnMenuItemClickListener { + modifierParams.set("inverted", !invert) + stickParams.set("modifier", modifierParams.serialize()) + true + } + + val toggle = modifierParams.get("toggle", false) + toggleButton.isVisible = true + toggleButton.isCheckable = true + toggleButton.isChecked = toggle + toggleButton.setOnMenuItemClickListener { + modifierParams.set("toggle", !toggle) + stickParams.set("modifier", modifierParams.serialize()) + true + } + + popup.setOnDismissListener { + NativeInput.setStickParam( + item.playerIndex, + item.nativeAnalog, + stickParams + ) + settingsViewModel.setAdapterItemChanged(position) + } + } + } + } + popup.show() + } + + fun onStringInputClick(item: StringInputSetting, position: Int) { + SettingsDialogFragment.newInstance( + settingsViewModel, + item, + SettingsItem.TYPE_STRING_INPUT, + position + ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG) + } + fun onLongClick(item: SettingsItem, position: Int): Boolean { SettingsDialogFragment.newInstance( settingsViewModel, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt new file mode 100644 index 000000000..7f562a1f4 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt @@ -0,0 +1,301 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui + +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.slider.Slider +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding +import org.yuzu.yuzu_emu.databinding.DialogSliderBinding +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.features.input.model.AnalogDirection +import org.yuzu.yuzu_emu.features.settings.model.view.AnalogInputSetting +import org.yuzu.yuzu_emu.features.settings.model.view.ButtonInputSetting +import org.yuzu.yuzu_emu.features.settings.model.view.IntSingleChoiceSetting +import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem +import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting +import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting +import org.yuzu.yuzu_emu.features.settings.model.view.StringInputSetting +import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting +import org.yuzu.yuzu_emu.utils.ParamPackage +import org.yuzu.yuzu_emu.utils.collect + +class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener { + private var type = 0 + private var position = 0 + + private var defaultCancelListener = + DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() } + + private val settingsViewModel: SettingsViewModel by activityViewModels() + + private lateinit var sliderBinding: DialogSliderBinding + private lateinit var stringInputBinding: DialogEditTextBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + type = requireArguments().getInt(TYPE) + position = requireArguments().getInt(POSITION) + + if (settingsViewModel.clickedItem == null) dismiss() + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return when (type) { + TYPE_RESET_SETTING -> { + MaterialAlertDialogBuilder(requireContext()) + .setMessage(R.string.reset_setting_confirmation) + .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> + when (val item = settingsViewModel.clickedItem) { + is AnalogInputSetting -> { + val stickParam = NativeInput.getStickParam( + item.playerIndex, + item.nativeAnalog + ) + if (stickParam.get("engine", "") == "analog_from_button") { + when (item.analogDirection) { + AnalogDirection.Up -> stickParam.erase("up") + AnalogDirection.Down -> stickParam.erase("down") + AnalogDirection.Left -> stickParam.erase("left") + AnalogDirection.Right -> stickParam.erase("right") + } + NativeInput.setStickParam( + item.playerIndex, + item.nativeAnalog, + stickParam + ) + settingsViewModel.setAdapterItemChanged(position) + } else { + NativeInput.setStickParam( + item.playerIndex, + item.nativeAnalog, + ParamPackage() + ) + settingsViewModel.setDatasetChanged(true) + } + } + + is ButtonInputSetting -> { + NativeInput.setButtonParam( + item.playerIndex, + item.nativeButton, + ParamPackage() + ) + settingsViewModel.setAdapterItemChanged(position) + } + + else -> { + settingsViewModel.clickedItem!!.setting.reset() + settingsViewModel.setAdapterItemChanged(position) + } + } + } + .setNegativeButton(android.R.string.cancel, null) + .create() + } + + SettingsItem.TYPE_SINGLE_CHOICE -> { + val item = settingsViewModel.clickedItem as SingleChoiceSetting + val value = getSelectionForSingleChoiceValue(item) + MaterialAlertDialogBuilder(requireContext()) + .setTitle(item.title) + .setSingleChoiceItems(item.choicesId, value, this) + .create() + } + + SettingsItem.TYPE_SLIDER -> { + sliderBinding = DialogSliderBinding.inflate(layoutInflater) + val item = settingsViewModel.clickedItem as SliderSetting + + settingsViewModel.setSliderTextValue(item.getSelectedValue().toFloat(), item.units) + sliderBinding.slider.apply { + valueFrom = item.min.toFloat() + valueTo = item.max.toFloat() + value = settingsViewModel.sliderProgress.value.toFloat() + addOnChangeListener { _: Slider, value: Float, _: Boolean -> + settingsViewModel.setSliderTextValue(value, item.units) + } + } + + MaterialAlertDialogBuilder(requireContext()) + .setTitle(item.title) + .setView(sliderBinding.root) + .setPositiveButton(android.R.string.ok, this) + .setNegativeButton(android.R.string.cancel, defaultCancelListener) + .create() + } + + SettingsItem.TYPE_STRING_INPUT -> { + stringInputBinding = DialogEditTextBinding.inflate(layoutInflater) + val item = settingsViewModel.clickedItem as StringInputSetting + stringInputBinding.editText.setText(item.getSelectedValue()) + MaterialAlertDialogBuilder(requireContext()) + .setTitle(item.title) + .setView(stringInputBinding.root) + .setPositiveButton(android.R.string.ok, this) + .setNegativeButton(android.R.string.cancel, defaultCancelListener) + .create() + } + + SettingsItem.TYPE_STRING_SINGLE_CHOICE -> { + val item = settingsViewModel.clickedItem as StringSingleChoiceSetting + MaterialAlertDialogBuilder(requireContext()) + .setTitle(item.title) + .setSingleChoiceItems(item.choices, item.selectedValueIndex, this) + .create() + } + + SettingsItem.TYPE_INT_SINGLE_CHOICE -> { + val item = settingsViewModel.clickedItem as IntSingleChoiceSetting + MaterialAlertDialogBuilder(requireContext()) + .setTitle(item.title) + .setSingleChoiceItems(item.choices, item.selectedValueIndex, this) + .create() + } + + else -> super.onCreateDialog(savedInstanceState) + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return when (type) { + SettingsItem.TYPE_SLIDER -> sliderBinding.root + SettingsItem.TYPE_STRING_INPUT -> stringInputBinding.root + else -> super.onCreateView(inflater, container, savedInstanceState) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + when (type) { + SettingsItem.TYPE_SLIDER -> { + settingsViewModel.sliderTextValue.collect(viewLifecycleOwner) { + sliderBinding.textValue.text = it + } + settingsViewModel.sliderProgress.collect(viewLifecycleOwner) { + sliderBinding.slider.value = it.toFloat() + } + } + } + } + + override fun onClick(dialog: DialogInterface, which: Int) { + when (settingsViewModel.clickedItem) { + is SingleChoiceSetting -> { + val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting + val value = getValueForSingleChoiceSelection(scSetting, which) + scSetting.setSelectedValue(value) + } + + is StringSingleChoiceSetting -> { + val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting + val value = scSetting.getValueAt(which) + scSetting.setSelectedValue(value) + } + + is IntSingleChoiceSetting -> { + val scSetting = settingsViewModel.clickedItem as IntSingleChoiceSetting + val value = scSetting.getValueAt(which) + scSetting.setSelectedValue(value) + } + + is SliderSetting -> { + val sliderSetting = settingsViewModel.clickedItem as SliderSetting + sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value) + } + + is StringInputSetting -> { + val stringInputSetting = settingsViewModel.clickedItem as StringInputSetting + stringInputSetting.setSelectedValue( + (stringInputBinding.editText.text ?: "").toString() + ) + } + } + closeDialog() + } + + private fun closeDialog() { + settingsViewModel.setAdapterItemChanged(position) + settingsViewModel.clickedItem = null + settingsViewModel.setSliderProgress(-1f) + dismiss() + } + + private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int { + val valuesId = item.valuesId + return if (valuesId > 0) { + val valuesArray = requireContext().resources.getIntArray(valuesId) + valuesArray[which] + } else { + which + } + } + + private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int { + val value = item.getSelectedValue() + val valuesId = item.valuesId + if (valuesId > 0) { + val valuesArray = requireContext().resources.getIntArray(valuesId) + for (index in valuesArray.indices) { + val current = valuesArray[index] + if (current == value) { + return index + } + } + } else { + return value + } + return -1 + } + + companion object { + const val TAG = "SettingsDialogFragment" + + const val TYPE_RESET_SETTING = -1 + + const val TITLE = "Title" + const val TYPE = "Type" + const val POSITION = "Position" + + fun newInstance( + settingsViewModel: SettingsViewModel, + clickedItem: SettingsItem, + type: Int, + position: Int + ): SettingsDialogFragment { + when (type) { + SettingsItem.TYPE_HEADER, + SettingsItem.TYPE_SWITCH, + SettingsItem.TYPE_SUBMENU, + SettingsItem.TYPE_DATETIME_SETTING, + SettingsItem.TYPE_RUNNABLE -> + throw IllegalArgumentException("[SettingsDialogFragment] Incompatible type!") + + SettingsItem.TYPE_SLIDER -> settingsViewModel.setSliderProgress( + (clickedItem as SliderSetting).getSelectedValue().toFloat() + ) + } + settingsViewModel.clickedItem = clickedItem + + val args = Bundle() + args.putInt(TYPE, type) + args.putInt(POSITION, position) + val fragment = SettingsDialogFragment() + fragment.arguments = args + return fragment + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt index 6f6e7be10..ec16f16c4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt @@ -13,20 +13,17 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.transition.MaterialSharedAxis -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding +import org.yuzu.yuzu_emu.features.input.NativeInput import org.yuzu.yuzu_emu.features.settings.model.Settings -import org.yuzu.yuzu_emu.model.SettingsViewModel +import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins +import org.yuzu.yuzu_emu.utils.collect class SettingsFragment : Fragment() { private lateinit var presenter: SettingsFragmentPresenter @@ -45,6 +42,12 @@ class SettingsFragment : Fragment() { returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) + + val playerIndex = getPlayerIndex() + if (playerIndex != -1) { + NativeInput.loadInputProfiles() + NativeInput.reloadInputDevices() + } } override fun onCreateView( @@ -56,9 +59,9 @@ class SettingsFragment : Fragment() { return binding.root } - // This is using the correct scope, lint is just acting up - @SuppressLint("UnsafeRepeatOnLifecycleDetector") + @SuppressLint("NotifyDataSetChanged") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) settingsAdapter = SettingsAdapter(this, requireContext()) presenter = SettingsFragmentPresenter( settingsViewModel, @@ -71,7 +74,17 @@ class SettingsFragment : Fragment() { ) { args.game!!.title } else { - getString(args.menuTag.titleId) + when (args.menuTag) { + Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> Settings.getPlayerString(1) + Settings.MenuTag.SECTION_INPUT_PLAYER_TWO -> Settings.getPlayerString(2) + Settings.MenuTag.SECTION_INPUT_PLAYER_THREE -> Settings.getPlayerString(3) + Settings.MenuTag.SECTION_INPUT_PLAYER_FOUR -> Settings.getPlayerString(4) + Settings.MenuTag.SECTION_INPUT_PLAYER_FIVE -> Settings.getPlayerString(5) + Settings.MenuTag.SECTION_INPUT_PLAYER_SIX -> Settings.getPlayerString(6) + Settings.MenuTag.SECTION_INPUT_PLAYER_SEVEN -> Settings.getPlayerString(7) + Settings.MenuTag.SECTION_INPUT_PLAYER_EIGHT -> Settings.getPlayerString(8) + else -> getString(args.menuTag.titleId) + } } binding.listSettings.apply { adapter = settingsAdapter @@ -82,16 +95,37 @@ class SettingsFragment : Fragment() { settingsViewModel.setShouldNavigateBack(true) } - viewLifecycleOwner.lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - settingsViewModel.shouldReloadSettingsList.collectLatest { - if (it) { - settingsViewModel.setShouldReloadSettingsList(false) - presenter.loadSettingsList() - } - } - } + settingsViewModel.shouldReloadSettingsList.collect( + viewLifecycleOwner, + resetState = { settingsViewModel.setShouldReloadSettingsList(false) } + ) { if (it) presenter.loadSettingsList() } + settingsViewModel.adapterItemChanged.collect( + viewLifecycleOwner, + resetState = { settingsViewModel.setAdapterItemChanged(-1) } + ) { if (it != -1) settingsAdapter?.notifyItemChanged(it) } + settingsViewModel.datasetChanged.collect( + viewLifecycleOwner, + resetState = { settingsViewModel.setDatasetChanged(false) } + ) { if (it) settingsAdapter?.notifyDataSetChanged() } + settingsViewModel.reloadListAndNotifyDataset.collect( + viewLifecycleOwner, + resetState = { settingsViewModel.setReloadListAndNotifyDataset(false) } + ) { if (it) presenter.loadSettingsList(true) } + settingsViewModel.shouldShowResetInputDialog.collect( + viewLifecycleOwner, + resetState = { settingsViewModel.setShouldShowResetInputDialog(false) } + ) { + if (it) { + MessageDialogFragment.newInstance( + activity = requireActivity(), + titleId = R.string.reset_mapping, + descriptionId = R.string.reset_mapping_description, + positiveAction = { + NativeInput.resetControllerMappings(getPlayerIndex()) + settingsViewModel.setReloadListAndNotifyDataset(true) + }, + negativeAction = {} + ).show(parentFragmentManager, MessageDialogFragment.TAG) } } @@ -115,6 +149,19 @@ class SettingsFragment : Fragment() { setInsets() } + private fun getPlayerIndex(): Int = + when (args.menuTag) { + Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> 0 + Settings.MenuTag.SECTION_INPUT_PLAYER_TWO -> 1 + Settings.MenuTag.SECTION_INPUT_PLAYER_THREE -> 2 + Settings.MenuTag.SECTION_INPUT_PLAYER_FOUR -> 3 + Settings.MenuTag.SECTION_INPUT_PLAYER_FIVE -> 4 + Settings.MenuTag.SECTION_INPUT_PLAYER_SIX -> 5 + Settings.MenuTag.SECTION_INPUT_PLAYER_SEVEN -> 6 + Settings.MenuTag.SECTION_INPUT_PLAYER_EIGHT -> 7 + else -> -1 + } + private fun setInsets() { ViewCompat.setOnApplyWindowInsetsListener( binding.root 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..6907bec02 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 @@ -3,11 +3,17 @@ package org.yuzu.yuzu_emu.features.settings.ui +import android.annotation.SuppressLint import android.os.Build import android.widget.Toast import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.features.input.model.AnalogDirection +import org.yuzu.yuzu_emu.features.input.model.NativeAnalog +import org.yuzu.yuzu_emu.features.input.model.NativeButton +import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting @@ -15,18 +21,22 @@ import org.yuzu.yuzu_emu.features.settings.model.ByteSetting import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.LongSetting import org.yuzu.yuzu_emu.features.settings.model.Settings +import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag import org.yuzu.yuzu_emu.features.settings.model.ShortSetting +import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.features.settings.model.view.* -import org.yuzu.yuzu_emu.model.SettingsViewModel +import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.NativeConfig class SettingsFragmentPresenter( private val settingsViewModel: SettingsViewModel, private val adapter: SettingsAdapter, - private var menuTag: Settings.MenuTag + private var menuTag: MenuTag ) { private var settingsList = ArrayList<SettingsItem>() + private val context get() = YuzuApplication.appContext + // Extension for altering settings list based on each setting's properties fun ArrayList<SettingsItem>.add(key: String) { val item = SettingsItem.settingsItems[key]!! @@ -53,73 +63,90 @@ class SettingsFragmentPresenter( add(item) } + // Allows you to show/hide abstract settings based on the paired setting key + fun ArrayList<SettingsItem>.addAbstract(item: SettingsItem) { + val pairedSettingKey = item.setting.pairedSettingKey + if (pairedSettingKey.isNotEmpty()) { + val pairedSettingsItem = + this.firstOrNull { it.setting.key == pairedSettingKey } ?: return + val pairedSetting = pairedSettingsItem.setting as AbstractBooleanSetting + if (!pairedSetting.getBoolean(!NativeConfig.isPerGameConfigLoaded())) return + } + add(item) + } + fun onViewCreated() { loadSettingsList() } - fun loadSettingsList() { + @SuppressLint("NotifyDataSetChanged") + fun loadSettingsList(notifyDataSetChanged: Boolean = false) { val sl = ArrayList<SettingsItem>() when (menuTag) { - Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl) - Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) - Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) - Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl) - Settings.MenuTag.SECTION_THEME -> addThemeSettings(sl) - Settings.MenuTag.SECTION_DEBUG -> addDebugSettings(sl) - else -> { - val context = YuzuApplication.appContext - Toast.makeText( - context, - context.getString(R.string.unimplemented_menu), - Toast.LENGTH_SHORT - ).show() - return - } + MenuTag.SECTION_ROOT -> addConfigSettings(sl) + MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) + MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) + MenuTag.SECTION_AUDIO -> addAudioSettings(sl) + MenuTag.SECTION_INPUT -> addInputSettings(sl) + MenuTag.SECTION_INPUT_PLAYER_ONE -> addInputPlayer(sl, 0) + MenuTag.SECTION_INPUT_PLAYER_TWO -> addInputPlayer(sl, 1) + MenuTag.SECTION_INPUT_PLAYER_THREE -> addInputPlayer(sl, 2) + MenuTag.SECTION_INPUT_PLAYER_FOUR -> addInputPlayer(sl, 3) + MenuTag.SECTION_INPUT_PLAYER_FIVE -> addInputPlayer(sl, 4) + MenuTag.SECTION_INPUT_PLAYER_SIX -> addInputPlayer(sl, 5) + MenuTag.SECTION_INPUT_PLAYER_SEVEN -> addInputPlayer(sl, 6) + MenuTag.SECTION_INPUT_PLAYER_EIGHT -> addInputPlayer(sl, 7) + MenuTag.SECTION_THEME -> addThemeSettings(sl) + MenuTag.SECTION_DEBUG -> addDebugSettings(sl) } settingsList = sl - adapter.submitList(settingsList) + adapter.submitList(settingsList) { + if (notifyDataSetChanged) { + adapter.notifyDataSetChanged() + } + } } private fun addConfigSettings(sl: ArrayList<SettingsItem>) { sl.apply { add( SubmenuSetting( - R.string.preferences_system, - R.string.preferences_system_description, - R.drawable.ic_system_settings, - Settings.MenuTag.SECTION_SYSTEM + titleId = R.string.preferences_system, + descriptionId = R.string.preferences_system_description, + iconId = R.drawable.ic_system_settings, + menuKey = MenuTag.SECTION_SYSTEM ) ) add( SubmenuSetting( - R.string.preferences_graphics, - R.string.preferences_graphics_description, - R.drawable.ic_graphics, - Settings.MenuTag.SECTION_RENDERER + titleId = R.string.preferences_graphics, + descriptionId = R.string.preferences_graphics_description, + iconId = R.drawable.ic_graphics, + menuKey = MenuTag.SECTION_RENDERER ) ) add( SubmenuSetting( - R.string.preferences_audio, - R.string.preferences_audio_description, - R.drawable.ic_audio, - Settings.MenuTag.SECTION_AUDIO + titleId = R.string.preferences_audio, + descriptionId = R.string.preferences_audio_description, + iconId = R.drawable.ic_audio, + menuKey = MenuTag.SECTION_AUDIO ) ) add( SubmenuSetting( - R.string.preferences_debug, - R.string.preferences_debug_description, - R.drawable.ic_code, - Settings.MenuTag.SECTION_DEBUG + titleId = R.string.preferences_debug, + descriptionId = R.string.preferences_debug_description, + iconId = R.drawable.ic_code, + menuKey = MenuTag.SECTION_DEBUG ) ) add( RunnableSetting( - R.string.reset_to_default, - R.string.reset_to_default_description, - false, - R.drawable.ic_restore + titleId = R.string.reset_to_default, + descriptionId = R.string.reset_to_default_description, + isRunnable = !NativeLibrary.isRunning(), + iconId = R.drawable.ic_restore ) { settingsViewModel.setShouldShowResetSettingsDialog(true) } ) } @@ -127,6 +154,7 @@ class SettingsFragmentPresenter( private fun addSystemSettings(sl: ArrayList<SettingsItem>) { sl.apply { + add(StringSetting.DEVICE_NAME.key) add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key) add(ShortSetting.RENDERER_SPEED_LIMIT.key) add(BooleanSetting.USE_DOCKED_MODE.key) @@ -143,10 +171,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) @@ -162,6 +192,671 @@ class SettingsFragmentPresenter( } } + private fun addInputSettings(sl: ArrayList<SettingsItem>) { + settingsViewModel.currentDevice = 0 + + if (NativeConfig.isPerGameConfigLoaded()) { + NativeInput.loadInputProfiles() + val profiles = NativeInput.getInputProfileNames().toMutableList() + profiles.add(0, "") + val prettyProfiles = profiles.toTypedArray() + prettyProfiles[0] = + context.getString(R.string.use_global_input_configuration) + sl.apply { + for (i in 0 until 8) { + add( + IntSingleChoiceSetting( + getPerGameProfileSetting(profiles, i), + titleString = getPlayerProfileString(i + 1), + choices = prettyProfiles, + values = IntArray(profiles.size) { it }.toTypedArray() + ) + ) + } + } + return + } + + val getConnectedIcon: (Int) -> Int = { playerIndex: Int -> + if (NativeInput.getIsConnected(playerIndex)) { + R.drawable.ic_controller + } else { + R.drawable.ic_controller_disconnected + } + } + + val inputSettings = NativeConfig.getInputSettings(true) + sl.apply { + add( + SubmenuSetting( + titleString = Settings.getPlayerString(1), + descriptionString = inputSettings[0].profileName, + menuKey = MenuTag.SECTION_INPUT_PLAYER_ONE, + iconId = getConnectedIcon(0) + ) + ) + add( + SubmenuSetting( + titleString = Settings.getPlayerString(2), + descriptionString = inputSettings[1].profileName, + menuKey = MenuTag.SECTION_INPUT_PLAYER_TWO, + iconId = getConnectedIcon(1) + ) + ) + add( + SubmenuSetting( + titleString = Settings.getPlayerString(3), + descriptionString = inputSettings[2].profileName, + menuKey = MenuTag.SECTION_INPUT_PLAYER_THREE, + iconId = getConnectedIcon(2) + ) + ) + add( + SubmenuSetting( + titleString = Settings.getPlayerString(4), + descriptionString = inputSettings[3].profileName, + menuKey = MenuTag.SECTION_INPUT_PLAYER_FOUR, + iconId = getConnectedIcon(3) + ) + ) + add( + SubmenuSetting( + titleString = Settings.getPlayerString(5), + descriptionString = inputSettings[4].profileName, + menuKey = MenuTag.SECTION_INPUT_PLAYER_FIVE, + iconId = getConnectedIcon(4) + ) + ) + add( + SubmenuSetting( + titleString = Settings.getPlayerString(6), + descriptionString = inputSettings[5].profileName, + menuKey = MenuTag.SECTION_INPUT_PLAYER_SIX, + iconId = getConnectedIcon(5) + ) + ) + add( + SubmenuSetting( + titleString = Settings.getPlayerString(7), + descriptionString = inputSettings[6].profileName, + menuKey = MenuTag.SECTION_INPUT_PLAYER_SEVEN, + iconId = getConnectedIcon(6) + ) + ) + add( + SubmenuSetting( + titleString = Settings.getPlayerString(8), + descriptionString = inputSettings[7].profileName, + menuKey = MenuTag.SECTION_INPUT_PLAYER_EIGHT, + iconId = getConnectedIcon(7) + ) + ) + } + } + + private fun getPlayerProfileString(player: Int): String = + context.getString(R.string.player_num_profile, player) + + private fun getPerGameProfileSetting( + profiles: List<String>, + playerIndex: Int + ): AbstractIntSetting { + return object : AbstractIntSetting { + private val players + get() = NativeConfig.getInputSettings(false) + + override val key = "" + + override fun getInt(needsGlobal: Boolean): Int { + val currentProfile = players[playerIndex].profileName + profiles.forEachIndexed { i, profile -> + if (profile == currentProfile) { + return i + } + } + return 0 + } + + override fun setInt(value: Int) { + NativeInput.loadPerGameConfiguration(playerIndex, value, profiles[value]) + NativeInput.connectControllers(playerIndex) + NativeConfig.saveControlPlayerValues() + } + + override val defaultValue = 0 + + override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString() + + override fun reset() = setInt(defaultValue) + + override var global = true + + override val isRuntimeModifiable = true + + override val isSaveable = true + } + } + + private fun addInputPlayer(sl: ArrayList<SettingsItem>, playerIndex: Int) { + sl.apply { + val connectedSetting = object : AbstractBooleanSetting { + override val key = "connected" + + override fun getBoolean(needsGlobal: Boolean): Boolean = + NativeInput.getIsConnected(playerIndex) + + override fun setBoolean(value: Boolean) = + NativeInput.connectControllers(playerIndex, value) + + override val defaultValue = playerIndex == 0 + + override fun getValueAsString(needsGlobal: Boolean): String = + getBoolean(needsGlobal).toString() + + override fun reset() = setBoolean(defaultValue) + } + add(SwitchSetting(connectedSetting, R.string.connected)) + + val styleTags = NativeInput.getSupportedStyleTags(playerIndex) + val npadType = object : AbstractIntSetting { + override val key = "npad_type" + override fun getInt(needsGlobal: Boolean): Int { + val styleIndex = NativeInput.getStyleIndex(playerIndex) + return styleTags.indexOfFirst { it == styleIndex } + } + + override fun setInt(value: Int) { + NativeInput.setStyleIndex(playerIndex, styleTags[value]) + settingsViewModel.setReloadListAndNotifyDataset(true) + } + + override val defaultValue = NpadStyleIndex.Fullkey.int + override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString() + override fun reset() = setInt(defaultValue) + override val pairedSettingKey: String = "connected" + } + addAbstract( + IntSingleChoiceSetting( + npadType, + titleId = R.string.controller_type, + choices = styleTags.map { context.getString(it.nameId) } + .toTypedArray(), + values = IntArray(styleTags.size) { it }.toTypedArray() + ) + ) + + InputHandler.updateControllerData() + + val autoMappingSetting = object : AbstractIntSetting { + override val key = "auto_mapping_device" + + override fun getInt(needsGlobal: Boolean): Int = -1 + + override fun setInt(value: Int) { + val registeredController = InputHandler.registeredControllers[value + 1] + val displayName = registeredController.get( + "display", + context.getString(R.string.unknown) + ) + NativeInput.updateMappingsWithDefault( + playerIndex, + registeredController, + displayName + ) + Toast.makeText( + context, + context.getString(R.string.attempted_auto_map, displayName), + Toast.LENGTH_SHORT + ).show() + settingsViewModel.setReloadListAndNotifyDataset(true) + } + + override val defaultValue = -1 + + override fun getValueAsString(needsGlobal: Boolean) = getInt().toString() + + override fun reset() = setInt(defaultValue) + + override val isRuntimeModifiable: Boolean = true + } + + val unknownString = context.getString(R.string.unknown) + val prettyAutoMappingControllerList = InputHandler.registeredControllers.mapNotNull { + val port = it.get("port", -1) + return@mapNotNull if (port == 100 || port == -1) { + null + } else { + it.get("display", unknownString) + } + }.toTypedArray() + add( + IntSingleChoiceSetting( + autoMappingSetting, + titleId = R.string.auto_map, + descriptionId = R.string.auto_map_description, + choices = prettyAutoMappingControllerList, + values = IntArray(prettyAutoMappingControllerList.size) { it }.toTypedArray() + ) + ) + + val mappingFilterSetting = object : AbstractIntSetting { + override val key = "mapping_filter" + + override fun getInt(needsGlobal: Boolean): Int = settingsViewModel.currentDevice + + override fun setInt(value: Int) { + settingsViewModel.currentDevice = value + } + + override val defaultValue = 0 + + override fun getValueAsString(needsGlobal: Boolean) = getInt().toString() + + override fun reset() = setInt(defaultValue) + + override val isRuntimeModifiable: Boolean = true + } + + val prettyControllerList = InputHandler.registeredControllers.mapNotNull { + return@mapNotNull if (it.get("port", 0) == 100) { + null + } else { + it.get("display", unknownString) + } + }.toTypedArray() + add( + IntSingleChoiceSetting( + mappingFilterSetting, + titleId = R.string.input_mapping_filter, + descriptionId = R.string.input_mapping_filter_description, + choices = prettyControllerList, + values = IntArray(prettyControllerList.size) { it }.toTypedArray() + ) + ) + + add(InputProfileSetting(playerIndex)) + add( + RunnableSetting(titleId = R.string.reset_to_default, isRunnable = true) { + settingsViewModel.setShouldShowResetInputDialog(true) + } + ) + + val styleIndex = NativeInput.getStyleIndex(playerIndex) + + // Buttons + when (styleIndex) { + NpadStyleIndex.Fullkey, + NpadStyleIndex.Handheld, + NpadStyleIndex.JoyconDual -> { + add(HeaderSetting(R.string.buttons)) + add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a)) + add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b)) + add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x)) + add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y)) + add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.button_plus)) + add(ButtonInputSetting(playerIndex, NativeButton.Minus, R.string.button_minus)) + add(ButtonInputSetting(playerIndex, NativeButton.Home, R.string.button_home)) + add( + ButtonInputSetting( + playerIndex, + NativeButton.Capture, + R.string.button_capture + ) + ) + } + + NpadStyleIndex.JoyconLeft -> { + add(HeaderSetting(R.string.buttons)) + add(ButtonInputSetting(playerIndex, NativeButton.Minus, R.string.button_minus)) + add( + ButtonInputSetting( + playerIndex, + NativeButton.Capture, + R.string.button_capture + ) + ) + } + + NpadStyleIndex.JoyconRight -> { + add(HeaderSetting(R.string.buttons)) + add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a)) + add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b)) + add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x)) + add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y)) + add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.button_plus)) + add(ButtonInputSetting(playerIndex, NativeButton.Home, R.string.button_home)) + } + + NpadStyleIndex.GameCube -> { + add(HeaderSetting(R.string.buttons)) + add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a)) + add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b)) + add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x)) + add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y)) + add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.start_pause)) + } + + else -> { + // No-op + } + } + + when (styleIndex) { + NpadStyleIndex.Fullkey, + NpadStyleIndex.Handheld, + NpadStyleIndex.JoyconDual, + NpadStyleIndex.JoyconLeft -> { + add(HeaderSetting(R.string.dpad)) + add(ButtonInputSetting(playerIndex, NativeButton.DUp, R.string.up)) + add(ButtonInputSetting(playerIndex, NativeButton.DDown, R.string.down)) + add(ButtonInputSetting(playerIndex, NativeButton.DLeft, R.string.left)) + add(ButtonInputSetting(playerIndex, NativeButton.DRight, R.string.right)) + } + + else -> { + // No-op + } + } + + // Left stick + when (styleIndex) { + NpadStyleIndex.Fullkey, + NpadStyleIndex.Handheld, + NpadStyleIndex.JoyconDual, + NpadStyleIndex.JoyconLeft -> { + add(HeaderSetting(R.string.left_stick)) + addAll(getStickDirections(playerIndex, NativeAnalog.LStick)) + add(ButtonInputSetting(playerIndex, NativeButton.LStick, R.string.pressed)) + addAll(getExtraStickSettings(playerIndex, NativeAnalog.LStick)) + } + + NpadStyleIndex.GameCube -> { + add(HeaderSetting(R.string.control_stick)) + addAll(getStickDirections(playerIndex, NativeAnalog.LStick)) + addAll(getExtraStickSettings(playerIndex, NativeAnalog.LStick)) + } + + else -> { + // No-op + } + } + + // Right stick + when (styleIndex) { + NpadStyleIndex.Fullkey, + NpadStyleIndex.Handheld, + NpadStyleIndex.JoyconDual, + NpadStyleIndex.JoyconRight -> { + add(HeaderSetting(R.string.right_stick)) + addAll(getStickDirections(playerIndex, NativeAnalog.RStick)) + add(ButtonInputSetting(playerIndex, NativeButton.RStick, R.string.pressed)) + addAll(getExtraStickSettings(playerIndex, NativeAnalog.RStick)) + } + + NpadStyleIndex.GameCube -> { + add(HeaderSetting(R.string.c_stick)) + addAll(getStickDirections(playerIndex, NativeAnalog.RStick)) + addAll(getExtraStickSettings(playerIndex, NativeAnalog.RStick)) + } + + else -> { + // No-op + } + } + + // L/R, ZL/ZR, and SL/SR + when (styleIndex) { + NpadStyleIndex.Fullkey, + NpadStyleIndex.Handheld -> { + add(HeaderSetting(R.string.triggers)) + add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l)) + add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r)) + add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl)) + add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr)) + } + + NpadStyleIndex.JoyconDual -> { + add(HeaderSetting(R.string.triggers)) + add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l)) + add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r)) + add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl)) + add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr)) + add( + ButtonInputSetting( + playerIndex, + NativeButton.SLLeft, + R.string.button_sl_left + ) + ) + add( + ButtonInputSetting( + playerIndex, + NativeButton.SRLeft, + R.string.button_sr_left + ) + ) + add( + ButtonInputSetting( + playerIndex, + NativeButton.SLRight, + R.string.button_sl_right + ) + ) + add( + ButtonInputSetting( + playerIndex, + NativeButton.SRRight, + R.string.button_sr_right + ) + ) + } + + NpadStyleIndex.JoyconLeft -> { + add(HeaderSetting(R.string.triggers)) + add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l)) + add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl)) + add( + ButtonInputSetting( + playerIndex, + NativeButton.SLLeft, + R.string.button_sl_left + ) + ) + add( + ButtonInputSetting( + playerIndex, + NativeButton.SRLeft, + R.string.button_sr_left + ) + ) + } + + NpadStyleIndex.JoyconRight -> { + add(HeaderSetting(R.string.triggers)) + add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r)) + add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr)) + add( + ButtonInputSetting( + playerIndex, + NativeButton.SLRight, + R.string.button_sl_right + ) + ) + add( + ButtonInputSetting( + playerIndex, + NativeButton.SRRight, + R.string.button_sr_right + ) + ) + } + + NpadStyleIndex.GameCube -> { + add(HeaderSetting(R.string.triggers)) + add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_z)) + add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_l)) + add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_r)) + } + + else -> { + // No-op + } + } + + add(HeaderSetting(R.string.vibration)) + val vibrationEnabledSetting = object : AbstractBooleanSetting { + override val key = "vibration" + + override fun getBoolean(needsGlobal: Boolean): Boolean = + NativeConfig.getInputSettings(true)[playerIndex].vibrationEnabled + + override fun setBoolean(value: Boolean) { + val settings = NativeConfig.getInputSettings(true) + settings[playerIndex].vibrationEnabled = value + NativeConfig.setInputSettings(settings, true) + } + + override val defaultValue = true + + override fun getValueAsString(needsGlobal: Boolean): String = + getBoolean(needsGlobal).toString() + + override fun reset() = setBoolean(defaultValue) + } + add(SwitchSetting(vibrationEnabledSetting, R.string.vibration)) + + val useSystemVibratorSetting = object : AbstractBooleanSetting { + override val key = "" + + override fun getBoolean(needsGlobal: Boolean): Boolean = + NativeConfig.getInputSettings(true)[playerIndex].useSystemVibrator + + override fun setBoolean(value: Boolean) { + val settings = NativeConfig.getInputSettings(true) + settings[playerIndex].useSystemVibrator = value + NativeConfig.setInputSettings(settings, true) + } + + override val defaultValue = playerIndex == 0 + + override fun getValueAsString(needsGlobal: Boolean): String = + getBoolean(needsGlobal).toString() + + override fun reset() = setBoolean(defaultValue) + + override val pairedSettingKey: String = "vibration" + } + addAbstract(SwitchSetting(useSystemVibratorSetting, R.string.use_system_vibrator)) + + val vibrationStrengthSetting = object : AbstractIntSetting { + override val key = "" + + override fun getInt(needsGlobal: Boolean): Int = + NativeConfig.getInputSettings(true)[playerIndex].vibrationStrength + + override fun setInt(value: Int) { + val settings = NativeConfig.getInputSettings(true) + settings[playerIndex].vibrationStrength = value + NativeConfig.setInputSettings(settings, true) + } + + override val defaultValue = 100 + + override fun getValueAsString(needsGlobal: Boolean): String = + getInt(needsGlobal).toString() + + override fun reset() = setInt(defaultValue) + + override val pairedSettingKey: String = "vibration" + } + addAbstract( + SliderSetting(vibrationStrengthSetting, R.string.vibration_strength, units = "%") + ) + } + } + + // Convenience function for creating AbstractIntSettings for modifier range/stick range/stick deadzones + private fun getStickIntSettingFromParam( + playerIndex: Int, + paramName: String, + stick: NativeAnalog, + defaultValue: Int + ): AbstractIntSetting = + object : AbstractIntSetting { + val params get() = NativeInput.getStickParam(playerIndex, stick) + + override val key = "" + + override fun getInt(needsGlobal: Boolean): Int = + (params.get(paramName, 0.15f) * 100).toInt() + + override fun setInt(value: Int) { + val tempParams = params + tempParams.set(paramName, value.toFloat() / 100) + NativeInput.setStickParam(playerIndex, stick, tempParams) + } + + override val defaultValue = defaultValue + + override fun getValueAsString(needsGlobal: Boolean): String = + getInt(needsGlobal).toString() + + override fun reset() = setInt(defaultValue) + } + + private fun getExtraStickSettings( + playerIndex: Int, + nativeAnalog: NativeAnalog + ): List<SettingsItem> { + val stickIsController = + NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog)) + val modifierRangeSetting = + getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 50) + val stickRangeSetting = + getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 95) + val stickDeadzoneSetting = + getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 15) + + val out = mutableListOf<SettingsItem>().apply { + if (stickIsController) { + add(SliderSetting(stickRangeSetting, titleId = R.string.range, min = 25, max = 150)) + add(SliderSetting(stickDeadzoneSetting, R.string.deadzone)) + } else { + add(ModifierInputSetting(playerIndex, NativeAnalog.LStick, R.string.modifier)) + add(SliderSetting(modifierRangeSetting, R.string.modifier_range)) + } + } + return out + } + + private fun getStickDirections(player: Int, stick: NativeAnalog): List<AnalogInputSetting> = + listOf( + AnalogInputSetting( + player, + stick, + AnalogDirection.Up, + R.string.up + ), + AnalogInputSetting( + player, + stick, + AnalogDirection.Down, + R.string.down + ), + AnalogInputSetting( + player, + stick, + AnalogDirection.Left, + R.string.left + ), + AnalogInputSetting( + player, + stick, + AnalogDirection.Right, + R.string.right + ) + ) + private fun addThemeSettings(sl: ArrayList<SettingsItem>) { sl.apply { val theme: AbstractIntSetting = object : AbstractIntSetting { @@ -184,20 +879,18 @@ class SettingsFragmentPresenter( add( SingleChoiceSetting( theme, - R.string.change_app_theme, - 0, - R.array.themeEntriesA12, - R.array.themeValuesA12 + titleId = R.string.change_app_theme, + choicesId = R.array.themeEntriesA12, + valuesId = R.array.themeValuesA12 ) ) } else { add( SingleChoiceSetting( theme, - R.string.change_app_theme, - 0, - R.array.themeEntries, - R.array.themeValues + titleId = R.string.change_app_theme, + choicesId = R.array.themeEntries, + valuesId = R.array.themeValues ) ) } @@ -226,10 +919,9 @@ class SettingsFragmentPresenter( add( SingleChoiceSetting( themeMode, - R.string.change_theme_mode, - 0, - R.array.themeModeEntries, - R.array.themeModeValues + titleId = R.string.change_theme_mode, + choicesId = R.array.themeModeEntries, + valuesId = R.array.themeModeValues ) ) @@ -260,8 +952,8 @@ class SettingsFragmentPresenter( add( SwitchSetting( blackBackgrounds, - R.string.use_black_backgrounds, - R.string.use_black_backgrounds_description + titleId = R.string.use_black_backgrounds, + descriptionId = R.string.use_black_backgrounds_description ) ) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt new file mode 100644 index 000000000..ed60cf34f --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt @@ -0,0 +1,183 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.InputMethodManager +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding +import androidx.core.widget.doOnTextChanged +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.divider.MaterialDividerItemDecoration +import com.google.android.material.transition.MaterialSharedAxis +import info.debatty.java.stringsimilarity.Cosine +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding +import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem +import org.yuzu.yuzu_emu.utils.NativeConfig +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible +import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins +import org.yuzu.yuzu_emu.utils.collect + +class SettingsSearchFragment : Fragment() { + private var _binding: FragmentSettingsSearchBinding? = null + private val binding get() = _binding!! + + private var settingsAdapter: SettingsAdapter? = null + + private val settingsViewModel: SettingsViewModel by activityViewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) + returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) + reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) + exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentSettingsSearchBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + if (savedInstanceState != null) { + binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) + } + + settingsAdapter = SettingsAdapter(this, requireContext()) + + val dividerDecoration = MaterialDividerItemDecoration( + requireContext(), + LinearLayoutManager.VERTICAL + ) + dividerDecoration.isLastItemDecorated = false + binding.settingsList.apply { + adapter = settingsAdapter + layoutManager = LinearLayoutManager(requireContext()) + addItemDecoration(dividerDecoration) + } + + focusSearch() + + binding.backButton.setOnClickListener { settingsViewModel.setShouldNavigateBack(true) } + binding.searchBackground.setOnClickListener { focusSearch() } + binding.clearButton.setOnClickListener { binding.searchText.setText("") } + binding.searchText.doOnTextChanged { _, _, _, _ -> + search() + binding.settingsList.smoothScrollToPosition(0) + } + settingsViewModel.shouldReloadSettingsList.collect(viewLifecycleOwner) { + if (it) { + settingsViewModel.setShouldReloadSettingsList(false) + search() + } + } + + search() + + setInsets() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString(SEARCH_TEXT, binding.searchText.text.toString()) + } + + private fun search() { + val searchTerm = binding.searchText.text.toString().lowercase() + binding.clearButton.setVisible(visible = searchTerm.isNotEmpty(), gone = false) + if (searchTerm.isEmpty()) { + binding.noResultsView.setVisible(visible = false, gone = false) + settingsAdapter?.submitList(emptyList()) + return + } + + val baseList = SettingsItem.settingsItems + val similarityAlgorithm = if (searchTerm.length > 2) Cosine() else Cosine(1) + val sortedList: List<SettingsItem> = baseList.mapNotNull { item -> + val title = item.value.title.lowercase() + val similarity = similarityAlgorithm.similarity(searchTerm, title) + if (similarity > 0.08) { + Pair(similarity, item) + } else { + null + } + }.sortedByDescending { it.first }.mapNotNull { + val item = it.second.value + val pairedSettingKey = item.setting.pairedSettingKey + val optionalSetting: SettingsItem? = if (pairedSettingKey.isNotEmpty()) { + val pairedSettingValue = NativeConfig.getBoolean(pairedSettingKey, false) + if (pairedSettingValue) it.second.value else null + } else { + it.second.value + } + optionalSetting + } + settingsAdapter?.submitList(sortedList) + binding.noResultsView.setVisible(visible = sortedList.isEmpty(), gone = false) + } + + private fun focusSearch() { + binding.searchText.requestFocus() + val imm = requireActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager? + imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT) + } + + private fun setInsets() = + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { _: View, windowInsets: WindowInsetsCompat -> + val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med) + val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge) + val topMargin = resources.getDimensionPixelSize(R.dimen.spacing_chip) + + val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) + + val leftInsets = barInsets.left + cutoutInsets.left + val rightInsets = barInsets.right + cutoutInsets.right + + binding.settingsList.updatePadding(bottom = barInsets.bottom + extraListSpacing) + binding.frameSearch.updatePadding( + left = leftInsets + sideMargin, + top = barInsets.top + topMargin, + right = rightInsets + sideMargin + ) + binding.noResultsView.updatePadding( + left = leftInsets, + right = rightInsets, + bottom = barInsets.bottom + ) + + binding.settingsList.updateMargins( + left = leftInsets + sideMargin, + right = rightInsets + sideMargin + ) + binding.divider.updateMargins( + left = leftInsets + sideMargin, + right = rightInsets + sideMargin + ) + + windowInsets + } + + companion object { + const val SEARCH_TEXT = "SearchText" + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt new file mode 100644 index 000000000..fbdca04e9 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication +import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem +import org.yuzu.yuzu_emu.model.Game +import org.yuzu.yuzu_emu.utils.InputHandler +import org.yuzu.yuzu_emu.utils.ParamPackage + +class SettingsViewModel : ViewModel() { + var game: Game? = null + + var clickedItem: SettingsItem? = null + + var currentDevice = 0 + + val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate + private val _shouldRecreate = MutableStateFlow(false) + + val shouldNavigateBack: StateFlow<Boolean> get() = _shouldNavigateBack + private val _shouldNavigateBack = MutableStateFlow(false) + + val shouldShowResetSettingsDialog: StateFlow<Boolean> get() = _shouldShowResetSettingsDialog + private val _shouldShowResetSettingsDialog = MutableStateFlow(false) + + val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList + private val _shouldReloadSettingsList = MutableStateFlow(false) + + val sliderProgress: StateFlow<Int> get() = _sliderProgress + private val _sliderProgress = MutableStateFlow(-1) + + val sliderTextValue: StateFlow<String> get() = _sliderTextValue + private val _sliderTextValue = MutableStateFlow("") + + val adapterItemChanged: StateFlow<Int> get() = _adapterItemChanged + private val _adapterItemChanged = MutableStateFlow(-1) + + private val _datasetChanged = MutableStateFlow(false) + val datasetChanged = _datasetChanged.asStateFlow() + + private val _reloadListAndNotifyDataset = MutableStateFlow(false) + val reloadListAndNotifyDataset = _reloadListAndNotifyDataset.asStateFlow() + + private val _shouldShowDeleteProfileDialog = MutableStateFlow("") + val shouldShowDeleteProfileDialog = _shouldShowDeleteProfileDialog.asStateFlow() + + private val _shouldShowResetInputDialog = MutableStateFlow(false) + val shouldShowResetInputDialog = _shouldShowResetInputDialog.asStateFlow() + + fun setShouldRecreate(value: Boolean) { + _shouldRecreate.value = value + } + + fun setShouldNavigateBack(value: Boolean) { + _shouldNavigateBack.value = value + } + + fun setShouldShowResetSettingsDialog(value: Boolean) { + _shouldShowResetSettingsDialog.value = value + } + + fun setShouldReloadSettingsList(value: Boolean) { + _shouldReloadSettingsList.value = value + } + + fun setSliderTextValue(value: Float, units: String) { + _sliderProgress.value = value.toInt() + _sliderTextValue.value = String.format( + YuzuApplication.appContext.getString(R.string.value_with_units), + value.toInt().toString(), + units + ) + } + + fun setSliderProgress(value: Float) { + _sliderProgress.value = value.toInt() + } + + fun setAdapterItemChanged(value: Int) { + _adapterItemChanged.value = value + } + + fun setDatasetChanged(value: Boolean) { + _datasetChanged.value = value + } + + fun setReloadListAndNotifyDataset(value: Boolean) { + _reloadListAndNotifyDataset.value = value + } + + fun setShouldShowDeleteProfileDialog(profile: String) { + _shouldShowDeleteProfileDialog.value = profile + } + + fun setShouldShowResetInputDialog(value: Boolean) { + _shouldShowResetInputDialog.value = value + } + + fun getCurrentDeviceParams(defaultParams: ParamPackage): ParamPackage = + try { + InputHandler.registeredControllers[currentDevice] + } catch (e: IndexOutOfBoundsException) { + defaultParams + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt index 5ad0899dd..0309fad59 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt @@ -13,7 +13,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter -import org.yuzu.yuzu_emu.utils.NativeConfig +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : SettingViewHolder(binding.root, adapter) { @@ -21,28 +21,17 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA override fun bind(item: SettingsItem) { setting = item as DateTimeSetting - binding.textSettingName.setText(item.nameId) - if (item.descriptionId != 0) { - binding.textSettingDescription.setText(item.descriptionId) - binding.textSettingDescription.visibility = View.VISIBLE - } else { - binding.textSettingDescription.visibility = View.GONE - } - - binding.textSettingValue.visibility = View.VISIBLE + binding.textSettingName.text = item.title + binding.textSettingDescription.setVisible(item.description.isNotEmpty()) + binding.textSettingDescription.text = item.description + binding.textSettingValue.setVisible(true) val epochTime = setting.getValue() val instant = Instant.ofEpochMilli(epochTime * 1000) val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC")) val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) binding.textSettingValue.text = dateFormatter.format(zonedTime) - binding.buttonClear.visibility = if (setting.setting.global || - !NativeConfig.isPerGameConfigLoaded() - ) { - View.GONE - } else { - View.VISIBLE - } + binding.buttonClear.setVisible(setting.clearable) binding.buttonClear.setOnClickListener { adapter.onClearClick(setting, bindingAdapterPosition) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt index f5bcf705c..0815c36e2 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt @@ -16,7 +16,7 @@ class HeaderViewHolder(val binding: ListItemSettingsHeaderBinding, adapter: Sett } override fun bind(item: SettingsItem) { - binding.textHeaderName.setText(item.nameId) + binding.textHeaderName.text = item.title } override fun onClick(clicked: View) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt new file mode 100644 index 000000000..86af3a226 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui.viewholder + +import android.view.View +import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding +import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting +import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem +import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter +import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible + +class InputProfileViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : + SettingViewHolder(binding.root, adapter) { + private lateinit var setting: InputProfileSetting + + override fun bind(item: SettingsItem) { + setting = item as InputProfileSetting + binding.textSettingName.text = setting.title + binding.textSettingValue.text = + setting.getCurrentProfile().ifEmpty { binding.root.context.getString(R.string.not_set) } + + binding.textSettingDescription.setVisible(false) + binding.buttonClear.setVisible(false) + binding.icon.setVisible(false) + binding.buttonClear.setVisible(false) + } + + override fun onClick(clicked: View) = + adapter.onInputProfileClick(setting, bindingAdapterPosition) + + override fun onLongClick(clicked: View): Boolean = false +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt new file mode 100644 index 000000000..9d9047804 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui.viewholder + +import android.view.View +import org.yuzu.yuzu_emu.databinding.ListItemSettingInputBinding +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.features.settings.model.view.AnalogInputSetting +import org.yuzu.yuzu_emu.features.settings.model.view.ButtonInputSetting +import org.yuzu.yuzu_emu.features.settings.model.view.InputSetting +import org.yuzu.yuzu_emu.features.settings.model.view.ModifierInputSetting +import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem +import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible + +class InputViewHolder(val binding: ListItemSettingInputBinding, adapter: SettingsAdapter) : + SettingViewHolder(binding.root, adapter) { + private lateinit var setting: InputSetting + + override fun bind(item: SettingsItem) { + setting = item as InputSetting + binding.textSettingName.text = setting.title + binding.textSettingValue.text = setting.getSelectedValue() + + when (item) { + is AnalogInputSetting -> { + val param = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog) + binding.buttonOptions.setVisible( + param.get("engine", "") == "analog_from_button" || + param.has("axis_x") || param.has("axis_y") + ) + } + + is ButtonInputSetting -> { + val param = NativeInput.getButtonParam(item.playerIndex, item.nativeButton) + binding.buttonOptions.setVisible( + param.has("code") || param.has("button") || param.has("hat") || + param.has("axis") + ) + } + + is ModifierInputSetting -> { + val params = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog) + binding.buttonOptions.setVisible(params.has("modifier")) + } + } + + binding.buttonOptions.setOnClickListener(null) + binding.buttonOptions.setOnClickListener { + adapter.onInputOptionsClick(binding.buttonOptions, setting, bindingAdapterPosition) + } + } + + override fun onClick(clicked: View) = + adapter.onInputClick(setting, bindingAdapterPosition) + + override fun onLongClick(clicked: View): Boolean = + adapter.onLongClick(setting, bindingAdapterPosition) +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt index 507184238..fc2ffb515 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt @@ -5,11 +5,11 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder import android.view.View import androidx.core.content.res.ResourcesCompat -import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : SettingViewHolder(binding.root, adapter) { @@ -17,34 +17,28 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA override fun bind(item: SettingsItem) { setting = item as RunnableSetting - if (item.iconId != 0) { - binding.icon.visibility = View.VISIBLE + binding.icon.setVisible(setting.iconId != 0) + if (setting.iconId != 0) { binding.icon.setImageDrawable( ResourcesCompat.getDrawable( binding.icon.resources, - item.iconId, + setting.iconId, binding.icon.context.theme ) ) - } else { - binding.icon.visibility = View.GONE } - binding.textSettingName.setText(item.nameId) - if (item.descriptionId != 0) { - binding.textSettingDescription.setText(item.descriptionId) - binding.textSettingDescription.visibility = View.VISIBLE - } else { - binding.textSettingDescription.visibility = View.GONE - } - binding.textSettingValue.visibility = View.GONE - binding.buttonClear.visibility = View.GONE + binding.textSettingName.text = setting.title + binding.textSettingDescription.setVisible(setting.description.isNotEmpty()) + binding.textSettingDescription.text = item.description + binding.textSettingValue.setVisible(false) + binding.buttonClear.setVisible(false) setStyle(setting.isEditable, binding) } override fun onClick(clicked: View) { - if (!setting.isRuntimeRunnable && !NativeLibrary.isRunning()) { + if (setting.isRunnable) { setting.runnable.invoke() } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt index 02dab3785..489f55455 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt @@ -5,11 +5,12 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder import android.view.View import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding +import org.yuzu.yuzu_emu.features.settings.model.view.IntSingleChoiceSetting import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter -import org.yuzu.yuzu_emu.utils.NativeConfig +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : SettingViewHolder(binding.root, adapter) { @@ -17,40 +18,36 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti override fun bind(item: SettingsItem) { setting = item - binding.textSettingName.setText(item.nameId) - if (item.descriptionId != 0) { - binding.textSettingDescription.setText(item.descriptionId) - binding.textSettingDescription.visibility = View.VISIBLE - } else { - binding.textSettingDescription.visibility = View.GONE - } + binding.textSettingName.text = setting.title + binding.textSettingDescription.setVisible(item.description.isNotEmpty()) + binding.textSettingDescription.text = item.description - binding.textSettingValue.visibility = View.VISIBLE - if (item is SingleChoiceSetting) { - val resMgr = binding.textSettingValue.context.resources - val values = resMgr.getIntArray(item.valuesId) - for (i in values.indices) { - if (values[i] == item.getSelectedValue()) { - binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i] - break + binding.textSettingValue.setVisible(true) + when (item) { + is SingleChoiceSetting -> { + val resMgr = binding.textSettingValue.context.resources + val values = resMgr.getIntArray(item.valuesId) + for (i in values.indices) { + if (values[i] == item.getSelectedValue()) { + binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i] + break + } } } - } else if (item is StringSingleChoiceSetting) { - for (i in item.values.indices) { - if (item.values[i] == item.getSelectedValue()) { - binding.textSettingValue.text = item.choices[i] - break - } + + is StringSingleChoiceSetting -> { + binding.textSettingValue.text = item.getSelectedValue() } - } - binding.buttonClear.visibility = if (setting.setting.global || - !NativeConfig.isPerGameConfigLoaded() - ) { - View.GONE - } else { - View.VISIBLE + is IntSingleChoiceSetting -> { + binding.textSettingValue.text = item.getChoiceAt(item.getSelectedValue()) + } + } + if (binding.textSettingValue.text.isEmpty()) { + binding.textSettingValue.setVisible(false) } + + binding.buttonClear.setVisible(setting.clearable) binding.buttonClear.setOnClickListener { adapter.onClearClick(setting, bindingAdapterPosition) } @@ -63,16 +60,25 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti return } - if (setting is SingleChoiceSetting) { - adapter.onSingleChoiceClick( - (setting as SingleChoiceSetting), - bindingAdapterPosition - ) - } else if (setting is StringSingleChoiceSetting) { - adapter.onStringSingleChoiceClick( - (setting as StringSingleChoiceSetting), + when (setting) { + is SingleChoiceSetting -> adapter.onSingleChoiceClick( + setting as SingleChoiceSetting, bindingAdapterPosition ) + + is StringSingleChoiceSetting -> { + adapter.onStringSingleChoiceClick( + setting as StringSingleChoiceSetting, + bindingAdapterPosition + ) + } + + is IntSingleChoiceSetting -> { + adapter.onIntSingleChoiceClick( + setting as IntSingleChoiceSetting, + bindingAdapterPosition + ) + } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt index 596c18012..90a7138cb 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt @@ -9,7 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter -import org.yuzu.yuzu_emu.utils.NativeConfig +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : SettingViewHolder(binding.root, adapter) { @@ -17,27 +17,17 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda override fun bind(item: SettingsItem) { setting = item as SliderSetting - binding.textSettingName.setText(item.nameId) - if (item.descriptionId != 0) { - binding.textSettingDescription.setText(item.descriptionId) - binding.textSettingDescription.visibility = View.VISIBLE - } else { - binding.textSettingDescription.visibility = View.GONE - } - binding.textSettingValue.visibility = View.VISIBLE + binding.textSettingName.text = setting.title + binding.textSettingDescription.setVisible(item.description.isNotEmpty()) + binding.textSettingDescription.text = setting.description + binding.textSettingValue.setVisible(true) binding.textSettingValue.text = String.format( binding.textSettingValue.context.getString(R.string.value_with_units), setting.getSelectedValue(), setting.units ) - binding.buttonClear.visibility = if (setting.setting.global || - !NativeConfig.isPerGameConfigLoaded() - ) { - View.GONE - } else { - View.VISIBLE - } + binding.buttonClear.setVisible(setting.clearable) binding.buttonClear.setOnClickListener { adapter.onClearClick(setting, bindingAdapterPosition) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/StringInputViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/StringInputViewHolder.kt new file mode 100644 index 000000000..a4fd36f62 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/StringInputViewHolder.kt @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.features.settings.ui.viewholder + +import android.view.View +import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding +import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem +import org.yuzu.yuzu_emu.features.settings.model.view.StringInputSetting +import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible + +class StringInputViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : + SettingViewHolder(binding.root, adapter) { + private lateinit var setting: StringInputSetting + + override fun bind(item: SettingsItem) { + setting = item as StringInputSetting + binding.textSettingName.text = setting.title + binding.textSettingDescription.setVisible(setting.description.isNotEmpty()) + binding.textSettingDescription.text = setting.description + binding.textSettingValue.setVisible(true) + binding.textSettingValue.text = setting.getSelectedValue() + + binding.buttonClear.setVisible(setting.clearable) + binding.buttonClear.setOnClickListener { + adapter.onClearClick(setting, bindingAdapterPosition) + } + + setStyle(setting.isEditable, binding) + } + + override fun onClick(clicked: View) { + if (setting.isEditable) { + adapter.onStringInputClick(setting, bindingAdapterPosition) + } + } + + override fun onLongClick(clicked: View): Boolean { + if (setting.isEditable) { + return adapter.onLongClick(setting, bindingAdapterPosition) + } + return false + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt index 20d35a17d..f7a9c08c3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt @@ -9,39 +9,34 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : SettingViewHolder(binding.root, adapter) { - private lateinit var item: SubmenuSetting + private lateinit var setting: SubmenuSetting override fun bind(item: SettingsItem) { - this.item = item as SubmenuSetting - if (item.iconId != 0) { - binding.icon.visibility = View.VISIBLE + setting = item as SubmenuSetting + binding.icon.setVisible(setting.iconId != 0) + if (setting.iconId != 0) { binding.icon.setImageDrawable( ResourcesCompat.getDrawable( binding.icon.resources, - item.iconId, + setting.iconId, binding.icon.context.theme ) ) - } else { - binding.icon.visibility = View.GONE } - binding.textSettingName.setText(item.nameId) - if (item.descriptionId != 0) { - binding.textSettingDescription.setText(item.descriptionId) - binding.textSettingDescription.visibility = View.VISIBLE - } else { - binding.textSettingDescription.visibility = View.GONE - } - binding.textSettingValue.visibility = View.GONE - binding.buttonClear.visibility = View.GONE + binding.textSettingName.text = setting.title + binding.textSettingDescription.setVisible(setting.description.isNotEmpty()) + binding.textSettingDescription.text = setting.description + binding.textSettingValue.setVisible(false) + binding.buttonClear.setVisible(false) } override fun onClick(clicked: View) { - adapter.onSubmenuClick(item) + adapter.onSubmenuClick(setting) } override fun onLongClick(clicked: View): Boolean { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt index d26bf9374..e5763264a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt @@ -9,7 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter -import org.yuzu.yuzu_emu.utils.NativeConfig +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) : SettingViewHolder(binding.root, adapter) { @@ -18,28 +18,17 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter override fun bind(item: SettingsItem) { setting = item as SwitchSetting - binding.textSettingName.setText(item.nameId) - if (item.descriptionId != 0) { - binding.textSettingDescription.setText(item.descriptionId) - binding.textSettingDescription.visibility = View.VISIBLE - } else { - binding.textSettingDescription.text = "" - binding.textSettingDescription.visibility = View.GONE - } + binding.textSettingName.text = setting.title + binding.textSettingDescription.setVisible(setting.description.isNotEmpty()) + binding.textSettingDescription.text = setting.description binding.switchWidget.setOnCheckedChangeListener(null) binding.switchWidget.isChecked = setting.getIsChecked(setting.needsRuntimeGlobal) binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> - adapter.onBooleanClick(item, binding.switchWidget.isChecked, bindingAdapterPosition) + adapter.onBooleanClick(setting, binding.switchWidget.isChecked, bindingAdapterPosition) } - binding.buttonClear.visibility = if (setting.setting.global || - !NativeConfig.isPerGameConfigLoaded() - ) { - View.GONE - } else { - View.VISIBLE - } + binding.buttonClear.setVisible(setting.clearable) binding.buttonClear.setOnClickListener { adapter.onClearClick(setting, bindingAdapterPosition) } 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..110aa2960 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 @@ -3,7 +3,6 @@ package org.yuzu.yuzu_emu.fragments -import android.annotation.SuppressLint import android.content.Intent import android.os.Bundle import android.view.LayoutInflater @@ -16,9 +15,6 @@ import androidx.core.view.updatePadding import androidx.documentfile.provider.DocumentFile import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager @@ -32,6 +28,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.utils.AddonUtil import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins +import org.yuzu.yuzu_emu.utils.collect import java.io.File class AddonsFragment : Fragment() { @@ -60,8 +57,6 @@ class AddonsFragment : Fragment() { return binding.root } - // This is using the correct scope, lint is just acting up - @SuppressLint("UnsafeRepeatOnLifecycleDetector") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setNavigationVisibility(visible = false, animated = false) @@ -78,53 +73,41 @@ class AddonsFragment : Fragment() { adapter = AddonAdapter(addonViewModel) } - viewLifecycleOwner.lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - addonViewModel.addonList.collect { - (binding.listAddons.adapter as AddonAdapter).submitList(it) - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - addonViewModel.showModInstallPicker.collect { - if (it) { - installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) - addonViewModel.showModInstallPicker(false) - } - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - addonViewModel.showModNoticeDialog.collect { - if (it) { - MessageDialogFragment.newInstance( - requireActivity(), - titleId = R.string.addon_notice, - descriptionId = R.string.addon_notice_description, - positiveAction = { addonViewModel.showModInstallPicker(true) } - ).show(parentFragmentManager, MessageDialogFragment.TAG) - addonViewModel.showModNoticeDialog(false) - } - } - } + addonViewModel.addonList.collect(viewLifecycleOwner) { + (binding.listAddons.adapter as AddonAdapter).submitList(it) + } + addonViewModel.showModInstallPicker.collect( + viewLifecycleOwner, + resetState = { addonViewModel.showModInstallPicker(false) } + ) { if (it) installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) } + addonViewModel.showModNoticeDialog.collect( + viewLifecycleOwner, + resetState = { addonViewModel.showModNoticeDialog(false) } + ) { + if (it) { + MessageDialogFragment.newInstance( + requireActivity(), + titleId = R.string.addon_notice, + descriptionId = R.string.addon_notice_description, + dismissible = false, + positiveAction = { addonViewModel.showModInstallPicker(true) }, + negativeAction = {}, + negativeButtonTitleId = R.string.close + ).show(parentFragmentManager, MessageDialogFragment.TAG) } - launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - addonViewModel.addonToDelete.collect { - if (it != null) { - MessageDialogFragment.newInstance( - requireActivity(), - titleId = R.string.confirm_uninstall, - descriptionId = R.string.confirm_uninstall_description, - positiveAction = { addonViewModel.onDeleteAddon(it) } - ).show(parentFragmentManager, MessageDialogFragment.TAG) - addonViewModel.setAddonToDelete(null) - } - } - } + } + addonViewModel.addonToDelete.collect( + viewLifecycleOwner, + resetState = { addonViewModel.setAddonToDelete(null) } + ) { + if (it != null) { + MessageDialogFragment.newInstance( + requireActivity(), + titleId = R.string.confirm_uninstall, + descriptionId = R.string.confirm_uninstall_description, + positiveAction = { addonViewModel.onDeleteAddon(it) }, + negativeAction = {} + ).show(parentFragmentManager, MessageDialogFragment.TAG) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CoreErrorDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CoreErrorDialogFragment.kt new file mode 100644 index 000000000..299f8da71 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CoreErrorDialogFragment.kt @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.fragments + +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.R + +class CoreErrorDialogFragment : DialogFragment() { + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = + MaterialAlertDialogBuilder(requireActivity()) + .setTitle(requireArguments().getString(TITLE)) + .setMessage(requireArguments().getString(MESSAGE)) + .setPositiveButton(R.string.continue_button, null) + .setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int -> + NativeLibrary.coreErrorAlertResult = false + synchronized(NativeLibrary.coreErrorAlertLock) { + NativeLibrary.coreErrorAlertLock.notify() + } + } + .create() + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + NativeLibrary.coreErrorAlertResult = true + synchronized(NativeLibrary.coreErrorAlertLock) { NativeLibrary.coreErrorAlertLock.notify() } + } + + companion object { + const val TITLE = "Title" + const val MESSAGE = "Message" + + fun newInstance(title: String, message: String): CoreErrorDialogFragment { + val frag = CoreErrorDialogFragment() + val args = Bundle() + args.putString(TITLE, title) + args.putString(MESSAGE, message) + frag.arguments = args + return frag + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt index 41cff46c1..8b23a1021 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt @@ -3,7 +3,6 @@ package org.yuzu.yuzu_emu.fragments -import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -14,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.GridLayoutManager @@ -35,6 +31,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins +import org.yuzu.yuzu_emu.utils.collect import java.io.File import java.io.IOException @@ -63,8 +60,6 @@ class DriverManagerFragment : Fragment() { return binding.root } - // This is using the correct scope, lint is just acting up - @SuppressLint("UnsafeRepeatOnLifecycleDetector") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setNavigationVisibility(visible = false, animated = true) @@ -89,15 +84,8 @@ class DriverManagerFragment : Fragment() { } } - viewLifecycleOwner.lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - driverViewModel.showClearButton.collect { - binding.toolbarDrivers.menu - .findItem(R.id.menu_driver_use_global).isVisible = it - } - } - } + driverViewModel.showClearButton.collect(viewLifecycleOwner) { + binding.toolbarDrivers.menu.findItem(R.id.menu_driver_use_global).isVisible = it } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt index 6a47b29f0..bad56e434 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt @@ -10,14 +10,11 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.dialog.MaterialAlertDialogBuilder -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding import org.yuzu.yuzu_emu.model.DriverViewModel +import org.yuzu.yuzu_emu.utils.collect class DriversLoadingDialogFragment : DialogFragment() { private val driverViewModel: DriverViewModel by activityViewModels() @@ -44,13 +41,7 @@ class DriversLoadingDialogFragment : DialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewLifecycleOwner.lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { - driverViewModel.isInteractionAllowed.collect { if (it) dismiss() } - } - } - } + driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) { if (it) dismiss() } } companion object { 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..bcc880e17 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,14 +26,12 @@ 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 import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.window.layout.FoldingFeature @@ -39,9 +39,6 @@ import androidx.window.layout.WindowInfoTracker import androidx.window.layout.WindowLayoutInfo import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.slider.Slider -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R @@ -52,6 +49,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 @@ -59,6 +57,7 @@ import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.overlay.model.OverlayControl import org.yuzu.yuzu_emu.overlay.model.OverlayLayout import org.yuzu.yuzu_emu.utils.* +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import java.lang.NullPointerException class EmulationFragment : Fragment(), SurfaceHolder.Callback { @@ -86,14 +85,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { if (context is EmulationActivity) { emulationActivity = context NativeLibrary.setEmulationActivity(context) - - lifecycleScope.launch(Dispatchers.Main) { - lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - WindowInfoTracker.getOrCreate(context) - .windowLayoutInfo(context) - .collect { updateFoldableLayout(context, it) } - } - } } else { throw IllegalStateException("EmulationFragment must have EmulationActivity parent") } @@ -164,8 +155,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { return binding.root } - // This is using the correct scope, lint is just acting up - @SuppressLint("UnsafeRepeatOnLifecycleDetector") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) if (requireActivity().isFinishing) { @@ -273,6 +262,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { true } + R.id.menu_controls -> { + val action = HomeNavigationDirections.actionGlobalSettingsActivity( + null, + Settings.MenuTag.SECTION_INPUT + ) + binding.root.findNavController().navigate(action) + true + } + R.id.menu_overlay_controls -> { showOverlayOptions() true @@ -337,129 +335,86 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { binding.loadingTitle.isSelected = true binding.loadingText.isSelected = true - viewLifecycleOwner.lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - WindowInfoTracker.getOrCreate(requireContext()) - .windowLayoutInfo(requireActivity()) - .collect { - updateFoldableLayout(requireActivity() as EmulationActivity, it) - } - } + WindowInfoTracker.getOrCreate(requireContext()) + .windowLayoutInfo(requireActivity()).collect(viewLifecycleOwner) { + updateFoldableLayout(requireActivity() as EmulationActivity, it) } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - emulationViewModel.shaderProgress.collectLatest { - if (it > 0 && it != emulationViewModel.totalShaders.value) { - binding.loadingProgressIndicator.isIndeterminate = false - - if (it < binding.loadingProgressIndicator.max) { - binding.loadingProgressIndicator.progress = it - } - } + emulationViewModel.shaderProgress.collect(viewLifecycleOwner) { + if (it > 0 && it != emulationViewModel.totalShaders.value) { + binding.loadingProgressIndicator.isIndeterminate = false - if (it == emulationViewModel.totalShaders.value) { - binding.loadingText.setText(R.string.loading) - binding.loadingProgressIndicator.isIndeterminate = true - } - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - emulationViewModel.totalShaders.collectLatest { - binding.loadingProgressIndicator.max = it - } + if (it < binding.loadingProgressIndicator.max) { + binding.loadingProgressIndicator.progress = it } } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - emulationViewModel.shaderMessage.collectLatest { - if (it.isNotEmpty()) { - binding.loadingText.text = it - } - } - } + + if (it == emulationViewModel.totalShaders.value) { + binding.loadingText.setText(R.string.loading) + binding.loadingProgressIndicator.isIndeterminate = true } - launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { - driverViewModel.isInteractionAllowed.collect { - if (it) { - startEmulation() - } - } - } + } + emulationViewModel.totalShaders.collect(viewLifecycleOwner) { + binding.loadingProgressIndicator.max = it + } + emulationViewModel.shaderMessage.collect(viewLifecycleOwner) { + if (it.isNotEmpty()) { + binding.loadingText.text = it } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - emulationViewModel.emulationStarted.collectLatest { - if (it) { - binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt()) - ViewUtils.showView(binding.surfaceInputOverlay) - ViewUtils.hideView(binding.loadingIndicator) - - emulationState.updateSurface() - - // Setup overlays - updateShowFpsOverlay() - updateThermalOverlay() - } - } - } + } + + emulationViewModel.emulationStarted.collect(viewLifecycleOwner) { + if (it) { + binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt()) + ViewUtils.showView(binding.surfaceInputOverlay) + ViewUtils.hideView(binding.loadingIndicator) + + emulationState.updateSurface() + + // Setup overlays + updateShowFpsOverlay() + updateThermalOverlay() } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - emulationViewModel.isEmulationStopping.collectLatest { - if (it) { - binding.loadingText.setText(R.string.shutting_down) - ViewUtils.showView(binding.loadingIndicator) - ViewUtils.hideView(binding.inputContainer) - ViewUtils.hideView(binding.showFpsText) - } - } - } + } + emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) { + if (it) { + binding.loadingText.setText(R.string.shutting_down) + ViewUtils.showView(binding.loadingIndicator) + ViewUtils.hideView(binding.inputContainer) + ViewUtils.hideView(binding.showFpsText) } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - emulationViewModel.drawerOpen.collect { - if (it) { - binding.drawerLayout.open() - binding.inGameMenu.requestFocus() - } else { - binding.drawerLayout.close() - } - } - } + } + emulationViewModel.drawerOpen.collect(viewLifecycleOwner) { + if (it) { + binding.drawerLayout.open() + binding.inGameMenu.requestFocus() + } else { + binding.drawerLayout.close() } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - emulationViewModel.programChanged.collect { - if (it != 0) { - emulationViewModel.setEmulationStarted(false) - binding.drawerLayout.close() - binding.drawerLayout - .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) - ViewUtils.hideView(binding.surfaceInputOverlay) - ViewUtils.showView(binding.loadingIndicator) - } - } - } + } + emulationViewModel.programChanged.collect(viewLifecycleOwner) { + if (it != 0) { + emulationViewModel.setEmulationStarted(false) + binding.drawerLayout.close() + binding.drawerLayout + .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) + ViewUtils.hideView(binding.surfaceInputOverlay) + ViewUtils.showView(binding.loadingIndicator) } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - emulationViewModel.emulationStopped.collect { - if (it && emulationViewModel.programChanged.value != -1) { - if (perfStatsUpdater != null) { - perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!) - } - emulationState.changeProgram(emulationViewModel.programChanged.value) - emulationViewModel.setProgramChanged(-1) - emulationViewModel.setEmulationStopped(false) - } - } + } + emulationViewModel.emulationStopped.collect(viewLifecycleOwner) { + if (it && emulationViewModel.programChanged.value != -1) { + if (perfStatsUpdater != null) { + perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!) } + emulationState.changeProgram(emulationViewModel.programChanged.value) + emulationViewModel.setProgramChanged(-1) + emulationViewModel.setEmulationStopped(false) } } + + driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) { + if (it) startEmulation() + } } private fun startEmulation(programIndex: Int = 0) { @@ -487,14 +442,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { binding.drawerLayout.close() } if (showInputOverlay) { - binding.surfaceInputOverlay.visibility = View.INVISIBLE + binding.surfaceInputOverlay.setVisible(visible = false, gone = false) } } else { - if (showInputOverlay && emulationViewModel.emulationStarted.value) { - binding.surfaceInputOverlay.visibility = View.VISIBLE - } else { - binding.surfaceInputOverlay.visibility = View.INVISIBLE - } + binding.surfaceInputOverlay.setVisible( + showInputOverlay && emulationViewModel.emulationStarted.value + ) if (!isInFoldableLayout) { if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { binding.surfaceInputOverlay.layout = OverlayLayout.Portrait @@ -531,7 +484,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } private fun updateShowFpsOverlay() { - if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) { + val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() + binding.showFpsText.setVisible(showOverlay) + if (showOverlay) { val SYSTEM_FPS = 0 val FPS = 1 val FRAMETIME = 2 @@ -551,17 +506,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } perfStatsUpdateHandler.post(perfStatsUpdater!!) - binding.showFpsText.visibility = View.VISIBLE } else { if (perfStatsUpdater != null) { perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!) } - binding.showFpsText.visibility = View.GONE } } private fun updateThermalOverlay() { - if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) { + val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() + binding.showThermalsText.setVisible(showOverlay) + if (showOverlay) { thermalStatsUpdater = { if (emulationViewModel.emulationStarted.value && !emulationViewModel.isEmulationStopping.value @@ -583,12 +538,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } thermalStatsUpdateHandler.post(thermalStatsUpdater!!) - binding.showThermalsText.visibility = View.VISIBLE } else { if (thermalStatsUpdater != null) { thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!) } - binding.showThermalsText.visibility = View.GONE } } @@ -617,7 +570,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() } @@ -818,12 +810,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } } - binding.doneControlConfig.visibility = View.VISIBLE + binding.doneControlConfig.setVisible(true) binding.surfaceInputOverlay.setIsInEditMode(true) } private fun stopConfiguringControls() { - binding.doneControlConfig.visibility = View.GONE + binding.doneControlConfig.setVisible(false) binding.surfaceInputOverlay.setIsInEditMode(false) // Unlock the orientation if it was locked for editing if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt index 5c558b1a5..3a6f7a38c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt @@ -13,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.transition.MaterialSharedAxis @@ -27,6 +24,7 @@ import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins +import org.yuzu.yuzu_emu.utils.collect class GameFoldersFragment : Fragment() { private var _binding: FragmentFoldersBinding? = null @@ -70,12 +68,8 @@ class GameFoldersFragment : Fragment() { adapter = FolderAdapter(requireActivity(), gamesViewModel) } - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - gamesViewModel.folders.collect { - (binding.listFolders.adapter as FolderAdapter).submitList(it) - } - } + gamesViewModel.folders.collect(viewLifecycleOwner) { + (binding.listFolders.adapter as FolderAdapter).submitList(it) } val mainActivity = requireActivity() as MainActivity diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt index dbd56e84f..97a8954bb 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt @@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding import org.yuzu.yuzu_emu.model.GameVerificationResult import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.utils.GameMetadata +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins class GameInfoFragment : Fragment() { @@ -85,7 +86,7 @@ class GameInfoFragment : Fragment() { copyToClipboard(getString(R.string.developer), args.game.developer) } } else { - developer.visibility = View.GONE + developer.setVisible(false) } version.setHint(R.string.version) 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..c06842c59 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 @@ -3,11 +3,9 @@ package org.yuzu.yuzu_emu.fragments -import android.annotation.SuppressLint import android.content.pm.ShortcutInfo import android.content.pm.ShortcutManager import android.os.Bundle -import android.text.TextUtils import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -18,9 +16,7 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.GridLayoutManager @@ -46,7 +42,9 @@ import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.GameIconUtils import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.MemoryUtil +import org.yuzu.yuzu_emu.utils.ViewUtils.marquee import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins +import org.yuzu.yuzu_emu.utils.collect import java.io.BufferedOutputStream import java.io.File @@ -76,8 +74,6 @@ class GamePropertiesFragment : Fragment() { return binding.root } - // This is using the correct scope, lint is just acting up - @SuppressLint("UnsafeRepeatOnLifecycleDetector") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setNavigationVisibility(visible = false, animated = true) @@ -107,13 +103,7 @@ class GamePropertiesFragment : Fragment() { GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen) binding.title.text = args.game.title - binding.title.postDelayed( - { - binding.title.ellipsize = TextUtils.TruncateAt.MARQUEE - binding.title.isSelected = true - }, - 3000 - ) + binding.title.marquee() binding.buttonStart.setOnClickListener { LaunchGameDialogFragment.newInstance(args.game) @@ -122,28 +112,14 @@ class GamePropertiesFragment : Fragment() { reloadList() - viewLifecycleOwner.lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - homeViewModel.openImportSaves.collect { - if (it) { - importSaves.launch(arrayOf("application/zip")) - homeViewModel.setOpenImportSaves(false) - } - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - homeViewModel.reloadPropertiesList.collect { - if (it) { - reloadList() - homeViewModel.reloadPropertiesList(false) - } - } - } - } - } + homeViewModel.openImportSaves.collect( + viewLifecycleOwner, + resetState = { homeViewModel.setOpenImportSaves(false) } + ) { if (it) importSaves.launch(arrayOf("application/zip")) } + homeViewModel.reloadPropertiesList.collect( + viewLifecycleOwner, + resetState = { homeViewModel.reloadPropertiesList(false) } + ) { if (it) reloadList() } setInsets() } @@ -243,7 +219,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/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index 87e130d3e..14a2504b6 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt @@ -91,6 +91,20 @@ class HomeSettingsFragment : Fragment() { ) add( HomeSetting( + R.string.preferences_controls, + R.string.preferences_controls_description, + R.drawable.ic_controller, + { + val action = HomeNavigationDirections.actionGlobalSettingsActivity( + null, + Settings.MenuTag.SECTION_INPUT + ) + binding.root.findNavController().navigate(action) + } + ) + ) + add( + HomeSetting( R.string.gpu_driver_manager, R.string.install_gpu_driver_description, R.drawable.ic_build, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt index 63112dc6f..d218da1c8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt @@ -14,9 +14,6 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.transition.MaterialSharedAxis @@ -35,6 +32,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins +import org.yuzu.yuzu_emu.utils.collect import java.io.BufferedOutputStream import java.io.File import java.math.BigInteger @@ -75,14 +73,10 @@ class InstallableFragment : Fragment() { binding.root.findNavController().popBackStack() } - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - homeViewModel.openImportSaves.collect { - if (it) { - importSaves.launch(arrayOf("application/zip")) - homeViewModel.setOpenImportSaves(false) - } - } + homeViewModel.openImportSaves.collect(viewLifecycleOwner) { + if (it) { + importSaves.launch(arrayOf("application/zip")) + homeViewModel.setOpenImportSaves(false) } } 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/fragments/ProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt index d201cb80c..ee3bb0386 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt @@ -13,15 +13,13 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentActivity import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.dialog.MaterialAlertDialogBuilder -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding import org.yuzu.yuzu_emu.model.TaskViewModel +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible +import org.yuzu.yuzu_emu.utils.collect class ProgressDialogFragment : DialogFragment() { private val taskViewModel: TaskViewModel by activityViewModels() @@ -64,72 +62,50 @@ class ProgressDialogFragment : DialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.message.isSelected = true - viewLifecycleOwner.lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - taskViewModel.isComplete.collect { - if (it) { - dismiss() - when (val result = taskViewModel.result.value) { - is String -> Toast.makeText( - requireContext(), - result, - Toast.LENGTH_LONG - ).show() - - is MessageDialogFragment -> result.show( - requireActivity().supportFragmentManager, - MessageDialogFragment.TAG - ) - - else -> { - // Do nothing - } - } - taskViewModel.clear() - } + taskViewModel.isComplete.collect(viewLifecycleOwner) { + if (it) { + dismiss() + when (val result = taskViewModel.result.value) { + is String -> Toast.makeText( + requireContext(), + result, + Toast.LENGTH_LONG + ).show() + + is MessageDialogFragment -> result.show( + requireActivity().supportFragmentManager, + MessageDialogFragment.TAG + ) + + else -> { + // Do nothing } } + taskViewModel.clear() } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - taskViewModel.cancelled.collect { - if (it) { - dialog?.setTitle(R.string.cancelling) - } - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - taskViewModel.progress.collect { - if (it != 0.0) { - binding.progressBar.apply { - isIndeterminate = false - progress = ( - (it / taskViewModel.maxProgress.value) * - PROGRESS_BAR_RESOLUTION - ).toInt() - min = 0 - max = PROGRESS_BAR_RESOLUTION - } - } - } - } + } + taskViewModel.cancelled.collect(viewLifecycleOwner) { + if (it) { + dialog?.setTitle(R.string.cancelling) } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - taskViewModel.message.collect { - if (it.isEmpty()) { - binding.message.visibility = View.GONE - } else { - binding.message.visibility = View.VISIBLE - binding.message.text = it - } - } + } + taskViewModel.progress.collect(viewLifecycleOwner) { + if (it != 0.0) { + binding.progressBar.apply { + isIndeterminate = false + progress = ( + (it / taskViewModel.maxProgress.value) * + PROGRESS_BAR_RESOLUTION + ).toInt() + min = 0 + max = PROGRESS_BAR_RESOLUTION } } } + taskViewModel.message.collect(viewLifecycleOwner) { + binding.message.setVisible(it.isNotEmpty()) + binding.message.text = it + } } // By default, the ProgressDialog will immediately dismiss itself upon a button being pressed. diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt index 20b10b1a0..662ae9760 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt @@ -3,7 +3,6 @@ package org.yuzu.yuzu_emu.fragments -import android.annotation.SuppressLint import android.content.Context import android.content.SharedPreferences import android.os.Bundle @@ -18,14 +17,9 @@ import androidx.core.view.updatePadding import androidx.core.widget.doOnTextChanged import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.preference.PreferenceManager import info.debatty.java.stringsimilarity.Jaccard import info.debatty.java.stringsimilarity.JaroWinkler -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch import java.util.Locale import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication @@ -35,6 +29,8 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.HomeViewModel +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible +import org.yuzu.yuzu_emu.utils.collect class SearchFragment : Fragment() { private var _binding: FragmentSearchBinding? = null @@ -58,8 +54,6 @@ class SearchFragment : Fragment() { return binding.root } - // This is using the correct scope, lint is just acting up - @SuppressLint("UnsafeRepeatOnLifecycleDetector") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setNavigationVisibility(visible = true, animated = true) @@ -81,42 +75,18 @@ class SearchFragment : Fragment() { binding.chipGroup.setOnCheckedStateChangeListener { _, _ -> filterAndSearch() } binding.searchText.doOnTextChanged { text: CharSequence?, _: Int, _: Int, _: Int -> - if (text.toString().isNotEmpty()) { - binding.clearButton.visibility = View.VISIBLE - } else { - binding.clearButton.visibility = View.INVISIBLE - } + binding.clearButton.setVisible(text.toString().isNotEmpty()) filterAndSearch() } - viewLifecycleOwner.lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - gamesViewModel.searchFocused.collect { - if (it) { - focusSearch() - gamesViewModel.setSearchFocused(false) - } - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - gamesViewModel.games.collectLatest { filterAndSearch() } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - gamesViewModel.searchedGames.collect { - (binding.gridGamesSearch.adapter as GameAdapter).submitList(it) - if (it.isEmpty()) { - binding.noResultsView.visibility = View.VISIBLE - } else { - binding.noResultsView.visibility = View.GONE - } - } - } - } + gamesViewModel.searchFocused.collect( + viewLifecycleOwner, + resetState = { gamesViewModel.setSearchFocused(false) } + ) { if (it) focusSearch() } + gamesViewModel.games.collect(viewLifecycleOwner) { filterAndSearch() } + gamesViewModel.searchedGames.collect(viewLifecycleOwner) { + (binding.gridGamesSearch.adapter as GameAdapter).submitList(it) + binding.noResultsView.setVisible(it.isNotEmpty()) } binding.clearButton.setOnClickListener { binding.searchText.setText("") } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt deleted file mode 100644 index 60e029f34..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.yuzu.yuzu_emu.fragments - -import android.app.Dialog -import android.content.DialogInterface -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.slider.Slider -import kotlinx.coroutines.launch -import org.yuzu.yuzu_emu.R -import org.yuzu.yuzu_emu.databinding.DialogSliderBinding -import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem -import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting -import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting -import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting -import org.yuzu.yuzu_emu.model.SettingsViewModel - -class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener { - private var type = 0 - private var position = 0 - - private var defaultCancelListener = - DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() } - - private val settingsViewModel: SettingsViewModel by activityViewModels() - - private lateinit var sliderBinding: DialogSliderBinding - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - type = requireArguments().getInt(TYPE) - position = requireArguments().getInt(POSITION) - - if (settingsViewModel.clickedItem == null) dismiss() - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return when (type) { - TYPE_RESET_SETTING -> { - MaterialAlertDialogBuilder(requireContext()) - .setMessage(R.string.reset_setting_confirmation) - .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> - settingsViewModel.clickedItem!!.setting.reset() - settingsViewModel.setAdapterItemChanged(position) - } - .setNegativeButton(android.R.string.cancel, null) - .create() - } - - SettingsItem.TYPE_SINGLE_CHOICE -> { - val item = settingsViewModel.clickedItem as SingleChoiceSetting - val value = getSelectionForSingleChoiceValue(item) - MaterialAlertDialogBuilder(requireContext()) - .setTitle(item.nameId) - .setSingleChoiceItems(item.choicesId, value, this) - .create() - } - - SettingsItem.TYPE_SLIDER -> { - sliderBinding = DialogSliderBinding.inflate(layoutInflater) - val item = settingsViewModel.clickedItem as SliderSetting - - settingsViewModel.setSliderTextValue(item.getSelectedValue().toFloat(), item.units) - sliderBinding.slider.apply { - valueFrom = item.min.toFloat() - valueTo = item.max.toFloat() - value = settingsViewModel.sliderProgress.value.toFloat() - addOnChangeListener { _: Slider, value: Float, _: Boolean -> - settingsViewModel.setSliderTextValue(value, item.units) - } - } - - MaterialAlertDialogBuilder(requireContext()) - .setTitle(item.nameId) - .setView(sliderBinding.root) - .setPositiveButton(android.R.string.ok, this) - .setNegativeButton(android.R.string.cancel, defaultCancelListener) - .create() - } - - SettingsItem.TYPE_STRING_SINGLE_CHOICE -> { - val item = settingsViewModel.clickedItem as StringSingleChoiceSetting - MaterialAlertDialogBuilder(requireContext()) - .setTitle(item.nameId) - .setSingleChoiceItems(item.choices, item.selectValueIndex, this) - .create() - } - - else -> super.onCreateDialog(savedInstanceState) - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return when (type) { - SettingsItem.TYPE_SLIDER -> sliderBinding.root - else -> super.onCreateView(inflater, container, savedInstanceState) - } - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - when (type) { - SettingsItem.TYPE_SLIDER -> { - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - settingsViewModel.sliderTextValue.collect { - sliderBinding.textValue.text = it - } - } - repeatOnLifecycle(Lifecycle.State.CREATED) { - settingsViewModel.sliderProgress.collect { - sliderBinding.slider.value = it.toFloat() - } - } - } - } - } - } - - override fun onClick(dialog: DialogInterface, which: Int) { - when (settingsViewModel.clickedItem) { - is SingleChoiceSetting -> { - val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting - val value = getValueForSingleChoiceSelection(scSetting, which) - scSetting.setSelectedValue(value) - } - - is StringSingleChoiceSetting -> { - val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting - val value = scSetting.getValueAt(which) - scSetting.setSelectedValue(value) - } - - is SliderSetting -> { - val sliderSetting = settingsViewModel.clickedItem as SliderSetting - sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value) - } - } - closeDialog() - } - - private fun closeDialog() { - settingsViewModel.setAdapterItemChanged(position) - settingsViewModel.clickedItem = null - settingsViewModel.setSliderProgress(-1f) - dismiss() - } - - private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int { - val valuesId = item.valuesId - return if (valuesId > 0) { - val valuesArray = requireContext().resources.getIntArray(valuesId) - valuesArray[which] - } else { - which - } - } - - private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int { - val value = item.getSelectedValue() - val valuesId = item.valuesId - if (valuesId > 0) { - val valuesArray = requireContext().resources.getIntArray(valuesId) - for (index in valuesArray.indices) { - val current = valuesArray[index] - if (current == value) { - return index - } - } - } else { - return value - } - return -1 - } - - companion object { - const val TAG = "SettingsDialogFragment" - - const val TYPE_RESET_SETTING = -1 - - const val TITLE = "Title" - const val TYPE = "Type" - const val POSITION = "Position" - - fun newInstance( - settingsViewModel: SettingsViewModel, - clickedItem: SettingsItem, - type: Int, - position: Int - ): SettingsDialogFragment { - when (type) { - SettingsItem.TYPE_HEADER, - SettingsItem.TYPE_SWITCH, - SettingsItem.TYPE_SUBMENU, - SettingsItem.TYPE_DATETIME_SETTING, - SettingsItem.TYPE_RUNNABLE -> - throw IllegalArgumentException("[SettingsDialogFragment] Incompatible type!") - - SettingsItem.TYPE_SLIDER -> settingsViewModel.setSliderProgress( - (clickedItem as SliderSetting).getSelectedValue().toFloat() - ) - } - settingsViewModel.clickedItem = clickedItem - - val args = Bundle() - args.putInt(TYPE, type) - args.putInt(POSITION, position) - val fragment = SettingsDialogFragment() - fragment.arguments = args - return fragment - } - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt deleted file mode 100644 index a135b80b4..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.yuzu.yuzu_emu.fragments - -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.inputmethod.InputMethodManager -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding -import androidx.core.widget.doOnTextChanged -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.divider.MaterialDividerItemDecoration -import com.google.android.material.transition.MaterialSharedAxis -import info.debatty.java.stringsimilarity.Cosine -import kotlinx.coroutines.launch -import org.yuzu.yuzu_emu.R -import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding -import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem -import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter -import org.yuzu.yuzu_emu.model.SettingsViewModel -import org.yuzu.yuzu_emu.utils.NativeConfig -import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins - -class SettingsSearchFragment : Fragment() { - private var _binding: FragmentSettingsSearchBinding? = null - private val binding get() = _binding!! - - private var settingsAdapter: SettingsAdapter? = null - - private val settingsViewModel: SettingsViewModel by activityViewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) - returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) - reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) - exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - _binding = FragmentSettingsSearchBinding.inflate(layoutInflater) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - if (savedInstanceState != null) { - binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) - } - - settingsAdapter = SettingsAdapter(this, requireContext()) - - val dividerDecoration = MaterialDividerItemDecoration( - requireContext(), - LinearLayoutManager.VERTICAL - ) - dividerDecoration.isLastItemDecorated = false - binding.settingsList.apply { - adapter = settingsAdapter - layoutManager = LinearLayoutManager(requireContext()) - addItemDecoration(dividerDecoration) - } - - focusSearch() - - binding.backButton.setOnClickListener { settingsViewModel.setShouldNavigateBack(true) } - binding.searchBackground.setOnClickListener { focusSearch() } - binding.clearButton.setOnClickListener { binding.searchText.setText("") } - binding.searchText.doOnTextChanged { _, _, _, _ -> - search() - binding.settingsList.smoothScrollToPosition(0) - } - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - settingsViewModel.shouldReloadSettingsList.collect { - if (it) { - settingsViewModel.setShouldReloadSettingsList(false) - search() - } - } - } - } - - search() - - setInsets() - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putString(SEARCH_TEXT, binding.searchText.text.toString()) - } - - private fun search() { - val searchTerm = binding.searchText.text.toString().lowercase() - binding.clearButton.visibility = - if (searchTerm.isEmpty()) View.INVISIBLE else View.VISIBLE - if (searchTerm.isEmpty()) { - binding.noResultsView.visibility = View.VISIBLE - settingsAdapter?.submitList(emptyList()) - return - } - - val baseList = SettingsItem.settingsItems - val similarityAlgorithm = if (searchTerm.length > 2) Cosine() else Cosine(1) - val sortedList: List<SettingsItem> = baseList.mapNotNull { item -> - val title = getString(item.value.nameId).lowercase() - val similarity = similarityAlgorithm.similarity(searchTerm, title) - if (similarity > 0.08) { - Pair(similarity, item) - } else { - null - } - }.sortedByDescending { it.first }.mapNotNull { - val item = it.second.value - val pairedSettingKey = item.setting.pairedSettingKey - val optionalSetting: SettingsItem? = if (pairedSettingKey.isNotEmpty()) { - val pairedSettingValue = NativeConfig.getBoolean(pairedSettingKey, false) - if (pairedSettingValue) it.second.value else null - } else { - it.second.value - } - optionalSetting - } - settingsAdapter?.submitList(sortedList) - binding.noResultsView.visibility = - if (sortedList.isEmpty()) View.VISIBLE else View.INVISIBLE - } - - private fun focusSearch() { - binding.searchText.requestFocus() - val imm = requireActivity() - .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager? - imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT) - } - - private fun setInsets() = - ViewCompat.setOnApplyWindowInsetsListener( - binding.root - ) { _: View, windowInsets: WindowInsetsCompat -> - val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med) - val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge) - val topMargin = resources.getDimensionPixelSize(R.dimen.spacing_chip) - - val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) - val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) - - val leftInsets = barInsets.left + cutoutInsets.left - val rightInsets = barInsets.right + cutoutInsets.right - - binding.settingsList.updatePadding(bottom = barInsets.bottom + extraListSpacing) - binding.frameSearch.updatePadding( - left = leftInsets + sideMargin, - top = barInsets.top + topMargin, - right = rightInsets + sideMargin - ) - binding.noResultsView.updatePadding( - left = leftInsets, - right = rightInsets, - bottom = barInsets.bottom - ) - - binding.settingsList.updateMargins( - left = leftInsets + sideMargin, - right = rightInsets + sideMargin - ) - binding.divider.updateMargins( - left = leftInsets + sideMargin, - right = rightInsets + sideMargin - ) - - windowInsets - } - - companion object { - const val SEARCH_TEXT = "SearchText" - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt index ebf41a639..4f7548e98 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt @@ -4,7 +4,6 @@ package org.yuzu.yuzu_emu.fragments import android.Manifest -import android.annotation.SuppressLint import android.content.Intent import android.os.Build import android.os.Bundle @@ -23,9 +22,6 @@ import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.preference.PreferenceManager import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback @@ -46,6 +42,8 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.ViewUtils +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible +import org.yuzu.yuzu_emu.utils.collect class SetupFragment : Fragment() { private var _binding: FragmentSetupBinding? = null @@ -77,8 +75,6 @@ class SetupFragment : Fragment() { return binding.root } - // This is using the correct scope, lint is just acting up - @SuppressLint("UnsafeRepeatOnLifecycleDetector") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { mainActivity = requireActivity() as MainActivity @@ -210,28 +206,14 @@ class SetupFragment : Fragment() { ) } - viewLifecycleOwner.lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - homeViewModel.shouldPageForward.collect { - if (it) { - pageForward() - homeViewModel.setShouldPageForward(false) - } - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - homeViewModel.gamesDirSelected.collect { - if (it) { - gamesDirCallback.onStepCompleted() - homeViewModel.setGamesDirSelected(false) - } - } - } - } - } + homeViewModel.shouldPageForward.collect( + viewLifecycleOwner, + resetState = { homeViewModel.setShouldPageForward(false) } + ) { if (it) pageForward() } + homeViewModel.gamesDirSelected.collect( + viewLifecycleOwner, + resetState = { homeViewModel.setGamesDirSelected(false) } + ) { if (it) gamesDirCallback.onStepCompleted() } binding.viewPager2.apply { adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages) @@ -292,12 +274,8 @@ class SetupFragment : Fragment() { val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY) hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!! - if (nextIsVisible) { - binding.buttonNext.visibility = View.VISIBLE - } - if (backIsVisible) { - binding.buttonBack.visibility = View.VISIBLE - } + binding.buttonNext.setVisible(nextIsVisible) + binding.buttonBack.setVisible(backIsVisible) } else { hasBeenWarned = BooleanArray(pages.size) } 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/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt deleted file mode 100644 index 5cb6a5d57..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.yuzu.yuzu_emu.model - -import androidx.lifecycle.ViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import org.yuzu.yuzu_emu.R -import org.yuzu.yuzu_emu.YuzuApplication -import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem - -class SettingsViewModel : ViewModel() { - var game: Game? = null - - var clickedItem: SettingsItem? = null - - val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate - private val _shouldRecreate = MutableStateFlow(false) - - val shouldNavigateBack: StateFlow<Boolean> get() = _shouldNavigateBack - private val _shouldNavigateBack = MutableStateFlow(false) - - val shouldShowResetSettingsDialog: StateFlow<Boolean> get() = _shouldShowResetSettingsDialog - private val _shouldShowResetSettingsDialog = MutableStateFlow(false) - - val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList - private val _shouldReloadSettingsList = MutableStateFlow(false) - - val sliderProgress: StateFlow<Int> get() = _sliderProgress - private val _sliderProgress = MutableStateFlow(-1) - - val sliderTextValue: StateFlow<String> get() = _sliderTextValue - private val _sliderTextValue = MutableStateFlow("") - - val adapterItemChanged: StateFlow<Int> get() = _adapterItemChanged - private val _adapterItemChanged = MutableStateFlow(-1) - - fun setShouldRecreate(value: Boolean) { - _shouldRecreate.value = value - } - - fun setShouldNavigateBack(value: Boolean) { - _shouldNavigateBack.value = value - } - - fun setShouldShowResetSettingsDialog(value: Boolean) { - _shouldShowResetSettingsDialog.value = value - } - - fun setShouldReloadSettingsList(value: Boolean) { - _shouldReloadSettingsList.value = value - } - - fun setSliderTextValue(value: Float, units: String) { - _sliderProgress.value = value.toInt() - _sliderTextValue.value = String.format( - YuzuApplication.appContext.getString(R.string.value_with_units), - value.toInt().toString(), - units - ) - } - - fun setSliderProgress(value: Float) { - _sliderProgress.value = value.toInt() - } - - fun setAdapterItemChanged(value: Int) { - _adapterItemChanged.value = value - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt index c87486c90..737e03584 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt @@ -24,10 +24,11 @@ import androidx.core.content.ContextCompat import androidx.window.layout.WindowMetricsCalculator import kotlin.math.max import kotlin.math.min -import org.yuzu.yuzu_emu.NativeLibrary -import org.yuzu.yuzu_emu.NativeLibrary.ButtonType -import org.yuzu.yuzu_emu.NativeLibrary.StickType +import org.yuzu.yuzu_emu.features.input.NativeInput import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.features.input.model.NativeAnalog +import org.yuzu.yuzu_emu.features.input.model.NativeButton +import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.overlay.model.OverlayControl @@ -99,20 +100,18 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : } var shouldUpdateView = false - val playerIndex = - if (NativeLibrary.isHandheldOnly()) { - NativeLibrary.ConsoleDevice - } else { - NativeLibrary.Player1Device - } + val playerIndex = when (NativeInput.getStyleIndex(0)) { + NpadStyleIndex.Handheld -> 8 + else -> 0 + } for (button in overlayButtons) { if (!button.updateStatus(event)) { continue } - NativeLibrary.onGamePadButtonEvent( + NativeInput.onOverlayButtonEvent( playerIndex, - button.buttonId, + button.button, button.status ) playHaptics(event) @@ -123,24 +122,24 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : if (!dpad.updateStatus(event, BooleanSetting.DPAD_SLIDE.getBoolean())) { continue } - NativeLibrary.onGamePadButtonEvent( + NativeInput.onOverlayButtonEvent( playerIndex, - dpad.upId, + dpad.up, dpad.upStatus ) - NativeLibrary.onGamePadButtonEvent( + NativeInput.onOverlayButtonEvent( playerIndex, - dpad.downId, + dpad.down, dpad.downStatus ) - NativeLibrary.onGamePadButtonEvent( + NativeInput.onOverlayButtonEvent( playerIndex, - dpad.leftId, + dpad.left, dpad.leftStatus ) - NativeLibrary.onGamePadButtonEvent( + NativeInput.onOverlayButtonEvent( playerIndex, - dpad.rightId, + dpad.right, dpad.rightStatus ) playHaptics(event) @@ -151,16 +150,15 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : if (!joystick.updateStatus(event)) { continue } - val axisID = joystick.joystickId - NativeLibrary.onGamePadJoystickEvent( + NativeInput.onOverlayJoystickEvent( playerIndex, - axisID, + joystick.joystick, joystick.xAxis, joystick.realYAxis ) - NativeLibrary.onGamePadButtonEvent( + NativeInput.onOverlayButtonEvent( playerIndex, - joystick.buttonId, + joystick.button, joystick.buttonStatus ) playHaptics(event) @@ -187,7 +185,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP if (isActionDown && !isTouchInputConsumed(pointerId)) { - NativeLibrary.onTouchPressed(pointerId, xPosition.toFloat(), yPosition.toFloat()) + NativeInput.onTouchPressed(pointerId, xPosition.toFloat(), yPosition.toFloat()) } if (isActionMove) { @@ -196,12 +194,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : if (isTouchInputConsumed(fingerId)) { continue } - NativeLibrary.onTouchMoved(fingerId, event.getX(i), event.getY(i)) + NativeInput.onTouchMoved(fingerId, event.getX(i), event.getY(i)) } } if (isActionUp && !isTouchInputConsumed(pointerId)) { - NativeLibrary.onTouchReleased(pointerId) + NativeInput.onTouchReleased(pointerId) } return true @@ -359,7 +357,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.facebutton_a, R.drawable.facebutton_a_depressed, - ButtonType.BUTTON_A, + NativeButton.A, data, position ) @@ -373,7 +371,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.facebutton_b, R.drawable.facebutton_b_depressed, - ButtonType.BUTTON_B, + NativeButton.B, data, position ) @@ -387,7 +385,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.facebutton_x, R.drawable.facebutton_x_depressed, - ButtonType.BUTTON_X, + NativeButton.X, data, position ) @@ -401,7 +399,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.facebutton_y, R.drawable.facebutton_y_depressed, - ButtonType.BUTTON_Y, + NativeButton.Y, data, position ) @@ -415,7 +413,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.facebutton_plus, R.drawable.facebutton_plus_depressed, - ButtonType.BUTTON_PLUS, + NativeButton.Plus, data, position ) @@ -429,7 +427,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.facebutton_minus, R.drawable.facebutton_minus_depressed, - ButtonType.BUTTON_MINUS, + NativeButton.Minus, data, position ) @@ -443,7 +441,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.facebutton_home, R.drawable.facebutton_home_depressed, - ButtonType.BUTTON_HOME, + NativeButton.Home, data, position ) @@ -457,7 +455,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.facebutton_screenshot, R.drawable.facebutton_screenshot_depressed, - ButtonType.BUTTON_CAPTURE, + NativeButton.Capture, data, position ) @@ -471,7 +469,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.l_shoulder, R.drawable.l_shoulder_depressed, - ButtonType.TRIGGER_L, + NativeButton.L, data, position ) @@ -485,7 +483,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.r_shoulder, R.drawable.r_shoulder_depressed, - ButtonType.TRIGGER_R, + NativeButton.R, data, position ) @@ -499,7 +497,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.zl_trigger, R.drawable.zl_trigger_depressed, - ButtonType.TRIGGER_ZL, + NativeButton.ZL, data, position ) @@ -513,7 +511,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.zr_trigger, R.drawable.zr_trigger_depressed, - ButtonType.TRIGGER_ZR, + NativeButton.ZR, data, position ) @@ -527,7 +525,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.button_l3, R.drawable.button_l3_depressed, - ButtonType.STICK_L, + NativeButton.LStick, data, position ) @@ -541,7 +539,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize, R.drawable.button_r3, R.drawable.button_r3_depressed, - ButtonType.STICK_R, + NativeButton.RStick, data, position ) @@ -556,8 +554,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.joystick_range, R.drawable.joystick, R.drawable.joystick_depressed, - StickType.STICK_L, - ButtonType.STICK_L, + NativeAnalog.LStick, + NativeButton.LStick, data, position ) @@ -572,8 +570,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.joystick_range, R.drawable.joystick, R.drawable.joystick_depressed, - StickType.STICK_R, - ButtonType.STICK_R, + NativeAnalog.RStick, + NativeButton.RStick, data, position ) @@ -665,7 +663,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : val overlayControlData = NativeConfig.getOverlayControlData() overlayControlData.forEach { - it.enabled = OverlayControl.from(it.id)?.defaultVisibility == false + it.enabled = OverlayControl.from(it.id)?.defaultVisibility == true } NativeConfig.setOverlayControlData(overlayControlData) @@ -835,7 +833,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : windowSize: Pair<Point, Point>, defaultResId: Int, pressedResId: Int, - buttonId: Int, + button: NativeButton, overlayControlData: OverlayControlData, position: Pair<Double, Double> ): InputOverlayDrawableButton { @@ -869,7 +867,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : res, defaultStateBitmap, pressedStateBitmap, - buttonId, + button, overlayControlData ) @@ -940,11 +938,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : res, defaultStateBitmap, pressedOneDirectionStateBitmap, - pressedTwoDirectionsStateBitmap, - ButtonType.DPAD_UP, - ButtonType.DPAD_DOWN, - ButtonType.DPAD_LEFT, - ButtonType.DPAD_RIGHT + pressedTwoDirectionsStateBitmap ) // Get the minimum and maximum coordinates of the screen where the button can be placed. @@ -993,8 +987,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : resOuter: Int, defaultResInner: Int, pressedResInner: Int, - joystick: Int, - buttonId: Int, + joystick: NativeAnalog, + button: NativeButton, overlayControlData: OverlayControlData, position: Pair<Double, Double> ): InputOverlayDrawableJoystick { @@ -1042,7 +1036,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : outerRect, innerRect, joystick, - buttonId, + button, overlayControlData.id ) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt index b14a4f96e..fee3d04ee 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt @@ -9,7 +9,8 @@ import android.graphics.Canvas import android.graphics.Rect import android.graphics.drawable.BitmapDrawable import android.view.MotionEvent -import org.yuzu.yuzu_emu.NativeLibrary.ButtonState +import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState +import org.yuzu.yuzu_emu.features.input.model.NativeButton import org.yuzu.yuzu_emu.overlay.model.OverlayControlData /** @@ -19,13 +20,13 @@ import org.yuzu.yuzu_emu.overlay.model.OverlayControlData * @param res [Resources] instance. * @param defaultStateBitmap [Bitmap] to use with the default state Drawable. * @param pressedStateBitmap [Bitmap] to use with the pressed state Drawable. - * @param buttonId Identifier for this type of button. + * @param button [NativeButton] for this type of button. */ class InputOverlayDrawableButton( res: Resources, defaultStateBitmap: Bitmap, pressedStateBitmap: Bitmap, - val buttonId: Int, + val button: NativeButton, val overlayControlData: OverlayControlData ) { // The ID value what motion event is tracking diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt index 8aef6f5a5..0cb6ff244 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt @@ -9,7 +9,8 @@ import android.graphics.Canvas import android.graphics.Rect import android.graphics.drawable.BitmapDrawable import android.view.MotionEvent -import org.yuzu.yuzu_emu.NativeLibrary.ButtonState +import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState +import org.yuzu.yuzu_emu.features.input.model.NativeButton /** * Custom [BitmapDrawable] that is capable @@ -19,20 +20,12 @@ import org.yuzu.yuzu_emu.NativeLibrary.ButtonState * @param defaultStateBitmap [Bitmap] of the default state. * @param pressedOneDirectionStateBitmap [Bitmap] of the pressed state in one direction. * @param pressedTwoDirectionsStateBitmap [Bitmap] of the pressed state in two direction. - * @param buttonUp Identifier for the up button. - * @param buttonDown Identifier for the down button. - * @param buttonLeft Identifier for the left button. - * @param buttonRight Identifier for the right button. */ class InputOverlayDrawableDpad( res: Resources, defaultStateBitmap: Bitmap, pressedOneDirectionStateBitmap: Bitmap, - pressedTwoDirectionsStateBitmap: Bitmap, - buttonUp: Int, - buttonDown: Int, - buttonLeft: Int, - buttonRight: Int + pressedTwoDirectionsStateBitmap: Bitmap ) { /** * Gets one of the InputOverlayDrawableDpad's button IDs. @@ -40,10 +33,10 @@ class InputOverlayDrawableDpad( * @return the requested InputOverlayDrawableDpad's button ID. */ // The ID identifying what type of button this Drawable represents. - val upId: Int - val downId: Int - val leftId: Int - val rightId: Int + val up = NativeButton.DUp + val down = NativeButton.DDown + val left = NativeButton.DLeft + val right = NativeButton.DRight var trackId: Int val width: Int @@ -69,10 +62,6 @@ class InputOverlayDrawableDpad( this.pressedTwoDirectionsStateBitmap = BitmapDrawable(res, pressedTwoDirectionsStateBitmap) width = this.defaultStateBitmap.intrinsicWidth height = this.defaultStateBitmap.intrinsicHeight - upId = buttonUp - downId = buttonDown - leftId = buttonLeft - rightId = buttonRight trackId = -1 } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt index 113bf7c24..4b07107fc 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt @@ -13,7 +13,9 @@ import kotlin.math.atan2 import kotlin.math.cos import kotlin.math.sin import kotlin.math.sqrt -import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState +import org.yuzu.yuzu_emu.features.input.model.NativeAnalog +import org.yuzu.yuzu_emu.features.input.model.NativeButton import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting /** @@ -26,8 +28,8 @@ import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting * @param bitmapInnerPressed [Bitmap] which represents the pressed inner movable part of the joystick. * @param rectOuter [Rect] which represents the outer joystick bounds. * @param rectInner [Rect] which represents the inner joystick bounds. - * @param joystickId The ID value what type of joystick this Drawable represents. - * @param buttonId The ID value what type of button this Drawable represents. + * @param joystick The [NativeAnalog] this Drawable represents. + * @param button The [NativeButton] this Drawable represents. */ class InputOverlayDrawableJoystick( res: Resources, @@ -36,8 +38,8 @@ class InputOverlayDrawableJoystick( bitmapInnerPressed: Bitmap, rectOuter: Rect, rectInner: Rect, - val joystickId: Int, - val buttonId: Int, + val joystick: NativeAnalog, + val button: NativeButton, val prefId: String ) { // The ID value what motion event is tracking @@ -69,8 +71,7 @@ class InputOverlayDrawableJoystick( // TODO: Add button support val buttonStatus: Int - get() = - NativeLibrary.ButtonState.RELEASED + get() = ButtonState.RELEASED var bounds: Rect get() = outerBitmap.bounds set(bounds) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt index 23ca49b53..fadb20e39 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt @@ -3,7 +3,6 @@ package org.yuzu.yuzu_emu.ui -import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -14,19 +13,16 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.color.MaterialColors -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.adapters.GameAdapter import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.HomeViewModel +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins +import org.yuzu.yuzu_emu.utils.collect class GamesFragment : Fragment() { private var _binding: FragmentGamesBinding? = null @@ -44,8 +40,6 @@ class GamesFragment : Fragment() { return binding.root } - // This is using the correct scope, lint is just acting up - @SuppressLint("UnsafeRepeatOnLifecycleDetector") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setNavigationVisibility(visible = true, animated = true) @@ -88,49 +82,28 @@ class GamesFragment : Fragment() { } } - viewLifecycleOwner.lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { - gamesViewModel.isReloading.collect { - binding.swipeRefresh.isRefreshing = it - if (gamesViewModel.games.value.isEmpty() && !it) { - binding.noticeText.visibility = View.VISIBLE - } else { - binding.noticeText.visibility = View.INVISIBLE - } - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { - gamesViewModel.games.collectLatest { - (binding.gridGames.adapter as GameAdapter).submitList(it) - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { - gamesViewModel.shouldSwapData.collect { - if (it) { - (binding.gridGames.adapter as GameAdapter).submitList( - gamesViewModel.games.value - ) - gamesViewModel.setShouldSwapData(false) - } - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { - gamesViewModel.shouldScrollToTop.collect { - if (it) { - scrollToTop() - gamesViewModel.setShouldScrollToTop(false) - } - } - } + gamesViewModel.isReloading.collect(viewLifecycleOwner) { + binding.swipeRefresh.isRefreshing = it + binding.noticeText.setVisible( + visible = gamesViewModel.games.value.isEmpty() && !it, + gone = false + ) + } + gamesViewModel.games.collect(viewLifecycleOwner) { + (binding.gridGames.adapter as GameAdapter).submitList(it) + } + gamesViewModel.shouldSwapData.collect( + viewLifecycleOwner, + resetState = { gamesViewModel.setShouldSwapData(false) } + ) { + if (it) { + (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value) } } + gamesViewModel.shouldScrollToTop.collect( + viewLifecycleOwner, + resetState = { gamesViewModel.setShouldScrollToTop(false) } + ) { if (it) scrollToTop() } setInsets() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index 4df4ac4c6..757463a0b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -19,9 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.setupWithNavController @@ -30,7 +27,6 @@ import com.google.android.material.color.MaterialColors import com.google.android.material.navigation.NavigationBarView import java.io.File import java.io.FilenameFilter -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R @@ -47,6 +43,7 @@ import org.yuzu.yuzu_emu.model.InstallResult import org.yuzu.yuzu_emu.model.TaskState import org.yuzu.yuzu_emu.model.TaskViewModel import org.yuzu.yuzu_emu.utils.* +import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import java.io.BufferedInputStream import java.io.BufferedOutputStream import java.util.zip.ZipEntry @@ -139,42 +136,23 @@ class MainActivity : AppCompatActivity(), ThemeProvider { // Prevents navigation from being drawn for a short time on recreation if set to hidden if (!homeViewModel.navigationVisible.value.first) { - binding.navigationView.visibility = View.INVISIBLE - binding.statusBarShade.visibility = View.INVISIBLE + binding.navigationView.setVisible(visible = false, gone = false) + binding.statusBarShade.setVisible(visible = false, gone = false) } - lifecycleScope.apply { - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - homeViewModel.navigationVisible.collect { showNavigation(it.first, it.second) } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - homeViewModel.contentToInstall.collect { - if (it != null) { - installContent(it) - homeViewModel.setContentToInstall(null) - } - } - } - } - launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - homeViewModel.checkKeys.collect { - if (it) { - checkKeys() - homeViewModel.setCheckKeys(false) - } - } - } + homeViewModel.navigationVisible.collect(this) { showNavigation(it.first, it.second) } + homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) } + homeViewModel.contentToInstall.collect( + this, + resetState = { homeViewModel.setContentToInstall(null) } + ) { + if (it != null) { + installContent(it) } } + homeViewModel.checkKeys.collect(this, resetState = { homeViewModel.setCheckKeys(false) }) { + if (it) checkKeys() + } setInsets() } @@ -214,18 +192,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider { private fun showNavigation(visible: Boolean, animated: Boolean) { if (!animated) { - if (visible) { - binding.navigationView.visibility = View.VISIBLE - } else { - binding.navigationView.visibility = View.INVISIBLE - } + binding.navigationView.setVisible(visible) return } val smallLayout = resources.getBoolean(R.bool.small_layout) binding.navigationView.animate().apply { if (visible) { - binding.navigationView.visibility = View.VISIBLE + binding.navigationView.setVisible(true) duration = 300 interpolator = PathInterpolator(0.05f, 0.7f, 0.1f, 1f) @@ -264,7 +238,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } }.withEndAction { if (!visible) { - binding.navigationView.visibility = View.INVISIBLE + binding.navigationView.setVisible(visible = false, gone = false) } }.start() } @@ -272,7 +246,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { private fun showStatusBarShade(visible: Boolean) { binding.statusBarShade.animate().apply { if (visible) { - binding.statusBarShade.visibility = View.VISIBLE + binding.statusBarShade.setVisible(true) binding.statusBarShade.translationY = binding.statusBarShade.height.toFloat() * -2 duration = 300 translationY(0f) @@ -284,7 +258,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } }.withEndAction { if (!visible) { - binding.statusBarShade.visibility = View.INVISIBLE + binding.statusBarShade.setVisible(visible = false, gone = false) } }.start() } @@ -524,7 +498,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider { this@MainActivity, titleId = R.string.content_install_notice, descriptionId = R.string.content_install_notice_description, - positiveAction = { homeViewModel.setContentToInstall(documents) } + positiveAction = { homeViewModel.setContentToInstall(documents) }, + negativeAction = {} ) } }.show(supportFragmentManager, ProgressDialogFragment.TAG) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt index e63382e1d..2c7356e6a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt @@ -6,439 +6,89 @@ package org.yuzu.yuzu_emu.utils import android.view.InputDevice import android.view.KeyEvent import android.view.MotionEvent -import kotlin.math.sqrt -import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.features.input.NativeInput +import org.yuzu.yuzu_emu.features.input.YuzuInputOverlayDevice +import org.yuzu.yuzu_emu.features.input.YuzuPhysicalDevice object InputHandler { - private var controllerIds = getGameControllerIds() - - fun initialize() { - // Connect first controller - NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device)) - } - - fun updateControllerIds() { - controllerIds = getGameControllerIds() - } + var androidControllers = mapOf<Int, YuzuPhysicalDevice>() + var registeredControllers = mutableListOf<ParamPackage>() fun dispatchKeyEvent(event: KeyEvent): Boolean { - val button: Int = when (event.device.vendorId) { - 0x045E -> getInputXboxButtonKey(event.keyCode) - 0x054C -> getInputDS5ButtonKey(event.keyCode) - 0x057E -> getInputJoyconButtonKey(event.keyCode) - 0x1532 -> getInputRazerButtonKey(event.keyCode) - 0x3537 -> getInputRedmagicButtonKey(event.keyCode) - 0x358A -> getInputBackboneLabsButtonKey(event.keyCode) - else -> getInputGenericButtonKey(event.keyCode) - } - val action = when (event.action) { - KeyEvent.ACTION_DOWN -> NativeLibrary.ButtonState.PRESSED - KeyEvent.ACTION_UP -> NativeLibrary.ButtonState.RELEASED + KeyEvent.ACTION_DOWN -> NativeInput.ButtonState.PRESSED + KeyEvent.ACTION_UP -> NativeInput.ButtonState.RELEASED else -> return false } - // Ignore invalid buttons - if (button < 0) { - return false + var controllerData = androidControllers[event.device.controllerNumber] + if (controllerData == null) { + updateControllerData() + controllerData = androidControllers[event.device.controllerNumber] ?: return false } - return NativeLibrary.onGamePadButtonEvent( - getPlayerNumber(event.device.controllerNumber, event.deviceId), - button, + NativeInput.onGamePadButtonEvent( + controllerData.getGUID(), + controllerData.getPort(), + event.keyCode, action ) + return true } fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { - val device = event.device - // Check every axis input available on the controller - for (range in device.motionRanges) { - val axis = range.axis - when (device.vendorId) { - 0x045E -> setGenericAxisInput(event, axis) - 0x054C -> setGenericAxisInput(event, axis) - 0x057E -> setJoyconAxisInput(event, axis) - 0x1532 -> setRazerAxisInput(event, axis) - else -> setGenericAxisInput(event, axis) - } + val controllerData = + androidControllers[event.device.controllerNumber] ?: return false + event.device.motionRanges.forEach { + NativeInput.onGamePadAxisEvent( + controllerData.getGUID(), + controllerData.getPort(), + it.axis, + event.getAxisValue(it.axis) + ) } - return true } - private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int { - var deviceIndex = index - if (deviceId != -1) { - deviceIndex = controllerIds[deviceId] ?: 0 - } - - // TODO: Joycons are handled as different controllers. Find a way to merge them. - return when (deviceIndex) { - 2 -> NativeLibrary.Player2Device - 3 -> NativeLibrary.Player3Device - 4 -> NativeLibrary.Player4Device - 5 -> NativeLibrary.Player5Device - 6 -> NativeLibrary.Player6Device - 7 -> NativeLibrary.Player7Device - 8 -> NativeLibrary.Player8Device - else -> if (NativeLibrary.isHandheldOnly()) { - NativeLibrary.ConsoleDevice - } else { - NativeLibrary.Player1Device - } - } - } - - private fun setStickState(playerNumber: Int, index: Int, xAxis: Float, yAxis: Float) { - // Calculate vector size - val r2 = xAxis * xAxis + yAxis * yAxis - var r = sqrt(r2.toDouble()).toFloat() - - // Adjust range of joystick - val deadzone = 0.15f - var x = xAxis - var y = yAxis - - if (r > deadzone) { - val deadzoneFactor = 1.0f / r * (r - deadzone) / (1.0f - deadzone) - x *= deadzoneFactor - y *= deadzoneFactor - r *= deadzoneFactor - } else { - x = 0.0f - y = 0.0f - } - - // Normalize joystick - if (r > 1.0f) { - x /= r - y /= r - } - - NativeLibrary.onGamePadJoystickEvent( - playerNumber, - index, - x, - -y - ) - } - - private fun getAxisToButton(axis: Float): Int { - return if (axis > 0.5f) { - NativeLibrary.ButtonState.PRESSED - } else { - NativeLibrary.ButtonState.RELEASED - } - } - - private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) { - NativeLibrary.onGamePadButtonEvent( - playerNumber, - NativeLibrary.ButtonType.DPAD_UP, - getAxisToButton(-yAxis) - ) - NativeLibrary.onGamePadButtonEvent( - playerNumber, - NativeLibrary.ButtonType.DPAD_DOWN, - getAxisToButton(yAxis) - ) - NativeLibrary.onGamePadButtonEvent( - playerNumber, - NativeLibrary.ButtonType.DPAD_LEFT, - getAxisToButton(-xAxis) - ) - NativeLibrary.onGamePadButtonEvent( - playerNumber, - NativeLibrary.ButtonType.DPAD_RIGHT, - getAxisToButton(xAxis) - ) - } - - private fun getInputDS5ButtonKey(key: Int): Int { - // The missing ds5 buttons are axis - return when (key) { - KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B - KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A - KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y - KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X - KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L - KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R - KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L - KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R - KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS - KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS - else -> -1 - } - } - - private fun getInputJoyconButtonKey(key: Int): Int { - // Joycon support is half dead. A lot of buttons can't be mapped - return when (key) { - KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B - KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A - KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X - KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y - KeyEvent.KEYCODE_DPAD_UP -> NativeLibrary.ButtonType.DPAD_UP - KeyEvent.KEYCODE_DPAD_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN - KeyEvent.KEYCODE_DPAD_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT - KeyEvent.KEYCODE_DPAD_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT - KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L - KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R - KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL - KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR - KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L - KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R - KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS - KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS - else -> -1 - } - } - - private fun getInputXboxButtonKey(key: Int): Int { - // The missing xbox buttons are axis - return when (key) { - KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A - KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_B - KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X - KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y - KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L - KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R - KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L - KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R - KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS - KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS - else -> -1 - } - } - - private fun getInputRazerButtonKey(key: Int): Int { - // The missing xbox buttons are axis - return when (key) { - KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B - KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A - KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y - KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X - KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L - KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R - KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L - KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R - KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS - KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS - else -> -1 - } - } - - private fun getInputRedmagicButtonKey(key: Int): Int { - return when (key) { - KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B - KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A - KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y - KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X - KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L - KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R - KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL - KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR - KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L - KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R - KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS - KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS - else -> -1 - } - } - - private fun getInputBackboneLabsButtonKey(key: Int): Int { - return when (key) { - KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B - KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A - KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y - KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X - KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L - KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R - KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL - KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR - KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L - KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R - KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS - KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS - else -> -1 - } - } - - private fun getInputGenericButtonKey(key: Int): Int { - return when (key) { - KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A - KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_B - KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X - KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y - KeyEvent.KEYCODE_DPAD_UP -> NativeLibrary.ButtonType.DPAD_UP - KeyEvent.KEYCODE_DPAD_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN - KeyEvent.KEYCODE_DPAD_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT - KeyEvent.KEYCODE_DPAD_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT - KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L - KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R - KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL - KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR - KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L - KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R - KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS - KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS - else -> -1 - } - } - - private fun setGenericAxisInput(event: MotionEvent, axis: Int) { - val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId) - - when (axis) { - MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> - setStickState( - playerNumber, - NativeLibrary.StickType.STICK_L, - event.getAxisValue(MotionEvent.AXIS_X), - event.getAxisValue(MotionEvent.AXIS_Y) - ) - MotionEvent.AXIS_RX, MotionEvent.AXIS_RY -> - setStickState( - playerNumber, - NativeLibrary.StickType.STICK_R, - event.getAxisValue(MotionEvent.AXIS_RX), - event.getAxisValue(MotionEvent.AXIS_RY) - ) - MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ -> - setStickState( - playerNumber, - NativeLibrary.StickType.STICK_R, - event.getAxisValue(MotionEvent.AXIS_Z), - event.getAxisValue(MotionEvent.AXIS_RZ) - ) - MotionEvent.AXIS_LTRIGGER -> - NativeLibrary.onGamePadButtonEvent( - playerNumber, - NativeLibrary.ButtonType.TRIGGER_ZL, - getAxisToButton(event.getAxisValue(MotionEvent.AXIS_LTRIGGER)) - ) - MotionEvent.AXIS_BRAKE -> - NativeLibrary.onGamePadButtonEvent( - playerNumber, - NativeLibrary.ButtonType.TRIGGER_ZL, - getAxisToButton(event.getAxisValue(MotionEvent.AXIS_BRAKE)) - ) - MotionEvent.AXIS_RTRIGGER -> - NativeLibrary.onGamePadButtonEvent( - playerNumber, - NativeLibrary.ButtonType.TRIGGER_ZR, - getAxisToButton(event.getAxisValue(MotionEvent.AXIS_RTRIGGER)) - ) - MotionEvent.AXIS_GAS -> - NativeLibrary.onGamePadButtonEvent( - playerNumber, - NativeLibrary.ButtonType.TRIGGER_ZR, - getAxisToButton(event.getAxisValue(MotionEvent.AXIS_GAS)) - ) - MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y -> - setAxisDpadState( - playerNumber, - event.getAxisValue(MotionEvent.AXIS_HAT_X), - event.getAxisValue(MotionEvent.AXIS_HAT_Y) - ) - } - } - - private fun setJoyconAxisInput(event: MotionEvent, axis: Int) { - // Joycon support is half dead. Right joystick doesn't work - val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId) - - when (axis) { - MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> - setStickState( - playerNumber, - NativeLibrary.StickType.STICK_L, - event.getAxisValue(MotionEvent.AXIS_X), - event.getAxisValue(MotionEvent.AXIS_Y) - ) - MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ -> - setStickState( - playerNumber, - NativeLibrary.StickType.STICK_R, - event.getAxisValue(MotionEvent.AXIS_Z), - event.getAxisValue(MotionEvent.AXIS_RZ) - ) - MotionEvent.AXIS_RX, MotionEvent.AXIS_RY -> - setStickState( - playerNumber, - NativeLibrary.StickType.STICK_R, - event.getAxisValue(MotionEvent.AXIS_RX), - event.getAxisValue(MotionEvent.AXIS_RY) - ) - } - } - - private fun setRazerAxisInput(event: MotionEvent, axis: Int) { - val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId) - - when (axis) { - MotionEvent.AXIS_X, MotionEvent.AXIS_Y -> - setStickState( - playerNumber, - NativeLibrary.StickType.STICK_L, - event.getAxisValue(MotionEvent.AXIS_X), - event.getAxisValue(MotionEvent.AXIS_Y) - ) - MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ -> - setStickState( - playerNumber, - NativeLibrary.StickType.STICK_R, - event.getAxisValue(MotionEvent.AXIS_Z), - event.getAxisValue(MotionEvent.AXIS_RZ) - ) - MotionEvent.AXIS_BRAKE -> - NativeLibrary.onGamePadButtonEvent( - playerNumber, - NativeLibrary.ButtonType.TRIGGER_ZL, - getAxisToButton(event.getAxisValue(MotionEvent.AXIS_BRAKE)) - ) - MotionEvent.AXIS_GAS -> - NativeLibrary.onGamePadButtonEvent( - playerNumber, - NativeLibrary.ButtonType.TRIGGER_ZR, - getAxisToButton(event.getAxisValue(MotionEvent.AXIS_GAS)) - ) - MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y -> - setAxisDpadState( - playerNumber, - event.getAxisValue(MotionEvent.AXIS_HAT_X), - event.getAxisValue(MotionEvent.AXIS_HAT_Y) - ) - } - } - - fun getGameControllerIds(): Map<Int, Int> { - val gameControllerDeviceIds = mutableMapOf<Int, Int>() + fun getDevices(): Map<Int, YuzuPhysicalDevice> { + val gameControllerDeviceIds = mutableMapOf<Int, YuzuPhysicalDevice>() val deviceIds = InputDevice.getDeviceIds() - var controllerSlot = 1 + var port = 0 + val inputSettings = NativeConfig.getInputSettings(true) deviceIds.forEach { deviceId -> InputDevice.getDevice(deviceId)?.apply { - // Don't over-assign controllers - if (controllerSlot >= 8) { - return gameControllerDeviceIds - } - // Verify that the device has gamepad buttons, control sticks, or both. if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD || sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK ) { - // This device is a game controller. Store its device ID. - if (deviceId and id and vendorId and productId != 0) { - // Additionally filter out devices that have no ID - gameControllerDeviceIds - .takeIf { !it.contains(deviceId) } - ?.put(deviceId, controllerSlot) - controllerSlot++ + if (!gameControllerDeviceIds.contains(controllerNumber)) { + gameControllerDeviceIds[controllerNumber] = YuzuPhysicalDevice( + this, + port, + inputSettings[port].useSystemVibrator + ) } + port++ } } } return gameControllerDeviceIds } + + fun updateControllerData() { + androidControllers = getDevices() + androidControllers.forEach { + NativeInput.registerController(it.value) + } + + // Register the input overlay on a dedicated port for all player 1 vibrations + NativeInput.registerController(YuzuInputOverlayDevice(androidControllers.isEmpty(), 100)) + registeredControllers.clear() + NativeInput.getInputDevices().forEach { + registeredControllers.add(ParamPackage(it)) + } + registeredControllers.sortBy { it.get("port", 0) } + } + + fun InputDevice.getGUID(): String = String.format("%016x%016x", productId, vendorId) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt new file mode 100644 index 000000000..d5c19c681 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.utils + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch + +/** + * Collects this [Flow] with a given [LifecycleOwner]. + * @param scope [LifecycleOwner] that this [Flow] will be collected with. + * @param repeatState When to repeat collection on this [Flow]. + * @param resetState Optional lambda to reset state of an underlying [MutableStateFlow] after + * [stateCollector] has been run. + * @param stateCollector Lambda that receives new state. + */ +inline fun <reified T> Flow<T>.collect( + scope: LifecycleOwner, + repeatState: Lifecycle.State = Lifecycle.State.CREATED, + crossinline resetState: () -> Unit = {}, + crossinline stateCollector: (state: T) -> Unit +) { + scope.apply { + lifecycleScope.launch { + repeatOnLifecycle(repeatState) { + this@collect.collect { + stateCollector(it) + resetState() + } + } + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt index a4c14b3a7..7228f25d2 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt @@ -6,6 +6,8 @@ package org.yuzu.yuzu_emu.utils import org.yuzu.yuzu_emu.model.GameDir import org.yuzu.yuzu_emu.overlay.model.OverlayControlData +import org.yuzu.yuzu_emu.features.input.model.PlayerInput + object NativeConfig { /** * Loads global config. @@ -168,4 +170,17 @@ object NativeConfig { */ @Synchronized external fun setOverlayControlData(overlayControlData: Array<OverlayControlData>) + + @Synchronized + external fun getInputSettings(global: Boolean): Array<PlayerInput> + + @Synchronized + external fun setInputSettings(value: Array<PlayerInput>, global: Boolean) + + /** + * Saves control values for a specific player + * Must be used when per game config is loaded + */ + @Synchronized + external fun saveControlPlayerValues() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt index 68ed66565..331b7ddca 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt @@ -14,7 +14,7 @@ import android.os.Build import android.os.Handler import android.os.Looper import java.io.IOException -import org.yuzu.yuzu_emu.NativeLibrary +import org.yuzu.yuzu_emu.features.input.NativeInput class NfcReader(private val activity: Activity) { private var nfcAdapter: NfcAdapter? = null @@ -76,12 +76,12 @@ class NfcReader(private val activity: Activity) { amiibo.connect() val tagData = ntag215ReadAll(amiibo) ?: return - NativeLibrary.onReadNfcTag(tagData) + NativeInput.onReadNfcTag(tagData) nfcAdapter?.ignore( tag, 1000, - { NativeLibrary.onRemoveNfcTag() }, + { NativeInput.onRemoveNfcTag() }, Handler(Looper.getMainLooper()) ) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ParamPackage.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ParamPackage.kt new file mode 100644 index 000000000..83fc7da3c --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ParamPackage.kt @@ -0,0 +1,141 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.utils + +// Kotlin version of src/common/param_package.h +class ParamPackage(serialized: String = "") { + private val KEY_VALUE_SEPARATOR = ":" + private val PARAM_SEPARATOR = "," + + private val ESCAPE_CHARACTER = "$" + private val KEY_VALUE_SEPARATOR_ESCAPE = "$0" + private val PARAM_SEPARATOR_ESCAPE = "$1" + private val ESCAPE_CHARACTER_ESCAPE = "$2" + + private val EMPTY_PLACEHOLDER = "[empty]" + + val data = mutableMapOf<String, String>() + + init { + val pairs = serialized.split(PARAM_SEPARATOR) + for (pair in pairs) { + val keyValue = pair.split(KEY_VALUE_SEPARATOR).toMutableList() + if (keyValue.size != 2) { + Log.error("[ParamPackage] Invalid key pair $keyValue") + continue + } + + keyValue.forEachIndexed { i: Int, _: String -> + keyValue[i] = keyValue[i].replace(KEY_VALUE_SEPARATOR_ESCAPE, KEY_VALUE_SEPARATOR) + keyValue[i] = keyValue[i].replace(PARAM_SEPARATOR_ESCAPE, PARAM_SEPARATOR) + keyValue[i] = keyValue[i].replace(ESCAPE_CHARACTER_ESCAPE, ESCAPE_CHARACTER) + } + + set(keyValue[0], keyValue[1]) + } + } + + constructor(params: List<Pair<String, String>>) : this() { + params.forEach { + data[it.first] = it.second + } + } + + fun serialize(): String { + if (data.isEmpty()) { + return EMPTY_PLACEHOLDER + } + + val result = StringBuilder() + data.forEach { + val keyValue = mutableListOf(it.key, it.value) + keyValue.forEachIndexed { i, _ -> + keyValue[i] = keyValue[i].replace(ESCAPE_CHARACTER, ESCAPE_CHARACTER_ESCAPE) + keyValue[i] = keyValue[i].replace(PARAM_SEPARATOR, PARAM_SEPARATOR_ESCAPE) + keyValue[i] = keyValue[i].replace(KEY_VALUE_SEPARATOR, KEY_VALUE_SEPARATOR_ESCAPE) + } + result.append("${keyValue[0]}$KEY_VALUE_SEPARATOR${keyValue[1]}$PARAM_SEPARATOR") + } + return result.removeSuffix(PARAM_SEPARATOR).toString() + } + + fun get(key: String, defaultValue: String): String = + if (has(key)) { + data[key]!! + } else { + Log.debug("[ParamPackage] key $key not found") + defaultValue + } + + fun get(key: String, defaultValue: Int): Int = + if (has(key)) { + try { + data[key]!!.toInt() + } catch (e: NumberFormatException) { + Log.debug("[ParamPackage] failed to convert ${data[key]!!} to int") + defaultValue + } + } else { + Log.debug("[ParamPackage] key $key not found") + defaultValue + } + + private fun Int.toBoolean(): Boolean = + if (this == 1) { + true + } else if (this == 0) { + false + } else { + throw Exception("Tried to convert a value to a boolean that was not 0 or 1!") + } + + fun get(key: String, defaultValue: Boolean): Boolean = + if (has(key)) { + try { + get(key, if (defaultValue) 1 else 0).toBoolean() + } catch (e: Exception) { + Log.debug("[ParamPackage] failed to convert ${data[key]!!} to boolean") + defaultValue + } + } else { + Log.debug("[ParamPackage] key $key not found") + defaultValue + } + + fun get(key: String, defaultValue: Float): Float = + if (has(key)) { + try { + data[key]!!.toFloat() + } catch (e: NumberFormatException) { + Log.debug("[ParamPackage] failed to convert ${data[key]!!} to float") + defaultValue + } + } else { + Log.debug("[ParamPackage] key $key not found") + defaultValue + } + + fun set(key: String, value: String) { + data[key] = value + } + + fun set(key: String, value: Int) { + data[key] = value.toString() + } + + fun Boolean.toInt(): Int = if (this) 1 else 0 + fun set(key: String, value: Boolean) { + data[key] = value.toInt().toString() + } + + fun set(key: String, value: Float) { + data[key] = value.toString() + } + + fun has(key: String): Boolean = data.containsKey(key) + + fun erase(key: String) = data.remove(key) + + fun clear() = data.clear() +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt index ffbfa9337..244091aec 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt @@ -3,8 +3,10 @@ package org.yuzu.yuzu_emu.utils +import android.text.TextUtils import android.view.View import android.view.ViewGroup +import android.widget.TextView object ViewUtils { fun showView(view: View, length: Long = 300) { @@ -57,4 +59,35 @@ object ViewUtils { } this.layoutParams = layoutParams } + + /** + * Shows or hides a view. + * @param visible Whether a view will be made View.VISIBLE or View.INVISIBLE/GONE. + * @param gone Optional parameter for hiding a view. Uses View.GONE if true and View.INVISIBLE otherwise. + */ + fun View.setVisible(visible: Boolean, gone: Boolean = true) { + visibility = if (visible) { + View.VISIBLE + } else { + if (gone) { + View.GONE + } else { + View.INVISIBLE + } + } + } + + /** + * Starts a marquee on some text. + * @param delay Optional parameter for changing the start delay. 3 seconds of delay by default. + */ + fun TextView.marquee(delay: Long = 3000) { + ellipsize = null + marqueeRepeatLimit = -1 + isSingleLine = true + postDelayed({ + ellipsize = TextUtils.TruncateAt.MARQUEE + isSelected = true + }, delay) + } } diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 20b319c12..ec8ae5c57 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -12,6 +12,7 @@ add_library(yuzu-android SHARED native_log.cpp android_config.cpp android_config.h + native_input.cpp ) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp index e147560c3..a79a64afb 100644 --- a/src/android/app/src/main/jni/android_config.cpp +++ b/src/android/app/src/main/jni/android_config.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <common/logging/log.h> +#include <input_common/main.h> #include "android_config.h" #include "android_settings.h" #include "common/settings_setting.h" @@ -32,6 +34,7 @@ void AndroidConfig::ReadAndroidValues() { ReadOverlayValues(); } ReadDriverValues(); + ReadAndroidControlValues(); } void AndroidConfig::ReadAndroidUIValues() { @@ -107,6 +110,76 @@ void AndroidConfig::ReadOverlayValues() { EndGroup(); } +void AndroidConfig::ReadAndroidPlayerValues(std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix.append("player_").append(ToString(player_index)).append("_"); + } + + auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + const auto profile_name = + ReadStringSetting(std::string(player_prefix).append("profile_name")); + if (profile_name.empty()) { + // Use the global input config + player = Settings::values.players.GetValue(true)[player_index]; + player.profile_name = ""; + return; + } + } + + // Android doesn't have default options for controllers. We have the input overlay for that. + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param; + auto& player_buttons = player.buttons[i]; + + player_buttons = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param); + if (player_buttons.empty()) { + player_buttons = default_param; + } + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param; + auto& player_analogs = player.analogs[i]; + + player_analogs = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param); + if (player_analogs.empty()) { + player_analogs = default_param; + } + } + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param; + auto& player_motions = player.motions[i]; + + player_motions = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param); + if (player_motions.empty()) { + player_motions = default_param; + } + } + player.use_system_vibrator = ReadBooleanSetting( + std::string(player_prefix).append("use_system_vibrator"), player_index == 0); +} + +void AndroidConfig::ReadAndroidControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + ReadAndroidPlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + // ReadDebugControlValues(); + // ReadHidbusValues(); + + EndGroup(); +} + void AndroidConfig::SaveAndroidValues() { if (global) { SaveAndroidUIValues(); @@ -114,6 +187,7 @@ void AndroidConfig::SaveAndroidValues() { SaveOverlayValues(); } SaveDriverValues(); + SaveAndroidControlValues(); WriteToIni(); } @@ -187,6 +261,52 @@ void AndroidConfig::SaveOverlayValues() { EndGroup(); } +void AndroidConfig::SaveAndroidPlayerValues(std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix = std::string("player_").append(ToString(player_index)).append("_"); + } + + const auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig() && player.profile_name.empty()) { + // No custom profile selected + return; + } + + const std::string default_param; + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), + player.buttons[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), + player.analogs[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), + player.motions[i], std::make_optional(default_param)); + } + WriteBooleanSetting(std::string(player_prefix).append("use_system_vibrator"), + player.use_system_vibrator, std::make_optional(player_index == 0)); +} + +void AndroidConfig::SaveAndroidControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + SaveAndroidPlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + // SaveDebugControlValues(); + // SaveHidbusValues(); + + EndGroup(); +} + std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) { auto& map = Settings::values.linkage.by_category; if (map.contains(category)) { @@ -194,3 +314,24 @@ std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings:: } return AndroidSettings::values.linkage.by_category[category]; } + +void AndroidConfig::ReadAndroidControlPlayerValues(std::size_t player_index) { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + ReadPlayerValues(player_index); + ReadAndroidPlayerValues(player_index); + + EndGroup(); +} + +void AndroidConfig::SaveAndroidControlPlayerValues(std::size_t player_index) { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + LOG_DEBUG(Config, "Saving players control configuration values"); + SavePlayerValues(player_index); + SaveAndroidPlayerValues(player_index); + + EndGroup(); + + WriteToIni(); +} diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h index 693e1e3f0..28ef5d0a8 100644 --- a/src/android/app/src/main/jni/android_config.h +++ b/src/android/app/src/main/jni/android_config.h @@ -13,7 +13,12 @@ public: void ReloadAllValues() override; void SaveAllValues() override; + void ReadAndroidControlPlayerValues(std::size_t player_index); + void SaveAndroidControlPlayerValues(std::size_t player_index); + protected: + void ReadAndroidPlayerValues(std::size_t player_index); + void ReadAndroidControlValues(); void ReadAndroidValues(); void ReadAndroidUIValues(); void ReadDriverValues(); @@ -27,6 +32,8 @@ protected: void ReadUILayoutValues() override {} void ReadMultiplayerValues() override {} + void SaveAndroidPlayerValues(std::size_t player_index); + void SaveAndroidControlValues(); void SaveAndroidValues(); void SaveAndroidUIValues(); void SaveDriverValues(); 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/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp index c927cddda..06db55369 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp @@ -5,6 +5,7 @@ #include "common/android/id_cache.h" #include "common/logging/log.h" +#include "input_common/drivers/android.h" #include "input_common/drivers/touch_screen.h" #include "input_common/drivers/virtual_amiibo.h" #include "input_common/drivers/virtual_gamepad.h" @@ -24,39 +25,18 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { void EmuWindow_Android::OnTouchPressed(int id, float x, float y) { const auto [touch_x, touch_y] = MapToTouchScreen(x, y); - m_input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id); + EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchPressed(touch_x, + touch_y, id); } void EmuWindow_Android::OnTouchMoved(int id, float x, float y) { const auto [touch_x, touch_y] = MapToTouchScreen(x, y); - m_input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id); + EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchMoved(touch_x, + touch_y, id); } void EmuWindow_Android::OnTouchReleased(int id) { - m_input_subsystem->GetTouchScreen()->TouchReleased(id); -} - -void EmuWindow_Android::OnGamepadButtonEvent(int player_index, int button_id, bool pressed) { - m_input_subsystem->GetVirtualGamepad()->SetButtonState(player_index, button_id, pressed); -} - -void EmuWindow_Android::OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y) { - m_input_subsystem->GetVirtualGamepad()->SetStickPosition(player_index, stick_id, x, y); -} - -void EmuWindow_Android::OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x, - float gyro_y, float gyro_z, float accel_x, - float accel_y, float accel_z) { - m_input_subsystem->GetVirtualGamepad()->SetMotionState( - player_index, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); -} - -void EmuWindow_Android::OnReadNfcTag(std::span<u8> data) { - m_input_subsystem->GetVirtualAmiibo()->LoadAmiibo(data); -} - -void EmuWindow_Android::OnRemoveNfcTag() { - m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo(); + EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchReleased(id); } void EmuWindow_Android::OnFrameDisplayed() { @@ -67,10 +47,9 @@ void EmuWindow_Android::OnFrameDisplayed() { } } -EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, - ANativeWindow* surface, +EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface, std::shared_ptr<Common::DynamicLibrary> driver_library) - : m_input_subsystem{input_subsystem}, m_driver_library{driver_library} { + : m_driver_library{driver_library} { LOG_INFO(Frontend, "initializing"); if (!surface) { @@ -80,10 +59,4 @@ EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsyste OnSurfaceChanged(surface); window_info.type = Core::Frontend::WindowSystemType::Android; - - m_input_subsystem->Initialize(); -} - -EmuWindow_Android::~EmuWindow_Android() { - m_input_subsystem->Shutdown(); } diff --git a/src/android/app/src/main/jni/emu_window/emu_window.h b/src/android/app/src/main/jni/emu_window/emu_window.h index a34a0e479..d7b5fc6da 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.h +++ b/src/android/app/src/main/jni/emu_window/emu_window.h @@ -30,22 +30,17 @@ private: class EmuWindow_Android final : public Core::Frontend::EmuWindow { public: - EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, ANativeWindow* surface, + EmuWindow_Android(ANativeWindow* surface, std::shared_ptr<Common::DynamicLibrary> driver_library); - ~EmuWindow_Android(); + ~EmuWindow_Android() = default; void OnSurfaceChanged(ANativeWindow* surface); + void OnFrameDisplayed() override; + void OnTouchPressed(int id, float x, float y); void OnTouchMoved(int id, float x, float y); void OnTouchReleased(int id); - void OnGamepadButtonEvent(int player_index, int button_id, bool pressed); - void OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y); - void OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x, float gyro_y, - float gyro_z, float accel_x, float accel_y, float accel_z); - void OnReadNfcTag(std::span<u8> data); - void OnRemoveNfcTag(); - void OnFrameDisplayed() override; std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override { return {std::make_unique<GraphicsContext_Android>(m_driver_library)}; @@ -55,8 +50,6 @@ public: }; private: - InputCommon::InputSubsystem* m_input_subsystem{}; - float m_window_width{}; float m_window_height{}; diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 4acc60956..4ea82e217 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -49,9 +49,7 @@ #include "core/frontend/applets/profile_select.h" #include "core/frontend/applets/software_keyboard.h" #include "core/frontend/applets/web_browser.h" -#include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_manager.h" -#include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" @@ -90,6 +88,10 @@ FileSys::ManualContentProvider* EmulationSession::GetContentProvider() { return m_manual_provider.get(); } +InputCommon::InputSubsystem& EmulationSession::GetInputSubsystem() { + return m_input_subsystem; +} + const EmuWindow_Android& EmulationSession::Window() const { return *m_window; } @@ -200,6 +202,8 @@ void EmulationSession::InitializeSystem(bool reload) { Common::Log::Initialize(); Common::Log::SetColorConsoleBackendEnabled(true); Common::Log::Start(); + + m_input_subsystem.Initialize(); } // Initialize filesystem. @@ -224,8 +228,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string std::scoped_lock lock(m_mutex); // Create the render window. - m_window = - std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library); + m_window = std::make_unique<EmuWindow_Android>(m_native_window, m_vulkan_library); // Initialize system. jauto android_keyboard = std::make_unique<Common::Android::SoftwareKeyboard::AndroidKeyboard>(); @@ -357,60 +360,6 @@ void EmulationSession::RunEmulation() { m_applet_id = static_cast<int>(Service::AM::AppletId::Application); } -bool EmulationSession::IsHandheldOnly() { - jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); - - if (npad_style_set.fullkey == 1) { - return false; - } - - if (npad_style_set.handheld == 0) { - return false; - } - - return !Settings::IsDockedMode(); -} - -void EmulationSession::SetDeviceType([[maybe_unused]] int index, int type) { - jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); -} - -void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) { - jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - - // Ensure that player1 is configured correctly and handheld disconnected - if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { - jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); - - if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { - handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); - handheld->Disconnect(); - } - } - - // Ensure that handheld is configured correctly and player 1 disconnected - if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { - jauto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); - - if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { - player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); - player1->Disconnect(); - } - } - - if (!controller->IsConnected()) { - controller->Connect(); - } -} - -void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) { - jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); - controller->Disconnect(); -} - Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { return m_software_keyboard; } @@ -455,7 +404,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath, const size_t program_index, const bool frontend_initiated) { MicroProfileOnThreadCreate("EmuThread"); - SCOPE_EXIT({ MicroProfileShutdown(); }); + SCOPE_EXIT { + MicroProfileShutdown(); + }; LOG_INFO(Frontend, "starting"); @@ -464,7 +415,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath, return Core::SystemResultStatus::ErrorLoader; } - SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); + SCOPE_EXIT { + EmulationSession::GetInstance().ShutdownEmulation(); + }; jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index, frontend_initiated); @@ -576,14 +529,14 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo( nullptr, nullptr, file_redirect_dir_, nullptr); auto driver_library = std::make_shared<Common::DynamicLibrary>(handle); InputCommon::InputSubsystem input_subsystem; - auto m_window = std::make_unique<EmuWindow_Android>( - &input_subsystem, ANativeWindow_fromSurface(env, j_surf), driver_library); + auto window = + std::make_unique<EmuWindow_Android>(ANativeWindow_fromSurface(env, j_surf), driver_library); Vulkan::vk::InstanceDispatch dld; Vulkan::vk::Instance vk_instance = Vulkan::CreateInstance( *driver_library, dld, VK_API_VERSION_1_1, Core::Frontend::WindowSystemType::Android); - auto surface = Vulkan::CreateSurface(vk_instance, m_window->GetWindowInfo()); + auto surface = Vulkan::CreateSurface(vk_instance, window->GetWindowInfo()); auto device = Vulkan::CreateDevice(vk_instance, dld, *surface); @@ -624,103 +577,6 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass claz return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused()); } -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) { - return EmulationSession::GetInstance().IsHandheldOnly(); -} - -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, jclass clazz, - jint j_device, jint j_type) { - if (EmulationSession::GetInstance().IsRunning()) { - EmulationSession::GetInstance().SetDeviceType(j_device, j_type); - } - return static_cast<jboolean>(true); -} - -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(JNIEnv* env, jclass clazz, - jint j_device) { - if (EmulationSession::GetInstance().IsRunning()) { - EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); - } - return static_cast<jboolean>(true); -} - -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* env, jclass clazz, - jint j_device) { - if (EmulationSession::GetInstance().IsRunning()) { - EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device); - } - return static_cast<jboolean>(true); -} -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz, - jint j_device, jint j_button, - jint action) { - if (EmulationSession::GetInstance().IsRunning()) { - // Ensure gamepad is connected - EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); - EmulationSession::GetInstance().Window().OnGamepadButtonEvent(j_device, j_button, - action != 0); - } - return static_cast<jboolean>(true); -} - -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent(JNIEnv* env, jclass clazz, - jint j_device, jint stick_id, - jfloat x, jfloat y) { - if (EmulationSession::GetInstance().IsRunning()) { - EmulationSession::GetInstance().Window().OnGamepadJoystickEvent(j_device, stick_id, x, y); - } - return static_cast<jboolean>(true); -} - -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent( - JNIEnv* env, jclass clazz, jint j_device, jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, - jfloat gyro_z, jfloat accel_x, jfloat accel_y, jfloat accel_z) { - if (EmulationSession::GetInstance().IsRunning()) { - EmulationSession::GetInstance().Window().OnGamepadMotionEvent( - j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); - } - return static_cast<jboolean>(true); -} - -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, jclass clazz, - jbyteArray j_data) { - jboolean isCopy{false}; - std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)), - static_cast<size_t>(env->GetArrayLength(j_data))); - - if (EmulationSession::GetInstance().IsRunning()) { - EmulationSession::GetInstance().Window().OnReadNfcTag(data); - } - return static_cast<jboolean>(true); -} - -jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, jclass clazz) { - if (EmulationSession::GetInstance().IsRunning()) { - EmulationSession::GetInstance().Window().OnRemoveNfcTag(); - } - return static_cast<jboolean>(true); -} - -void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed(JNIEnv* env, jclass clazz, jint id, - jfloat x, jfloat y) { - if (EmulationSession::GetInstance().IsRunning()) { - EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y); - } -} - -void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, jint id, - jfloat x, jfloat y) { - if (EmulationSession::GetInstance().IsRunning()) { - EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y); - } -} - -void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass clazz, jint id) { - if (EmulationSession::GetInstance().IsRunning()) { - EmulationSession::GetInstance().Window().OnTouchReleased(id); - } -} - void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, jboolean reload) { // Initialize the emulated system. @@ -761,6 +617,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) { EmulationSession::GetInstance().System().ApplySettings(); + EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices(); } void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj) { diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index 47936e305..6a4551ada 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h @@ -23,6 +23,7 @@ public: const Core::System& System() const; Core::System& System(); FileSys::ManualContentProvider* GetContentProvider(); + InputCommon::InputSubsystem& GetInputSubsystem(); const EmuWindow_Android& Window() const; EmuWindow_Android& Window(); @@ -50,10 +51,6 @@ public: const std::size_t program_index, const bool frontend_initiated); - bool IsHandheldOnly(); - void SetDeviceType([[maybe_unused]] int index, int type); - void OnGamepadConnectEvent([[maybe_unused]] int index); - void OnGamepadDisconnectEvent([[maybe_unused]] int index); Common::Android::SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); static void OnEmulationStarted(); diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp index 8ae10fbc7..0b26280c6 100644 --- a/src/android/app/src/main/jni/native_config.cpp +++ b/src/android/app/src/main/jni/native_config.cpp @@ -3,7 +3,6 @@ #include <string> -#include <common/fs/fs_util.h> #include <jni.h> #include "android_config.h" @@ -425,4 +424,120 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData( } } +jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInputSettings(JNIEnv* env, jobject obj, + jboolean j_global) { + Settings::values.players.SetGlobal(static_cast<bool>(j_global)); + auto& players = Settings::values.players.GetValue(); + jobjectArray j_input_settings = + env->NewObjectArray(players.size(), Common::Android::GetPlayerInputClass(), nullptr); + for (size_t i = 0; i < players.size(); ++i) { + auto j_connected = static_cast<jboolean>(players[i].connected); + + jobjectArray j_buttons = env->NewObjectArray( + players[i].buttons.size(), Common::Android::GetStringClass(), env->NewStringUTF("")); + for (size_t j = 0; j < players[i].buttons.size(); ++j) { + env->SetObjectArrayElement(j_buttons, j, + Common::Android::ToJString(env, players[i].buttons[j])); + } + jobjectArray j_analogs = env->NewObjectArray( + players[i].analogs.size(), Common::Android::GetStringClass(), env->NewStringUTF("")); + for (size_t j = 0; j < players[i].analogs.size(); ++j) { + env->SetObjectArrayElement(j_analogs, j, + Common::Android::ToJString(env, players[i].analogs[j])); + } + jobjectArray j_motions = env->NewObjectArray( + players[i].motions.size(), Common::Android::GetStringClass(), env->NewStringUTF("")); + for (size_t j = 0; j < players[i].motions.size(); ++j) { + env->SetObjectArrayElement(j_motions, j, + Common::Android::ToJString(env, players[i].motions[j])); + } + + auto j_vibration_enabled = static_cast<jboolean>(players[i].vibration_enabled); + auto j_vibration_strength = static_cast<jint>(players[i].vibration_strength); + + auto j_body_color_left = static_cast<jlong>(players[i].body_color_left); + auto j_body_color_right = static_cast<jlong>(players[i].body_color_right); + auto j_button_color_left = static_cast<jlong>(players[i].button_color_left); + auto j_button_color_right = static_cast<jlong>(players[i].button_color_right); + + auto j_profile_name = Common::Android::ToJString(env, players[i].profile_name); + + auto j_use_system_vibrator = players[i].use_system_vibrator; + + jobject playerInput = env->NewObject( + Common::Android::GetPlayerInputClass(), Common::Android::GetPlayerInputConstructor(), + j_connected, j_buttons, j_analogs, j_motions, j_vibration_enabled, j_vibration_strength, + j_body_color_left, j_body_color_right, j_button_color_left, j_button_color_right, + j_profile_name, j_use_system_vibrator); + env->SetObjectArrayElement(j_input_settings, i, playerInput); + } + return j_input_settings; +} + +void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setInputSettings(JNIEnv* env, jobject obj, + jobjectArray j_value, + jboolean j_global) { + auto& players = Settings::values.players.GetValue(static_cast<bool>(j_global)); + int playersSize = env->GetArrayLength(j_value); + for (int i = 0; i < playersSize; ++i) { + jobject jplayer = env->GetObjectArrayElement(j_value, i); + + players[i].connected = static_cast<bool>( + env->GetBooleanField(jplayer, Common::Android::GetPlayerInputConnectedField())); + + auto j_buttons_array = static_cast<jobjectArray>( + env->GetObjectField(jplayer, Common::Android::GetPlayerInputButtonsField())); + int buttons_size = env->GetArrayLength(j_buttons_array); + for (int j = 0; j < buttons_size; ++j) { + auto button = static_cast<jstring>(env->GetObjectArrayElement(j_buttons_array, j)); + players[i].buttons[j] = Common::Android::GetJString(env, button); + } + auto j_analogs_array = static_cast<jobjectArray>( + env->GetObjectField(jplayer, Common::Android::GetPlayerInputAnalogsField())); + int analogs_size = env->GetArrayLength(j_analogs_array); + for (int j = 0; j < analogs_size; ++j) { + auto analog = static_cast<jstring>(env->GetObjectArrayElement(j_analogs_array, j)); + players[i].analogs[j] = Common::Android::GetJString(env, analog); + } + auto j_motions_array = static_cast<jobjectArray>( + env->GetObjectField(jplayer, Common::Android::GetPlayerInputMotionsField())); + int motions_size = env->GetArrayLength(j_motions_array); + for (int j = 0; j < motions_size; ++j) { + auto motion = static_cast<jstring>(env->GetObjectArrayElement(j_motions_array, j)); + players[i].motions[j] = Common::Android::GetJString(env, motion); + } + + players[i].vibration_enabled = static_cast<bool>( + env->GetBooleanField(jplayer, Common::Android::GetPlayerInputVibrationEnabledField())); + players[i].vibration_strength = static_cast<int>( + env->GetIntField(jplayer, Common::Android::GetPlayerInputVibrationStrengthField())); + + players[i].body_color_left = static_cast<u32>( + env->GetLongField(jplayer, Common::Android::GetPlayerInputBodyColorLeftField())); + players[i].body_color_right = static_cast<u32>( + env->GetLongField(jplayer, Common::Android::GetPlayerInputBodyColorRightField())); + players[i].button_color_left = static_cast<u32>( + env->GetLongField(jplayer, Common::Android::GetPlayerInputButtonColorLeftField())); + players[i].button_color_right = static_cast<u32>( + env->GetLongField(jplayer, Common::Android::GetPlayerInputButtonColorRightField())); + + auto profileName = static_cast<jstring>( + env->GetObjectField(jplayer, Common::Android::GetPlayerInputProfileNameField())); + players[i].profile_name = Common::Android::GetJString(env, profileName); + + players[i].use_system_vibrator = + env->GetBooleanField(jplayer, Common::Android::GetPlayerInputUseSystemVibratorField()); + } +} + +void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveControlPlayerValues(JNIEnv* env, jobject obj) { + Settings::values.players.SetGlobal(false); + + // Clear all controls from the config in case the user reverted back to globals + per_game_config->ClearControlPlayerValues(); + for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) { + per_game_config->SaveAndroidControlPlayerValues(index); + } +} + } // extern "C" diff --git a/src/android/app/src/main/jni/native_input.cpp b/src/android/app/src/main/jni/native_input.cpp new file mode 100644 index 000000000..37a65f2b8 --- /dev/null +++ b/src/android/app/src/main/jni/native_input.cpp @@ -0,0 +1,629 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <common/fs/fs.h> +#include <common/fs/path_util.h> +#include <common/settings.h> +#include <hid_core/hid_types.h> +#include <jni.h> + +#include "android_config.h" +#include "common/android/android_common.h" +#include "common/android/id_cache.h" +#include "hid_core/frontend/emulated_controller.h" +#include "hid_core/hid_core.h" +#include "input_common/drivers/android.h" +#include "input_common/drivers/touch_screen.h" +#include "input_common/drivers/virtual_amiibo.h" +#include "input_common/drivers/virtual_gamepad.h" +#include "native.h" + +std::unordered_map<std::string, std::unique_ptr<AndroidConfig>> map_profiles; + +bool IsHandheldOnly() { + const auto npad_style_set = + EmulationSession::GetInstance().System().HIDCore().GetSupportedStyleTag(); + + if (npad_style_set.fullkey == 1) { + return false; + } + + if (npad_style_set.handheld == 0) { + return false; + } + + return !Settings::IsDockedMode(); +} + +std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) { + return filename.replace_extension(); +} + +bool IsProfileNameValid(std::string_view profile_name) { + return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos; +} + +bool ProfileExistsInFilesystem(std::string_view profile_name) { + return Common::FS::Exists(Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "input" / + fmt::format("{}.ini", profile_name)); +} + +bool ProfileExistsInMap(const std::string& profile_name) { + return map_profiles.find(profile_name) != map_profiles.end(); +} + +bool SaveProfile(const std::string& profile_name, std::size_t player_index) { + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + Settings::values.players.GetValue()[player_index].profile_name = profile_name; + map_profiles[profile_name]->SaveAndroidControlPlayerValues(player_index); + return true; +} + +bool LoadProfile(std::string& profile_name, std::size_t player_index) { + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + if (!ProfileExistsInFilesystem(profile_name)) { + map_profiles.erase(profile_name); + return false; + } + + LOG_INFO(Config, "Loading input profile `{}`", profile_name); + + Settings::values.players.GetValue()[player_index].profile_name = profile_name; + map_profiles[profile_name]->ReadAndroidControlPlayerValues(player_index); + return true; +} + +void ApplyControllerConfig(size_t player_index, + const std::function<void(Core::HID::EmulatedController*)>& apply) { + auto& hid_core = EmulationSession::GetInstance().System().HIDCore(); + if (player_index == 0) { + auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); + auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); + handheld->EnableConfiguration(); + player_one->EnableConfiguration(); + apply(handheld); + apply(player_one); + handheld->DisableConfiguration(); + player_one->DisableConfiguration(); + handheld->SaveCurrentConfig(); + player_one->SaveCurrentConfig(); + } else { + auto* controller = hid_core.GetEmulatedControllerByIndex(player_index); + controller->EnableConfiguration(); + apply(controller); + controller->DisableConfiguration(); + controller->SaveCurrentConfig(); + } +} + +void ConnectController(size_t player_index, bool connected) { + auto& hid_core = EmulationSession::GetInstance().System().HIDCore(); + if (player_index == 0) { + auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); + auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); + handheld->EnableConfiguration(); + player_one->EnableConfiguration(); + if (player_one->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) { + if (connected) { + handheld->Connect(); + } else { + handheld->Disconnect(); + } + player_one->Disconnect(); + } else { + if (connected) { + player_one->Connect(); + } else { + player_one->Disconnect(); + } + handheld->Disconnect(); + } + handheld->DisableConfiguration(); + player_one->DisableConfiguration(); + handheld->SaveCurrentConfig(); + player_one->SaveCurrentConfig(); + } else { + auto* controller = hid_core.GetEmulatedControllerByIndex(player_index); + controller->EnableConfiguration(); + if (connected) { + controller->Connect(); + } else { + controller->Disconnect(); + } + controller->DisableConfiguration(); + controller->SaveCurrentConfig(); + } +} + +extern "C" { + +jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isHandheldOnly(JNIEnv* env, + jobject j_obj) { + return IsHandheldOnly(); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadButtonEvent( + JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jint j_button_id, jint j_action) { + EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetButtonState( + Common::Android::GetJString(env, j_guid), j_port, j_button_id, j_action != 0); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadAxisEvent( + JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jint j_stick_id, jfloat j_value) { + EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetAxisPosition( + Common::Android::GetJString(env, j_guid), j_port, j_stick_id, j_value); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadMotionEvent( + JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jlong j_delta_timestamp, + jfloat j_x_gyro, jfloat j_y_gyro, jfloat j_z_gyro, jfloat j_x_accel, jfloat j_y_accel, + jfloat j_z_accel) { + EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetMotionState( + Common::Android::GetJString(env, j_guid), j_port, j_delta_timestamp, j_x_gyro, j_y_gyro, + j_z_gyro, j_x_accel, j_y_accel, j_z_accel); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onReadNfcTag(JNIEnv* env, jobject j_obj, + jbyteArray j_data) { + jboolean isCopy{false}; + std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)), + static_cast<size_t>(env->GetArrayLength(j_data))); + + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->LoadAmiibo(data); + } +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onRemoveNfcTag(JNIEnv* env, jobject j_obj) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->CloseAmiibo(); + } +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchPressed(JNIEnv* env, jobject j_obj, + jint j_id, jfloat j_x_axis, + jfloat j_y_axis) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().Window().OnTouchPressed(j_id, j_x_axis, j_y_axis); + } +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchMoved(JNIEnv* env, jobject j_obj, + jint j_id, jfloat j_x_axis, + jfloat j_y_axis) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().Window().OnTouchMoved(j_id, j_x_axis, j_y_axis); + } +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchReleased(JNIEnv* env, jobject j_obj, + jint j_id) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().Window().OnTouchReleased(j_id); + } +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onOverlayButtonEventImpl( + JNIEnv* env, jobject j_obj, jint j_port, jint j_button_id, jint j_action) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetButtonState( + j_port, j_button_id, j_action == 1); + } +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onOverlayJoystickEventImpl( + JNIEnv* env, jobject j_obj, jint j_port, jint j_stick_id, jfloat j_x_axis, jfloat j_y_axis) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetStickPosition( + j_port, j_stick_id, j_x_axis, j_y_axis); + } +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onDeviceMotionEvent( + JNIEnv* env, jobject j_obj, jint j_port, jlong j_delta_timestamp, jfloat j_x_gyro, + jfloat j_y_gyro, jfloat j_z_gyro, jfloat j_x_accel, jfloat j_y_accel, jfloat j_z_accel) { + if (EmulationSession::GetInstance().IsRunning()) { + EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetMotionState( + j_port, j_delta_timestamp, j_x_gyro, j_y_gyro, j_z_gyro, j_x_accel, j_y_accel, + j_z_accel); + } +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_reloadInputDevices(JNIEnv* env, + jobject j_obj) { + EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices(); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_registerController(JNIEnv* env, + jobject j_obj, + jobject j_device) { + EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->RegisterController(j_device); +} + +jobjectArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getInputDevices(JNIEnv* env, + jobject j_obj) { + auto devices = EmulationSession::GetInstance().GetInputSubsystem().GetInputDevices(); + jobjectArray jdevices = env->NewObjectArray(devices.size(), Common::Android::GetStringClass(), + Common::Android::ToJString(env, "")); + for (size_t i = 0; i < devices.size(); ++i) { + env->SetObjectArrayElement(jdevices, i, + Common::Android::ToJString(env, devices[i].Serialize())); + } + return jdevices; +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadInputProfiles(JNIEnv* env, + jobject j_obj) { + map_profiles.clear(); + const auto input_profile_loc = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "input"; + + if (Common::FS::IsDir(input_profile_loc)) { + Common::FS::IterateDirEntries( + input_profile_loc, + [&](const std::filesystem::path& full_path) { + const auto filename = full_path.filename(); + const auto name_without_ext = + Common::FS::PathToUTF8String(GetNameWithoutExtension(filename)); + + if (filename.extension() == ".ini" && IsProfileNameValid(name_without_ext)) { + map_profiles.insert_or_assign( + name_without_ext, std::make_unique<AndroidConfig>( + name_without_ext, Config::ConfigType::InputProfile)); + } + + return true; + }, + Common::FS::DirEntryFilter::File); + } +} + +jobjectArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getInputProfileNames( + JNIEnv* env, jobject j_obj) { + std::vector<std::string> profile_names; + profile_names.reserve(map_profiles.size()); + + auto it = map_profiles.cbegin(); + while (it != map_profiles.cend()) { + const auto& [profile_name, config] = *it; + if (!ProfileExistsInFilesystem(profile_name)) { + it = map_profiles.erase(it); + continue; + } + + profile_names.push_back(profile_name); + ++it; + } + + std::stable_sort(profile_names.begin(), profile_names.end()); + + jobjectArray j_profile_names = + env->NewObjectArray(profile_names.size(), Common::Android::GetStringClass(), + Common::Android::ToJString(env, "")); + for (size_t i = 0; i < profile_names.size(); ++i) { + env->SetObjectArrayElement(j_profile_names, i, + Common::Android::ToJString(env, profile_names[i])); + } + + return j_profile_names; +} + +jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isProfileNameValid(JNIEnv* env, + jobject j_obj, + jstring j_name) { + return Common::Android::GetJString(env, j_name).find_first_of("<>:;\"/\\|,.!?*") == + std::string::npos; +} + +jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_createProfile(JNIEnv* env, + jobject j_obj, + jstring j_name, + jint j_player_index) { + auto profile_name = Common::Android::GetJString(env, j_name); + if (ProfileExistsInMap(profile_name)) { + return false; + } + + map_profiles.insert_or_assign( + profile_name, + std::make_unique<AndroidConfig>(profile_name, Config::ConfigType::InputProfile)); + + return SaveProfile(profile_name, j_player_index); +} + +jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_deleteProfile(JNIEnv* env, + jobject j_obj, + jstring j_name, + jint j_player_index) { + auto profile_name = Common::Android::GetJString(env, j_name); + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + if (!ProfileExistsInFilesystem(profile_name) || + Common::FS::RemoveFile(map_profiles[profile_name]->GetConfigFilePath())) { + map_profiles.erase(profile_name); + } + + Settings::values.players.GetValue()[j_player_index].profile_name = ""; + return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name); +} + +jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadProfile(JNIEnv* env, jobject j_obj, + jstring j_name, + jint j_player_index) { + auto profile_name = Common::Android::GetJString(env, j_name); + return LoadProfile(profile_name, j_player_index); +} + +jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_saveProfile(JNIEnv* env, jobject j_obj, + jstring j_name, + jint j_player_index) { + auto profile_name = Common::Android::GetJString(env, j_name); + return SaveProfile(profile_name, j_player_index); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadPerGameConfiguration( + JNIEnv* env, jobject j_obj, jint j_player_index, jint j_selected_index, + jstring j_selected_profile_name) { + static constexpr size_t HANDHELD_INDEX = 8; + + auto& hid_core = EmulationSession::GetInstance().System().HIDCore(); + Settings::values.players.SetGlobal(false); + + auto profile_name = Common::Android::GetJString(env, j_selected_profile_name); + auto* emulated_controller = hid_core.GetEmulatedControllerByIndex(j_player_index); + + if (j_selected_index == 0) { + Settings::values.players.GetValue()[j_player_index].profile_name = ""; + if (j_player_index == 0) { + Settings::values.players.GetValue()[HANDHELD_INDEX] = {}; + } + Settings::values.players.SetGlobal(true); + emulated_controller->ReloadFromSettings(); + return; + } + if (profile_name.empty()) { + return; + } + auto& player = Settings::values.players.GetValue()[j_player_index]; + auto& global_player = Settings::values.players.GetValue(true)[j_player_index]; + player.profile_name = profile_name; + global_player.profile_name = profile_name; + // Read from the profile into the custom player settings + LoadProfile(profile_name, j_player_index); + // Make sure the controller is connected + player.connected = true; + + emulated_controller->ReloadFromSettings(); + + if (j_player_index > 0) { + return; + } + // Handle Handheld cases + auto& handheld_player = Settings::values.players.GetValue()[HANDHELD_INDEX]; + auto* handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); + if (player.controller_type == Settings::ControllerType::Handheld) { + handheld_player = player; + } else { + handheld_player = {}; + } + handheld_controller->ReloadFromSettings(); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_beginMapping(JNIEnv* env, jobject j_obj, + jint jtype) { + EmulationSession::GetInstance().GetInputSubsystem().BeginMapping( + static_cast<InputCommon::Polling::InputType>(jtype)); +} + +jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getNextInput(JNIEnv* env, + jobject j_obj) { + return Common::Android::ToJString( + env, EmulationSession::GetInstance().GetInputSubsystem().GetNextInput().Serialize()); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_stopMapping(JNIEnv* env, jobject j_obj) { + EmulationSession::GetInstance().GetInputSubsystem().StopMapping(); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_updateMappingsWithDefaultImpl( + JNIEnv* env, jobject j_obj, jint j_player_index, jstring j_device_params, + jstring j_display_name) { + auto& input_subsystem = EmulationSession::GetInstance().GetInputSubsystem(); + + // Clear all previous mappings + for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { + ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) { + controller->SetButtonParam(button_id, {}); + }); + } + for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { + ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) { + controller->SetStickParam(analog_id, {}); + }); + } + + // Apply new mappings + auto device = Common::ParamPackage(Common::Android::GetJString(env, j_device_params)); + auto button_mappings = input_subsystem.GetButtonMappingForDevice(device); + auto analog_mappings = input_subsystem.GetAnalogMappingForDevice(device); + auto display_name = Common::Android::GetJString(env, j_display_name); + for (const auto& button_mapping : button_mappings) { + const std::size_t index = button_mapping.first; + auto named_mapping = button_mapping.second; + named_mapping.Set("display", display_name); + ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) { + controller->SetButtonParam(index, named_mapping); + }); + } + for (const auto& analog_mapping : analog_mappings) { + const std::size_t index = analog_mapping.first; + auto named_mapping = analog_mapping.second; + named_mapping.Set("display", display_name); + ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) { + controller->SetStickParam(index, named_mapping); + }); + } +} + +jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonParamImpl(JNIEnv* env, + jobject j_obj, + jint j_player_index, + jint j_button) { + return Common::Android::ToJString(env, EmulationSession::GetInstance() + .System() + .HIDCore() + .GetEmulatedControllerByIndex(j_player_index) + ->GetButtonParam(j_button) + .Serialize()); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setButtonParamImpl( + JNIEnv* env, jobject j_obj, jint j_player_index, jint j_button_id, jstring j_param) { + ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) { + controller->SetButtonParam(j_button_id, + Common::ParamPackage(Common::Android::GetJString(env, j_param))); + }); +} + +jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getStickParamImpl(JNIEnv* env, + jobject j_obj, + jint j_player_index, + jint j_stick) { + return Common::Android::ToJString(env, EmulationSession::GetInstance() + .System() + .HIDCore() + .GetEmulatedControllerByIndex(j_player_index) + ->GetStickParam(j_stick) + .Serialize()); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setStickParamImpl( + JNIEnv* env, jobject j_obj, jint j_player_index, jint j_stick_id, jstring j_param) { + ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) { + controller->SetStickParam(j_stick_id, + Common::ParamPackage(Common::Android::GetJString(env, j_param))); + }); +} + +jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonNameImpl(JNIEnv* env, + jobject j_obj, + jstring j_param) { + return static_cast<jint>(EmulationSession::GetInstance().GetInputSubsystem().GetButtonName( + Common::ParamPackage(Common::Android::GetJString(env, j_param)))); +} + +jintArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl( + JNIEnv* env, jobject j_obj, jint j_player_index) { + auto& hid_core = EmulationSession::GetInstance().System().HIDCore(); + const auto npad_style_set = hid_core.GetSupportedStyleTag(); + std::vector<s32> supported_indexes; + if (npad_style_set.fullkey == 1) { + supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Fullkey)); + } + + if (npad_style_set.joycon_dual == 1) { + supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconDual)); + } + + if (npad_style_set.joycon_left == 1) { + supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconLeft)); + } + + if (npad_style_set.joycon_right == 1) { + supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconRight)); + } + + if (j_player_index == 0 && npad_style_set.handheld == 1) { + supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Handheld)); + } + + if (npad_style_set.gamecube == 1) { + supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::GameCube)); + } + + jintArray j_supported_indexes = env->NewIntArray(supported_indexes.size()); + env->SetIntArrayRegion(j_supported_indexes, 0, supported_indexes.size(), + supported_indexes.data()); + return j_supported_indexes; +} + +jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getStyleIndexImpl(JNIEnv* env, + jobject j_obj, + jint j_player_index) { + return static_cast<s32>(EmulationSession::GetInstance() + .System() + .HIDCore() + .GetEmulatedControllerByIndex(j_player_index) + ->GetNpadStyleIndex(true)); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setStyleIndexImpl(JNIEnv* env, + jobject j_obj, + jint j_player_index, + jint j_style_index) { + auto& hid_core = EmulationSession::GetInstance().System().HIDCore(); + auto type = static_cast<Core::HID::NpadStyleIndex>(j_style_index); + ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) { + controller->SetNpadStyleIndex(type); + }); + if (j_player_index == 0) { + auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); + auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); + ConnectController(j_player_index, + player_one->IsConnected(true) || handheld->IsConnected(true)); + } +} + +jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isControllerImpl(JNIEnv* env, + jobject j_obj, + jstring jparams) { + return static_cast<jint>(EmulationSession::GetInstance().GetInputSubsystem().IsController( + Common::ParamPackage(Common::Android::GetJString(env, jparams)))); +} + +jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getIsConnected(JNIEnv* env, + jobject j_obj, + jint j_player_index) { + auto& hid_core = EmulationSession::GetInstance().System().HIDCore(); + auto* controller = hid_core.GetEmulatedControllerByIndex(static_cast<size_t>(j_player_index)); + if (j_player_index == 0 && + controller->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) { + return hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld)->IsConnected(true); + } + return controller->IsConnected(true); +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_connectControllersImpl( + JNIEnv* env, jobject j_obj, jbooleanArray j_connected) { + jboolean isCopy = false; + auto j_connected_array_size = env->GetArrayLength(j_connected); + jboolean* j_connected_array = env->GetBooleanArrayElements(j_connected, &isCopy); + for (int i = 0; i < j_connected_array_size; ++i) { + ConnectController(i, j_connected_array[i]); + } +} + +void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_resetControllerMappings( + JNIEnv* env, jobject j_obj, jint j_player_index) { + // Clear all previous mappings + for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { + ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) { + controller->SetButtonParam(button_id, {}); + }); + } + for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { + ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) { + controller->SetStickParam(analog_id, {}); + }); + } +} + +} // extern "C" diff --git a/src/android/app/src/main/res/drawable/button_anim.xml b/src/android/app/src/main/res/drawable/button_anim.xml new file mode 100644 index 000000000..ccdc5ca6a --- /dev/null +++ b/src/android/app/src/main/res/drawable/button_anim.xml @@ -0,0 +1,142 @@ +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector + android:width="1000dp" + android:height="1000dp" + android:viewportWidth="1000" + android:viewportHeight="1000"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_0_G" + android:pivotX="100" + android:pivotY="100" + android:scaleX="4.5" + android:scaleY="4.5" + android:translateX="400" + android:translateY="400"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="?attr/colorSecondaryContainer" + android:fillType="nonZero" + android:pathData=" M198.56 100 C198.56,154.43 154.43,198.56 100,198.56 C45.57,198.56 1.44,154.43 1.44,100 C1.44,45.57 45.57,1.44 100,1.44 C154.43,1.44 198.56,45.57 198.56,100c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="0.8" + android:fillColor="?attr/colorOnSecondaryContainer" + android:fillType="nonZero" + android:pathData=" M50.14 151.21 C50.53,150.18 89.6,49.87 90.1,48.63 C90.1,48.63 90.67,47.2 90.67,47.2 C90.67,47.2 101.67,47.2 101.67,47.2 C101.67,47.2 112.67,47.2 112.67,47.2 C112.67,47.2 133.47,99.12 133.47,99.12 C144.91,127.68 154.32,151.17 154.38,151.33 C154.47,151.56 152.2,151.6 143.14,151.55 C143.14,151.55 131.79,151.48 131.79,151.48 C131.79,151.48 127.22,139.57 127.22,139.57 C127.22,139.57 122.65,127.66 122.65,127.66 C122.65,127.66 101.68,127.73 101.68,127.73 C101.68,127.73 80.71,127.8 80.71,127.8 C80.71,127.8 76.38,139.71 76.38,139.71 C76.38,139.71 72.06,151.62 72.06,151.62 C72.06,151.62 61.02,151.62 61.02,151.62 C50.61,151.62 50,151.55 50.14,151.22 C50.14,151.22 50.14,151.21 50.14,151.21c M115.86 110.06 C115.8,109.91 112.55,101.13 108.62,90.56 C104.7,80 101.42,71.43 101.34,71.53 C101.22,71.66 92.84,94.61 87.25,110.06 C87.17,110.29 90.13,110.34 101.56,110.34 C113,110.34 115.95,110.28 115.86,110.06c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> + <target android:name="_R_G_L_0_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="100" + android:propertyName="scaleX" + android:startOffset="0" + android:valueFrom="4.5" + android:valueTo="3.75" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="100" + android:propertyName="scaleY" + android:startOffset="0" + android:valueFrom="4.5" + android:valueTo="3.75" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="234" + android:propertyName="scaleX" + android:startOffset="100" + android:valueFrom="3.75" + android:valueTo="3.75" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="234" + android:propertyName="scaleY" + android:startOffset="100" + android:valueFrom="3.75" + android:valueTo="3.75" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="scaleX" + android:startOffset="334" + android:valueFrom="3.75" + android:valueTo="4.75" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="scaleY" + android:startOffset="334" + android:valueFrom="3.75" + android:valueTo="4.75" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="67" + android:propertyName="scaleX" + android:startOffset="501" + android:valueFrom="4.75" + android:valueTo="4.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="67" + android:propertyName="scaleY" + android:startOffset="501" + android:valueFrom="4.75" + android:valueTo="4.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="1034" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> +</animated-vector> diff --git a/src/android/app/src/main/res/drawable/ic_controller_disconnected.xml b/src/android/app/src/main/res/drawable/ic_controller_disconnected.xml new file mode 100644 index 000000000..8e3c66f74 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_controller_disconnected.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M700,480q-25,0 -42.5,-17.5T640,420q0,-25 17.5,-42.5T700,360q25,0 42.5,17.5T760,420q0,25 -17.5,42.5T700,480ZM366,480ZM280,600v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM160,720q-33,0 -56.5,-23.5T80,640v-320q0,-34 24,-57.5t58,-23.5h77l81,81L160,320v320h366L55,169l57,-57 736,736 -57,57 -185,-185L160,720ZM880,640q0,26 -14,46t-37,29l-29,-29v-366L434,320l-80,-80h446q33,0 56.5,23.5T880,320v320ZM617,503Z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_more_vert.xml b/src/android/app/src/main/res/drawable/ic_more_vert.xml new file mode 100644 index 000000000..9f62ac595 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_more_vert.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_new_label.xml b/src/android/app/src/main/res/drawable/ic_new_label.xml new file mode 100644 index 000000000..fac562c26 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_new_label.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M21,12l-4.37,6.16C16.26,18.68 15.65,19 15,19h-3l0,-6H9v-3H3V7c0,-1.1 0.9,-2 2,-2h10c0.65,0 1.26,0.31 1.63,0.84L21,12zM10,15H7v-3H5v3H2v2h3v3h2v-3h3V15z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_overlay.xml b/src/android/app/src/main/res/drawable/ic_overlay.xml new file mode 100644 index 000000000..c7986c5a2 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_overlay.xml @@ -0,0 +1,21 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M21,5H3C1.9,5 1,5.9 1,7v10c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V7C23,5.9 22.1,5 21,5zM18,17H6V7h12V17z" /> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M15,11.25h1.5v1.5h-1.5z" /> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M12.5,11.25h1.5v1.5h-1.5z" /> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M10,11.25h1.5v1.5h-1.5z" /> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M7.5,11.25h1.5v1.5h-1.5z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/ic_share.xml b/src/android/app/src/main/res/drawable/ic_share.xml new file mode 100644 index 000000000..3fc2f3c99 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_share.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?attr/colorControlNormal" + android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z" /> +</vector> diff --git a/src/android/app/src/main/res/drawable/stick_one_direction_anim.xml b/src/android/app/src/main/res/drawable/stick_one_direction_anim.xml new file mode 100644 index 000000000..a1da1316f --- /dev/null +++ b/src/android/app/src/main/res/drawable/stick_one_direction_anim.xml @@ -0,0 +1,118 @@ +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector + android:width="1000dp" + android:height="1000dp" + android:viewportWidth="1000" + android:viewportHeight="1000"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_1_G" + android:pivotX="100" + android:pivotY="100" + android:scaleX="5" + android:scaleY="5" + android:translateX="400" + android:translateY="400"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:pathData=" M100 199.39 C59.8,199.39 23.56,175.17 8.18,138.04 C-7.2,100.9 1.3,58.15 29.73,29.72 C58.15,1.3 100.9,-7.21 138.04,8.18 C175.18,23.56 199.39,59.8 199.39,100 C199.33,154.87 154.87,199.33 100,199.39c " + android:strokeWidth="1" + android:strokeAlpha="0.6" + android:strokeColor="?attr/colorOutline" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> + </group> + <group + android:name="_R_G_L_0_G_T_1" + android:scaleX="5" + android:scaleY="5" + android:translateX="500" + android:translateY="500"> + <group + android:name="_R_G_L_0_G" + android:translateX="-100" + android:translateY="-100"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="?attr/colorSecondaryContainer" + android:fillType="nonZero" + android:pathData=" M100.45 28.02 C140.63,28.02 173.2,60.59 173.2,100.77 C173.2,140.95 140.63,173.52 100.45,173.52 C60.27,173.52 27.7,140.95 27.7,100.77 C27.7,60.59 60.27,28.02 100.45,28.02c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="0.8" + android:fillColor="?attr/colorOnSecondaryContainer" + android:fillType="nonZero" + android:pathData=" M100.45 50.26 C128.62,50.26 151.46,73.1 151.46,101.28 C151.46,129.45 128.62,152.29 100.45,152.29 C72.27,152.29 49.43,129.45 49.43,101.28 C49.43,73.1 72.27,50.26 100.45,50.26c " /> + </group> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="267" + android:pathData="M 500,500C 500,500 364,500 364,500" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="0"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="234" + android:pathData="M 364,500C 364,500 364,500 364,500" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="267"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="133" + android:pathData="M 364,500C 364,500 525,500 525,500" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="501"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="100" + android:pathData="M 525,500C 525,500 500,500 500,500" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="634"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="968" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> +</animated-vector> diff --git a/src/android/app/src/main/res/drawable/stick_two_direction_anim.xml b/src/android/app/src/main/res/drawable/stick_two_direction_anim.xml new file mode 100644 index 000000000..bc71adcbd --- /dev/null +++ b/src/android/app/src/main/res/drawable/stick_two_direction_anim.xml @@ -0,0 +1,173 @@ +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector + android:width="1000dp" + android:height="1000dp" + android:viewportWidth="1000" + android:viewportHeight="1000"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_1_G" + android:pivotX="100" + android:pivotY="100" + android:scaleX="5" + android:scaleY="5" + android:translateX="400" + android:translateY="400"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:pathData=" M100 199.39 C59.8,199.39 23.56,175.17 8.18,138.04 C-7.2,100.9 1.3,58.15 29.73,29.72 C58.15,1.3 100.9,-7.21 138.04,8.18 C175.18,23.56 199.39,59.8 199.39,100 C199.33,154.87 154.87,199.33 100,199.39c " + android:strokeWidth="1" + android:strokeAlpha="0.6" + android:strokeColor="?attr/colorOutline" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> + </group> + <group + android:name="_R_G_L_0_G_T_1" + android:scaleX="5" + android:scaleY="5" + android:translateX="500" + android:translateY="500"> + <group + android:name="_R_G_L_0_G" + android:translateX="-100" + android:translateY="-100"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="?attr/colorSecondaryContainer" + android:fillType="nonZero" + android:pathData=" M100.45 28.02 C140.63,28.02 173.2,60.59 173.2,100.77 C173.2,140.95 140.63,173.52 100.45,173.52 C60.27,173.52 27.7,140.95 27.7,100.77 C27.7,60.59 60.27,28.02 100.45,28.02c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="0.8" + android:fillColor="?attr/colorOnSecondaryContainer" + android:fillType="nonZero" + android:pathData=" M100.45 50.26 C128.62,50.26 151.46,73.1 151.46,101.28 C151.46,129.45 128.62,152.29 100.45,152.29 C72.27,152.29 49.43,129.45 49.43,101.28 C49.43,73.1 72.27,50.26 100.45,50.26c " /> + </group> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="267" + android:pathData="M 500,500C 500,500 364,500 364,500" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="0"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="234" + android:pathData="M 364,500C 364,500 364,500 364,500" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="267"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="133" + android:pathData="M 364,500C 364,500 525,500 525,500" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="501"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="100" + android:pathData="M 525,500C 525,500 500,500 500,500" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="634"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="400" + android:pathData="M 500,500C 500,500 500,500 500,500" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="734"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="267" + android:pathData="M 500,500C 500,500 500,364 500,364" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="1134"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="234" + android:pathData="M 500,364C 500,364 500,364 500,364" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="1401"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="133" + android:pathData="M 500,364C 500,364 500,535 500,535" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="1635"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="100" + android:pathData="M 500,535C 500,535 500,500 500,500" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="1768"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="2269" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> +</animated-vector> diff --git a/src/android/app/src/main/res/layout-ldrtl/list_item_setting_input.xml b/src/android/app/src/main/res/layout-ldrtl/list_item_setting_input.xml new file mode 100644 index 000000000..583620dc6 --- /dev/null +++ b/src/android/app/src/main/res/layout-ldrtl/list_item_setting_input.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/setting_body" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?android:attr/selectableItemBackground" + android:clickable="true" + android:focusable="true" + android:gravity="center_vertical" + android:minHeight="72dp" + android:padding="16dp" + android:nextFocusLeft="@id/button_options"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal"> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_weight="1"> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/text_setting_name" + style="@style/TextAppearance.Material3.HeadlineMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="viewStart" + android:textSize="17sp" + app:lineHeight="22dp" + tools:text="Setting Name" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/text_setting_value" + style="@style/TextAppearance.Material3.LabelMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:textAlignment="viewStart" + android:textStyle="bold" + android:textSize="13sp" + tools:text="1x" /> + + </LinearLayout> + + <Button + android:id="@+id/button_options" + style="?attr/materialIconButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:nextFocusRight="@id/setting_body" + app:icon="@drawable/ic_more_vert" + app:iconSize="24dp" + app:iconTint="?attr/colorOnSurface" /> + + </LinearLayout> + +</RelativeLayout> diff --git a/src/android/app/src/main/res/layout/card_driver_option.xml b/src/android/app/src/main/res/layout/card_driver_option.xml index bda524f0f..09e26990b 100644 --- a/src/android/app/src/main/res/layout/card_driver_option.xml +++ b/src/android/app/src/main/res/layout/card_driver_option.xml @@ -39,10 +39,7 @@ style="@style/TextAppearance.Material3.TitleMedium" android:layout_width="match_parent" android:layout_height="wrap_content" - android:ellipsize="none" - android:marqueeRepeatLimit="marquee_forever" android:requiresFadingEdge="horizontal" - android:singleLine="true" android:textAlignment="viewStart" tools:text="@string/select_gpu_driver_default" /> @@ -52,10 +49,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="6dp" - android:ellipsize="none" - android:marqueeRepeatLimit="marquee_forever" android:requiresFadingEdge="horizontal" - android:singleLine="true" android:textAlignment="viewStart" tools:text="@string/install_gpu_driver_description" /> @@ -65,10 +59,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="6dp" - android:ellipsize="none" - android:marqueeRepeatLimit="marquee_forever" android:requiresFadingEdge="horizontal" - android:singleLine="true" android:textAlignment="viewStart" tools:text="@string/install_gpu_driver_description" /> diff --git a/src/android/app/src/main/res/layout/card_folder.xml b/src/android/app/src/main/res/layout/card_folder.xml index ed4a7ca8f..e3a5f1a86 100644 --- a/src/android/app/src/main/res/layout/card_folder.xml +++ b/src/android/app/src/main/res/layout/card_folder.xml @@ -21,10 +21,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical|start" - android:ellipsize="none" - android:marqueeRepeatLimit="marquee_forever" android:requiresFadingEdge="horizontal" - android:singleLine="true" android:textAlignment="viewStart" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/button_layout" diff --git a/src/android/app/src/main/res/layout/card_game.xml b/src/android/app/src/main/res/layout/card_game.xml index 6340171ec..411b50315 100644 --- a/src/android/app/src/main/res/layout/card_game.xml +++ b/src/android/app/src/main/res/layout/card_game.xml @@ -40,10 +40,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:ellipsize="none" - android:marqueeRepeatLimit="marquee_forever" android:requiresFadingEdge="horizontal" - android:singleLine="true" android:textAlignment="center" android:textSize="14sp" app:layout_constraintEnd_toEndOf="@+id/image_game_screen" diff --git a/src/android/app/src/main/res/layout/card_simple_outlined.xml b/src/android/app/src/main/res/layout/card_simple_outlined.xml index b73930e7e..e29df6a2d 100644 --- a/src/android/app/src/main/res/layout/card_simple_outlined.xml +++ b/src/android/app/src/main/res/layout/card_simple_outlined.xml @@ -59,9 +59,6 @@ android:textAlignment="viewStart" android:textSize="14sp" android:textStyle="bold" - android:singleLine="true" - android:marqueeRepeatLimit="marquee_forever" - android:ellipsize="none" android:requiresFadingEdge="horizontal" android:layout_marginTop="6dp" android:visibility="gone" diff --git a/src/android/app/src/main/res/layout/dialog_input_profiles.xml b/src/android/app/src/main/res/layout/dialog_input_profiles.xml new file mode 100644 index 000000000..6ad76fe41 --- /dev/null +++ b/src/android/app/src/main/res/layout/dialog_input_profiles.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/list_profiles" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:fadeScrollbars="false" /> diff --git a/src/android/app/src/main/res/layout/dialog_mapping.xml b/src/android/app/src/main/res/layout/dialog_mapping.xml new file mode 100644 index 000000000..06190b8d2 --- /dev/null +++ b/src/android/app/src/main/res/layout/dialog_mapping.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + xmlns:tools="http://schemas.android.com/tools" + android:defaultFocusHighlightEnabled="false" + android:focusable="true" + android:focusableInTouchMode="true" + android:focusedByDefault="true" + android:orientation="horizontal" + android:gravity="center"> + + <ImageView + android:id="@+id/image_stick_animation" + android:layout_width="@dimen/mapping_anim_size" + android:layout_height="@dimen/mapping_anim_size" + tools:src="@drawable/stick_two_direction_anim" /> + + <ImageView + android:id="@+id/image_button_animation" + android:layout_width="@dimen/mapping_anim_size" + android:layout_height="@dimen/mapping_anim_size" + android:layout_marginStart="48dp" + tools:src="@drawable/button_anim" /> + +</LinearLayout> diff --git a/src/android/app/src/main/res/layout/fragment_game_properties.xml b/src/android/app/src/main/res/layout/fragment_game_properties.xml index 436ebd79d..5e3f3cf28 100644 --- a/src/android/app/src/main/res/layout/fragment_game_properties.xml +++ b/src/android/app/src/main/res/layout/fragment_game_properties.xml @@ -76,10 +76,7 @@ android:layout_marginTop="12dp" android:layout_marginBottom="12dp" android:layout_marginHorizontal="16dp" - android:ellipsize="none" - android:marqueeRepeatLimit="marquee_forever" android:requiresFadingEdge="horizontal" - android:singleLine="true" android:textAlignment="center" tools:text="deko_basic" /> diff --git a/src/android/app/src/main/res/layout/list_item_input_profile.xml b/src/android/app/src/main/res/layout/list_item_input_profile.xml new file mode 100644 index 000000000..a08dccf0c --- /dev/null +++ b/src/android/app/src/main/res/layout/list_item_input_profile.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:focusable="false" + android:paddingHorizontal="20dp" + android:paddingVertical="16dp"> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/title" + style="@style/TextAppearance.Material3.HeadlineMedium" + android:layout_width="0dp" + android:layout_height="0dp" + android:textAlignment="viewStart" + android:gravity="start|center_vertical" + android:textSize="17sp" + android:layout_marginEnd="16dp" + app:layout_constraintBottom_toBottomOf="@+id/button_layout" + app:layout_constraintEnd_toStartOf="@+id/button_layout" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:lineHeight="28dp" + tools:text="My profile" /> + + <LinearLayout + android:id="@+id/button_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <Button + android:id="@+id/button_new" + style="@style/Widget.Material3.Button.IconButton.Filled.Tonal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/create_new_profile" + android:tooltipText="@string/create_new_profile" + app:icon="@drawable/ic_new_label" /> + + <Button + android:id="@+id/button_delete" + style="@style/Widget.Material3.Button.IconButton.Filled.Tonal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/delete" + android:tooltipText="@string/delete" + app:icon="@drawable/ic_delete" /> + + <Button + android:id="@+id/button_save" + style="@style/Widget.Material3.Button.IconButton.Filled.Tonal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/save" + android:tooltipText="@string/save" + app:icon="@drawable/ic_save" /> + + <Button + android:id="@+id/button_load" + style="@style/Widget.Material3.Button.IconButton.Filled.Tonal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/load" + android:tooltipText="@string/load" + app:icon="@drawable/ic_import" /> + + </LinearLayout> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/src/android/app/src/main/res/layout/list_item_setting_input.xml b/src/android/app/src/main/res/layout/list_item_setting_input.xml new file mode 100644 index 000000000..d67cbe245 --- /dev/null +++ b/src/android/app/src/main/res/layout/list_item_setting_input.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/setting_body" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?android:attr/selectableItemBackground" + android:clickable="true" + android:focusable="true" + android:gravity="center_vertical" + android:minHeight="72dp" + android:padding="16dp" + android:nextFocusRight="@id/button_options"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal"> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_weight="1"> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/text_setting_name" + style="@style/TextAppearance.Material3.HeadlineMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="viewStart" + android:textSize="17sp" + app:lineHeight="22dp" + tools:text="Setting Name" /> + + <com.google.android.material.textview.MaterialTextView + android:id="@+id/text_setting_value" + style="@style/TextAppearance.Material3.LabelMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:textAlignment="viewStart" + android:textStyle="bold" + android:textSize="13sp" + tools:text="1x" /> + + </LinearLayout> + + <Button + android:id="@+id/button_options" + style="?attr/materialIconButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:nextFocusLeft="@id/setting_body" + app:icon="@drawable/ic_more_vert" + app:iconSize="24dp" + app:iconTint="?attr/colorOnSurface" /> + + </LinearLayout> + +</RelativeLayout> diff --git a/src/android/app/src/main/res/menu/menu_in_game.xml b/src/android/app/src/main/res/menu/menu_in_game.xml index eecb0563b..867197ebc 100644 --- a/src/android/app/src/main/res/menu/menu_in_game.xml +++ b/src/android/app/src/main/res/menu/menu_in_game.xml @@ -17,8 +17,13 @@ android:title="@string/per_game_settings" /> <item - android:id="@+id/menu_overlay_controls" + android:id="@+id/menu_controls" android:icon="@drawable/ic_controller" + android:title="@string/preferences_controls" /> + + <item + android:id="@+id/menu_overlay_controls" + android:icon="@drawable/ic_overlay" android:title="@string/emulation_input_overlay" /> <item diff --git a/src/android/app/src/main/res/menu/menu_input_options.xml b/src/android/app/src/main/res/menu/menu_input_options.xml new file mode 100644 index 000000000..81ea5043f --- /dev/null +++ b/src/android/app/src/main/res/menu/menu_input_options.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:id="@+id/invert_axis" + android:title="@string/invert_axis" + android:visible="false" /> + + <item + android:id="@+id/invert_button" + android:title="@string/invert_button" + android:visible="false" /> + + <item + android:id="@+id/toggle_button" + android:title="@string/toggle_button" + android:visible="false" /> + + <item + android:id="@+id/turbo_button" + android:title="@string/turbo_button" + android:visible="false" /> + + <item + android:id="@+id/set_threshold" + android:title="@string/set_threshold" + android:visible="false" /> + + <item + android:id="@+id/toggle_axis" + android:title="@string/toggle_axis" + android:visible="false" /> + +</menu> diff --git a/src/android/app/src/main/res/navigation/settings_navigation.xml b/src/android/app/src/main/res/navigation/settings_navigation.xml index 1d87d36b3..e4c66e7d5 100644 --- a/src/android/app/src/main/res/navigation/settings_navigation.xml +++ b/src/android/app/src/main/res/navigation/settings_navigation.xml @@ -26,7 +26,7 @@ <fragment android:id="@+id/settingsSearchFragment" - android:name="org.yuzu.yuzu_emu.fragments.SettingsSearchFragment" + android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsSearchFragment" android:label="SettingsSearchFragment" /> </navigation> diff --git a/src/android/app/src/main/res/values-w600dp/dimens.xml b/src/android/app/src/main/res/values-w600dp/dimens.xml index 128319e27..0e2d40876 100644 --- a/src/android/app/src/main/res/values-w600dp/dimens.xml +++ b/src/android/app/src/main/res/values-w600dp/dimens.xml @@ -2,4 +2,6 @@ <resources> <dimen name="spacing_navigation">0dp</dimen> <dimen name="spacing_navigation_rail">80dp</dimen> + + <dimen name="mapping_anim_size">100dp</dimen> </resources> 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/dimens.xml b/src/android/app/src/main/res/values/dimens.xml index 992b5ae44..bf733637f 100644 --- a/src/android/app/src/main/res/values/dimens.xml +++ b/src/android/app/src/main/res/values/dimens.xml @@ -18,4 +18,6 @@ <dimen name="dialog_margin">20dp</dimen> <dimen name="elevated_app_bar">3dp</dimen> + + <dimen name="mapping_anim_size">75dp</dimen> </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..f7f19cdad 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -209,6 +209,7 @@ <string name="value_with_units">%1$s%2$s</string> <!-- System settings strings --> + <string name="device_name">Device name</string> <string name="use_docked_mode">Docked Mode</string> <string name="use_docked_mode_description">Increases resolution, decreasing performance. Handheld Mode is used when disabled, lowering resolution and increasing performance.</string> <string name="emulated_region">Emulated region</string> @@ -226,6 +227,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> @@ -253,6 +256,92 @@ <string name="audio_volume">Volume</string> <string name="audio_volume_description">Specifies the volume of audio output.</string> + <!-- Input strings --> + <string name="buttons">Buttons</string> + <string name="button_a">A</string> + <string name="button_b">B</string> + <string name="button_x">X</string> + <string name="button_y">Y</string> + <string name="button_plus">Plus</string> + <string name="button_minus">Minus</string> + <string name="button_home">Home</string> + <string name="button_capture">Capture</string> + <string name="start_pause">Start/Pause</string> + <string name="dpad">D-Pad</string> + <string name="up">Up</string> + <string name="down">Down</string> + <string name="left">Left</string> + <string name="right">Right</string> + <string name="left_stick">Left stick</string> + <string name="control_stick">Control stick</string> + <string name="right_stick">Right stick</string> + <string name="c_stick">C-Stick</string> + <string name="pressed">Pressed</string> + <string name="range">Range</string> + <string name="deadzone">Deadzone</string> + <string name="modifier">Modifier</string> + <string name="modifier_range">Modifier range</string> + <string name="triggers">Triggers</string> + <string name="button_l">L</string> + <string name="button_r">R</string> + <string name="button_zl">ZL</string> + <string name="button_zr">ZR</string> + <string name="button_sl_left">Left SL</string> + <string name="button_sr_left">Left SR</string> + <string name="button_sl_right">Right SL</string> + <string name="button_sr_right">Right SR</string> + <string name="button_z">Z</string> + <string name="invalid">Invalid</string> + <string name="not_set">Not set</string> + <string name="unknown">Unknown</string> + <string name="qualified_hat">%1$s%2$s%3$sHat %4$s</string> + <string name="qualified_button_stick_axis">%1$s%2$s%3$sAxis %4$s</string> + <string name="qualified_button">%1$s%2$s%3$sButton %4$s</string> + <string name="qualified_axis">Axis %1$s%2$s</string> + <string name="unused">Unused</string> + <string name="input_prompt">Move or press an input</string> + <string name="unsupported_input">Unsupported input type</string> + <string name="input_mapping_filter">Input mapping filter</string> + <string name="input_mapping_filter_description">Select a device to filter mapping inputs</string> + <string name="auto_map">Auto-map a controller</string> + <string name="auto_map_description">Select a device to attempt auto-mapping</string> + <string name="attempted_auto_map">Attempted auto-map with %1$s</string> + <string name="controller_type">Controller type</string> + <string name="pro_controller">Pro Controller</string> + <string name="handheld">Handheld</string> + <string name="dual_joycons">Dual Joycons</string> + <string name="left_joycon">Left Joycon</string> + <string name="right_joycon">Right Joycon</string> + <string name="gamecube_controller">GameCube Controller</string> + <string name="invert_axis">Invert axis</string> + <string name="invert_button">Invert button</string> + <string name="toggle_button">Toggle button</string> + <string name="turbo_button">Turbo button</string> + <string name="set_threshold">Set threshold</string> + <string name="toggle_axis">Toggle axis</string> + <string name="connected">Connected</string> + <string name="use_system_vibrator">Use system vibrator</string> + <string name="input_overlay">Input overlay</string> + <string name="vibration">Vibration</string> + <string name="vibration_strength">Vibration strength</string> + <string name="profile">Profile</string> + <string name="create_new_profile">Create new profile</string> + <string name="enter_profile_name">Enter profile name</string> + <string name="profile_name_already_exists">Profile name already exists</string> + <string name="invalid_profile_name">Invalid profile name</string> + <string name="use_global_input_configuration">Use global input configuration</string> + <string name="player_num_profile">Player %d profile</string> + <string name="delete_input_profile">Delete input profile</string> + <string name="delete_input_profile_description">Are you sure that you want to delete this profile? This is not recoverable.</string> + <string name="stick_map_description">Move a stick left and then up or press a button</string> + <string name="button_map_description">Press a button or move a trigger/stick</string> + <string name="map_dpad_direction">Map to D-Pad %1$s</string> + <string name="map_control">Map to %1$s</string> + <string name="failed_to_load_profile">Failed to load profile</string> + <string name="failed_to_save_profile">Failed to save profile</string> + <string name="reset_mapping">Reset mappings</string> + <string name="reset_mapping_description">Are you sure that you want to reset all mappings for this controller to default? This cannot be undone.</string> + <!-- Miscellaneous --> <string name="slider_default">Default</string> <string name="ini_saved">Saved settings</string> @@ -290,6 +379,10 @@ <string name="more_options">More options</string> <string name="use_global_setting">Use global setting</string> <string name="operation_completed_successfully">The operation completed successfully</string> + <string name="retry">Retry</string> + <string name="confirm">Confirm</string> + <string name="load">Load</string> + <string name="save">Save</string> <!-- GPU driver installation --> <string name="select_gpu_driver">Select GPU driver</string> @@ -311,6 +404,9 @@ <string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string> <string name="preferences_audio">Audio</string> <string name="preferences_audio_description">Output engine, volume</string> + <string name="preferences_controls">Controls</string> + <string name="preferences_controls_description">Map controller input</string> + <string name="preferences_player">Player %d</string> <string name="preferences_theme">Theme and color</string> <string name="preferences_debug">Debug</string> <string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string> @@ -558,6 +654,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/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index d97ca2a40..49efae8e3 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp @@ -357,7 +357,9 @@ bool IsCubebSuitable() { return false; } - SCOPE_EXIT({ cubeb_destroy(ctx); }); + SCOPE_EXIT { + cubeb_destroy(ctx); + }; #ifdef _WIN32 if (SUCCEEDED(com_init_result)) { diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index c047b0668..0a98eb31e 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp @@ -20,10 +20,10 @@ namespace AudioCore::Sink { void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) { - SCOPE_EXIT({ + SCOPE_EXIT { queue.enqueue(buffer); ++queued_buffers; - }); + }; if (type == StreamType::In) { return; diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp index f39262db9..1145cbdf2 100644 --- a/src/common/android/id_cache.cpp +++ b/src/common/android/id_cache.cpp @@ -65,6 +65,30 @@ static jclass s_boolean_class; static jmethodID s_boolean_constructor; static jfieldID s_boolean_value_field; +static jclass s_player_input_class; +static jmethodID s_player_input_constructor; +static jfieldID s_player_input_connected_field; +static jfieldID s_player_input_buttons_field; +static jfieldID s_player_input_analogs_field; +static jfieldID s_player_input_motions_field; +static jfieldID s_player_input_vibration_enabled_field; +static jfieldID s_player_input_vibration_strength_field; +static jfieldID s_player_input_body_color_left_field; +static jfieldID s_player_input_body_color_right_field; +static jfieldID s_player_input_button_color_left_field; +static jfieldID s_player_input_button_color_right_field; +static jfieldID s_player_input_profile_name_field; +static jfieldID s_player_input_use_system_vibrator_field; + +static jclass s_yuzu_input_device_interface; +static jmethodID s_yuzu_input_device_get_name; +static jmethodID s_yuzu_input_device_get_guid; +static jmethodID s_yuzu_input_device_get_port; +static jmethodID s_yuzu_input_device_get_supports_vibration; +static jmethodID s_yuzu_input_device_vibrate; +static jmethodID s_yuzu_input_device_get_axes; +static jmethodID s_yuzu_input_device_has_keys; + static constexpr jint JNI_VERSION = JNI_VERSION_1_6; namespace Common::Android { @@ -276,6 +300,94 @@ jfieldID GetBooleanValueField() { return s_boolean_value_field; } +jclass GetPlayerInputClass() { + return s_player_input_class; +} + +jmethodID GetPlayerInputConstructor() { + return s_player_input_constructor; +} + +jfieldID GetPlayerInputConnectedField() { + return s_player_input_connected_field; +} + +jfieldID GetPlayerInputButtonsField() { + return s_player_input_buttons_field; +} + +jfieldID GetPlayerInputAnalogsField() { + return s_player_input_analogs_field; +} + +jfieldID GetPlayerInputMotionsField() { + return s_player_input_motions_field; +} + +jfieldID GetPlayerInputVibrationEnabledField() { + return s_player_input_vibration_enabled_field; +} + +jfieldID GetPlayerInputVibrationStrengthField() { + return s_player_input_vibration_strength_field; +} + +jfieldID GetPlayerInputBodyColorLeftField() { + return s_player_input_body_color_left_field; +} + +jfieldID GetPlayerInputBodyColorRightField() { + return s_player_input_body_color_right_field; +} + +jfieldID GetPlayerInputButtonColorLeftField() { + return s_player_input_button_color_left_field; +} + +jfieldID GetPlayerInputButtonColorRightField() { + return s_player_input_button_color_right_field; +} + +jfieldID GetPlayerInputProfileNameField() { + return s_player_input_profile_name_field; +} + +jfieldID GetPlayerInputUseSystemVibratorField() { + return s_player_input_use_system_vibrator_field; +} + +jclass GetYuzuInputDeviceInterface() { + return s_yuzu_input_device_interface; +} + +jmethodID GetYuzuDeviceGetName() { + return s_yuzu_input_device_get_name; +} + +jmethodID GetYuzuDeviceGetGUID() { + return s_yuzu_input_device_get_guid; +} + +jmethodID GetYuzuDeviceGetPort() { + return s_yuzu_input_device_get_port; +} + +jmethodID GetYuzuDeviceGetSupportsVibration() { + return s_yuzu_input_device_get_supports_vibration; +} + +jmethodID GetYuzuDeviceVibrate() { + return s_yuzu_input_device_vibrate; +} + +jmethodID GetYuzuDeviceGetAxes() { + return s_yuzu_input_device_get_axes; +} + +jmethodID GetYuzuDeviceHasKeys() { + return s_yuzu_input_device_has_keys; +} + #ifdef __cplusplus extern "C" { #endif @@ -387,6 +499,55 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z"); env->DeleteLocalRef(boolean_class); + const jclass player_input_class = + env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput"); + s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class)); + s_player_input_constructor = env->GetMethodID( + player_input_class, "<init>", + "(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V"); + s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z"); + s_player_input_buttons_field = + env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;"); + s_player_input_analogs_field = + env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;"); + s_player_input_motions_field = + env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;"); + s_player_input_vibration_enabled_field = + env->GetFieldID(player_input_class, "vibrationEnabled", "Z"); + s_player_input_vibration_strength_field = + env->GetFieldID(player_input_class, "vibrationStrength", "I"); + s_player_input_body_color_left_field = + env->GetFieldID(player_input_class, "bodyColorLeft", "J"); + s_player_input_body_color_right_field = + env->GetFieldID(player_input_class, "bodyColorRight", "J"); + s_player_input_button_color_left_field = + env->GetFieldID(player_input_class, "buttonColorLeft", "J"); + s_player_input_button_color_right_field = + env->GetFieldID(player_input_class, "buttonColorRight", "J"); + s_player_input_profile_name_field = + env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;"); + s_player_input_use_system_vibrator_field = + env->GetFieldID(player_input_class, "useSystemVibrator", "Z"); + env->DeleteLocalRef(player_input_class); + + const jclass yuzu_input_device_interface = + env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice"); + s_yuzu_input_device_interface = + reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface)); + s_yuzu_input_device_get_name = + env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;"); + s_yuzu_input_device_get_guid = + env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;"); + s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort", "()I"); + s_yuzu_input_device_get_supports_vibration = + env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z"); + s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate", "(F)V"); + s_yuzu_input_device_get_axes = + env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;"); + s_yuzu_input_device_has_keys = + env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z"); + env->DeleteLocalRef(yuzu_input_device_interface); + // Initialize Android Storage Common::FS::Android::RegisterCallbacks(env, s_native_library_class); @@ -416,6 +577,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) { env->DeleteGlobalRef(s_double_class); env->DeleteGlobalRef(s_integer_class); env->DeleteGlobalRef(s_boolean_class); + env->DeleteGlobalRef(s_player_input_class); + env->DeleteGlobalRef(s_yuzu_input_device_interface); // UnInitialize applets SoftwareKeyboard::CleanupJNI(env); diff --git a/src/common/android/id_cache.h b/src/common/android/id_cache.h index 47802f96c..cd2844dcc 100644 --- a/src/common/android/id_cache.h +++ b/src/common/android/id_cache.h @@ -85,4 +85,28 @@ jclass GetBooleanClass(); jmethodID GetBooleanConstructor(); jfieldID GetBooleanValueField(); +jclass GetPlayerInputClass(); +jmethodID GetPlayerInputConstructor(); +jfieldID GetPlayerInputConnectedField(); +jfieldID GetPlayerInputButtonsField(); +jfieldID GetPlayerInputAnalogsField(); +jfieldID GetPlayerInputMotionsField(); +jfieldID GetPlayerInputVibrationEnabledField(); +jfieldID GetPlayerInputVibrationStrengthField(); +jfieldID GetPlayerInputBodyColorLeftField(); +jfieldID GetPlayerInputBodyColorRightField(); +jfieldID GetPlayerInputButtonColorLeftField(); +jfieldID GetPlayerInputButtonColorRightField(); +jfieldID GetPlayerInputProfileNameField(); +jfieldID GetPlayerInputUseSystemVibratorField(); + +jclass GetYuzuInputDeviceInterface(); +jmethodID GetYuzuDeviceGetName(); +jmethodID GetYuzuDeviceGetGUID(); +jmethodID GetYuzuDeviceGetPort(); +jmethodID GetYuzuDeviceGetSupportsVibration(); +jmethodID GetYuzuDeviceVibrate(); +jmethodID GetYuzuDeviceGetAxes(); +jmethodID GetYuzuDeviceHasKeys(); + } // namespace Common::Android diff --git a/src/common/demangle.cpp b/src/common/demangle.cpp index 6e117cb41..b2c9d126a 100644 --- a/src/common/demangle.cpp +++ b/src/common/demangle.cpp @@ -20,7 +20,9 @@ std::string DemangleSymbol(const std::string& mangled) { } char* demangled = nullptr; - SCOPE_EXIT({ std::free(demangled); }); + SCOPE_EXIT { + std::free(demangled); + }; if (is_itanium(mangled)) { demangled = llvm::itaniumDemangle(mangled.c_str()); diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 860c39e6a..e0b5a6a67 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -430,11 +430,11 @@ public: explicit Impl(size_t backing_size_, size_t virtual_size_) : backing_size{backing_size_}, virtual_size{virtual_size_} { bool good = false; - SCOPE_EXIT({ + SCOPE_EXIT { if (!good) { Release(); } - }); + }; long page_size = sysconf(_SC_PAGESIZE); if (page_size != 0x1000) { diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index 85dc18c11..3205eb7da 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp @@ -24,10 +24,10 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c out_entry->block_size = page_size; // Regardless of whether the page was mapped, advance on exit. - SCOPE_EXIT({ + SCOPE_EXIT { context->next_page += 1; context->next_offset += page_size; - }); + }; // Validate that we can read the actual entry. const auto page = context->next_page; diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h index e9c789c88..f3e88cde9 100644 --- a/src/common/scope_exit.h +++ b/src/common/scope_exit.h @@ -7,29 +7,61 @@ #include "common/common_funcs.h" namespace detail { -template <typename Func> -struct ScopeExitHelper { - explicit ScopeExitHelper(Func&& func_) : func(std::move(func_)) {} - ~ScopeExitHelper() { +template <class F> +class ScopeGuard { + YUZU_NON_COPYABLE(ScopeGuard); + +private: + F f; + bool active; + +public: + constexpr ScopeGuard(F f_) : f(std::move(f_)), active(true) {} + constexpr ~ScopeGuard() { if (active) { - func(); + f(); } } - - void Cancel() { + constexpr void Cancel() { active = false; } - Func func; - bool active{true}; + constexpr ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) { + rhs.Cancel(); + } + + ScopeGuard& operator=(ScopeGuard&& rhs) = delete; }; -template <typename Func> -ScopeExitHelper<Func> ScopeExit(Func&& func) { - return ScopeExitHelper<Func>(std::forward<Func>(func)); +template <class F> +constexpr ScopeGuard<F> MakeScopeGuard(F f) { + return ScopeGuard<F>(std::move(f)); } + +enum class ScopeGuardOnExit {}; + +template <typename F> +constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) { + return ScopeGuard<F>(std::forward<F>(f)); +} + } // namespace detail +#define CONCATENATE_IMPL(s1, s2) s1##s2 +#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2) + +#ifdef __COUNTER__ +#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__) +#else +#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__) +#endif + +/** + * This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be + * used when the caller might want to cancel the ScopeExit. + */ +#define SCOPE_GUARD detail::ScopeGuardOnExit() + [&]() + /** * This macro allows you to conveniently specify a block of code that will run on scope exit. Handy * for doing ad-hoc clean-up tasks in a function with multiple returns. @@ -38,7 +70,7 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) { * \code * const int saved_val = g_foo; * g_foo = 55; - * SCOPE_EXIT({ g_foo = saved_val; }); + * SCOPE_EXIT{ g_foo = saved_val; }; * * if (Bar()) { * return 0; @@ -47,10 +79,4 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) { * } * \endcode */ -#define SCOPE_EXIT(body) auto CONCAT2(scope_exit_helper_, __LINE__) = detail::ScopeExit([&]() body) - -/** - * This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be - * used when the caller might want to cancel the ScopeExit. - */ -#define SCOPE_GUARD(body) detail::ScopeExit([&]() body) +#define SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 07709d4e5..80d388fe8 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -30,6 +30,7 @@ namespace Settings { #define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED> #define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED> +SETTING(AppletMode, false); SETTING(AudioEngine, false); SETTING(bool, false); SETTING(int, false); @@ -215,6 +216,8 @@ const char* TranslateCategory(Category category) { return "Debugging"; case Category::GpuDriver: return "GpuDriver"; + case Category::LibraryApplet: + return "LibraryApplet"; case Category::Miscellaneous: return "Miscellaneous"; case Category::Network: diff --git a/src/common/settings.h b/src/common/settings.h index f1b1add56..aa054dc24 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -133,6 +133,38 @@ struct TouchFromButtonMap { struct Values { Linkage linkage{}; + // Applet + Setting<AppletMode> cabinet_applet_mode{linkage, AppletMode::LLE, "cabinet_applet_mode", + Category::LibraryApplet}; + Setting<AppletMode> controller_applet_mode{linkage, AppletMode::HLE, "controller_applet_mode", + Category::LibraryApplet}; + Setting<AppletMode> data_erase_applet_mode{linkage, AppletMode::HLE, "data_erase_applet_mode", + Category::LibraryApplet}; + Setting<AppletMode> error_applet_mode{linkage, AppletMode::HLE, "error_applet_mode", + Category::LibraryApplet}; + Setting<AppletMode> net_connect_applet_mode{linkage, AppletMode::HLE, "net_connect_applet_mode", + Category::LibraryApplet}; + Setting<AppletMode> player_select_applet_mode{ + linkage, AppletMode::HLE, "player_select_applet_mode", Category::LibraryApplet}; + Setting<AppletMode> swkbd_applet_mode{linkage, AppletMode::LLE, "swkbd_applet_mode", + Category::LibraryApplet}; + Setting<AppletMode> mii_edit_applet_mode{linkage, AppletMode::LLE, "mii_edit_applet_mode", + Category::LibraryApplet}; + Setting<AppletMode> web_applet_mode{linkage, AppletMode::HLE, "web_applet_mode", + Category::LibraryApplet}; + Setting<AppletMode> shop_applet_mode{linkage, AppletMode::HLE, "shop_applet_mode", + Category::LibraryApplet}; + Setting<AppletMode> photo_viewer_applet_mode{ + linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet}; + Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::LLE, "offline_web_applet_mode", + Category::LibraryApplet}; + Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode", + Category::LibraryApplet}; + Setting<AppletMode> wifi_web_auth_applet_mode{ + linkage, AppletMode::HLE, "wifi_web_auth_applet_mode", Category::LibraryApplet}; + Setting<AppletMode> my_page_applet_mode{linkage, AppletMode::LLE, "my_page_applet_mode", + Category::LibraryApplet}; + // Audio SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", Category::Audio, Specialization::RuntimeList}; diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 987489e8a..2df3f0809 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h @@ -44,6 +44,7 @@ enum class Category : u32 { Services, Paths, Linux, + LibraryApplet, MaxEnum, }; diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 617036588..f42367e67 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -151,6 +151,8 @@ ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(ConsoleMode, Handheld, Docked); +ENUM(AppletMode, HLE, LLE); + template <typename Type> inline std::string CanonicalizeEnum(Type id) { const auto group = EnumMetadata<Type>::Canonicalizations(); diff --git a/src/common/settings_input.h b/src/common/settings_input.h index 53a95ef8f..a99bb0892 100644 --- a/src/common/settings_input.h +++ b/src/common/settings_input.h @@ -395,6 +395,10 @@ struct PlayerInput { u32 button_color_left; u32 button_color_right; std::string profile_name; + + // This is meant to tell the Android frontend whether to use a device's built-in vibration + // motor or a controller's vibrations. + bool use_system_vibrator; }; struct TouchscreenInput { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2d5490968..f67a12f8f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2,8 +2,8 @@ # SPDX-License-Identifier: GPL-2.0-or-later add_library(core STATIC - arm/arm_interface.h arm/arm_interface.cpp + arm/arm_interface.h arm/debug.cpp arm/debug.h arm/exclusive_monitor.cpp @@ -37,10 +37,10 @@ add_library(core STATIC debugger/gdbstub.h debugger/gdbstub_arch.cpp debugger/gdbstub_arch.h - device_memory_manager.h - device_memory_manager.inc device_memory.cpp device_memory.h + device_memory_manager.h + device_memory_manager.inc file_sys/bis_factory.cpp file_sys/bis_factory.h file_sys/card_image.cpp @@ -390,6 +390,20 @@ add_library(core STATIC hle/service/acc/errors.h hle/service/acc/profile_manager.cpp hle/service/acc/profile_manager.h + hle/service/am/am.cpp + hle/service/am/am.h + hle/service/am/am_results.h + hle/service/am/am_types.h + hle/service/am/applet.cpp + hle/service/am/applet.h + hle/service/am/applet_data_broker.cpp + hle/service/am/applet_data_broker.h + hle/service/am/applet_manager.cpp + hle/service/am/applet_manager.h + hle/service/am/applet_message_queue.cpp + hle/service/am/applet_message_queue.h + hle/service/am/display_layer_manager.cpp + hle/service/am/display_layer_manager.h hle/service/am/frontend/applet_cabinet.cpp hle/service/am/frontend/applet_cabinet.h hle/service/am/frontend/applet_controller.cpp @@ -411,80 +425,62 @@ add_library(core STATIC hle/service/am/frontend/applet_web_browser_types.h hle/service/am/frontend/applets.cpp hle/service/am/frontend/applets.h - hle/service/am/am.cpp - hle/service/am/am.h - hle/service/am/am_results.h - hle/service/am/am_types.h - hle/service/am/applet.cpp - hle/service/am/applet.h - hle/service/am/applet_ae.cpp - hle/service/am/applet_ae.h - hle/service/am/applet_manager.cpp - hle/service/am/applet_data_broker.cpp - hle/service/am/applet_data_broker.h - hle/service/am/applet_manager.h - hle/service/am/applet_oe.cpp - hle/service/am/applet_oe.h - hle/service/am/applet_common_functions.cpp - hle/service/am/applet_common_functions.h - hle/service/am/applet_message_queue.cpp - hle/service/am/applet_message_queue.h - hle/service/am/application_creator.cpp - hle/service/am/application_creator.h - hle/service/am/application_functions.cpp - hle/service/am/application_functions.h - hle/service/am/application_proxy.cpp - hle/service/am/application_proxy.h - hle/service/am/audio_controller.cpp - hle/service/am/audio_controller.h - hle/service/am/common_state_getter.cpp - hle/service/am/common_state_getter.h - hle/service/am/debug_functions.cpp - hle/service/am/debug_functions.h - hle/service/am/display_controller.cpp - hle/service/am/display_controller.h - hle/service/am/global_state_controller.cpp - hle/service/am/global_state_controller.h hle/service/am/hid_registration.cpp hle/service/am/hid_registration.h - hle/service/am/home_menu_functions.cpp - hle/service/am/home_menu_functions.h - hle/service/am/idle.cpp - hle/service/am/idle.h - hle/service/am/library_applet_accessor.cpp - hle/service/am/library_applet_accessor.h - hle/service/am/library_applet_creator.cpp - hle/service/am/library_applet_creator.h - hle/service/am/library_applet_proxy.cpp - hle/service/am/library_applet_proxy.h - hle/service/am/library_applet_self_accessor.cpp - hle/service/am/library_applet_self_accessor.h hle/service/am/library_applet_storage.cpp hle/service/am/library_applet_storage.h - hle/service/am/lock_accessor.cpp - hle/service/am/lock_accessor.h - hle/service/am/managed_layer_holder.cpp - hle/service/am/managed_layer_holder.h - hle/service/am/omm.cpp - hle/service/am/omm.h - hle/service/am/process_winding_controller.cpp - hle/service/am/process_winding_controller.h hle/service/am/process.cpp hle/service/am/process.h - hle/service/am/self_controller.cpp - hle/service/am/self_controller.h - hle/service/am/system_applet_proxy.cpp - hle/service/am/system_applet_proxy.h - hle/service/am/system_buffer_manager.cpp - hle/service/am/system_buffer_manager.h - hle/service/am/spsm.cpp - hle/service/am/spsm.h - hle/service/am/storage_accessor.cpp - hle/service/am/storage_accessor.h - hle/service/am/storage.cpp - hle/service/am/storage.h - hle/service/am/window_controller.cpp - hle/service/am/window_controller.h + hle/service/am/service/all_system_applet_proxies_service.cpp + hle/service/am/service/all_system_applet_proxies_service.h + hle/service/am/service/applet_common_functions.cpp + hle/service/am/service/applet_common_functions.h + hle/service/am/service/application_accessor.cpp + hle/service/am/service/application_accessor.h + hle/service/am/service/application_creator.cpp + hle/service/am/service/application_creator.h + hle/service/am/service/application_functions.cpp + hle/service/am/service/application_functions.h + hle/service/am/service/application_proxy.cpp + hle/service/am/service/application_proxy.h + hle/service/am/service/application_proxy_service.cpp + hle/service/am/service/application_proxy_service.h + hle/service/am/service/audio_controller.cpp + hle/service/am/service/audio_controller.h + hle/service/am/service/common_state_getter.cpp + hle/service/am/service/common_state_getter.h + hle/service/am/service/cradle_firmware_updater.cpp + hle/service/am/service/cradle_firmware_updater.h + hle/service/am/service/debug_functions.cpp + hle/service/am/service/debug_functions.h + hle/service/am/service/display_controller.cpp + hle/service/am/service/display_controller.h + hle/service/am/service/global_state_controller.cpp + hle/service/am/service/global_state_controller.h + hle/service/am/service/home_menu_functions.cpp + hle/service/am/service/home_menu_functions.h + hle/service/am/service/library_applet_accessor.cpp + hle/service/am/service/library_applet_accessor.h + hle/service/am/service/library_applet_creator.cpp + hle/service/am/service/library_applet_creator.h + hle/service/am/service/library_applet_proxy.cpp + hle/service/am/service/library_applet_proxy.h + hle/service/am/service/library_applet_self_accessor.cpp + hle/service/am/service/library_applet_self_accessor.h + hle/service/am/service/lock_accessor.cpp + hle/service/am/service/lock_accessor.h + hle/service/am/service/process_winding_controller.cpp + hle/service/am/service/process_winding_controller.h + hle/service/am/service/self_controller.cpp + hle/service/am/service/self_controller.h + hle/service/am/service/storage.cpp + hle/service/am/service/storage.h + hle/service/am/service/storage_accessor.cpp + hle/service/am/service/storage_accessor.h + hle/service/am/service/system_applet_proxy.cpp + hle/service/am/service/system_applet_proxy.h + hle/service/am/service/window_controller.cpp + hle/service/am/service/window_controller.h hle/service/aoc/aoc_u.cpp hle/service/aoc/aoc_u.h hle/service/apm/apm.cpp @@ -493,12 +489,12 @@ add_library(core STATIC hle/service/apm/apm_controller.h hle/service/apm/apm_interface.cpp hle/service/apm/apm_interface.h - hle/service/audio/audctl.cpp - hle/service/audio/audctl.h hle/service/audio/audin_u.cpp hle/service/audio/audin_u.h hle/service/audio/audio.cpp hle/service/audio/audio.h + hle/service/audio/audio_controller.cpp + hle/service/audio/audio_controller.h hle/service/audio/audout_u.cpp hle/service/audio/audout_u.h hle/service/audio/audrec_a.cpp @@ -512,18 +508,6 @@ add_library(core STATIC hle/service/audio/hwopus.h hle/service/bcat/backend/backend.cpp hle/service/bcat/backend/backend.h - hle/service/bcat/news/newly_arrived_event_holder.cpp - hle/service/bcat/news/newly_arrived_event_holder.h - hle/service/bcat/news/news_data_service.cpp - hle/service/bcat/news/news_data_service.h - hle/service/bcat/news/news_database_service.cpp - hle/service/bcat/news/news_database_service.h - hle/service/bcat/news/news_service.cpp - hle/service/bcat/news/news_service.h - hle/service/bcat/news/overwrite_event_holder.cpp - hle/service/bcat/news/overwrite_event_holder.h - hle/service/bcat/news/service_creator.cpp - hle/service/bcat/news/service_creator.h hle/service/bcat/bcat.cpp hle/service/bcat/bcat.h hle/service/bcat/bcat_result.h @@ -539,6 +523,18 @@ add_library(core STATIC hle/service/bcat/delivery_cache_progress_service.h hle/service/bcat/delivery_cache_storage_service.cpp hle/service/bcat/delivery_cache_storage_service.h + hle/service/bcat/news/newly_arrived_event_holder.cpp + hle/service/bcat/news/newly_arrived_event_holder.h + hle/service/bcat/news/news_data_service.cpp + hle/service/bcat/news/news_data_service.h + hle/service/bcat/news/news_database_service.cpp + hle/service/bcat/news/news_database_service.h + hle/service/bcat/news/news_service.cpp + hle/service/bcat/news/news_service.h + hle/service/bcat/news/overwrite_event_holder.cpp + hle/service/bcat/news/overwrite_event_holder.h + hle/service/bcat/news/service_creator.cpp + hle/service/bcat/news/service_creator.h hle/service/bcat/service_creator.cpp hle/service/bcat/service_creator.h hle/service/bpc/bpc.cpp @@ -547,6 +543,16 @@ add_library(core STATIC hle/service/btdrv/btdrv.h hle/service/btm/btm.cpp hle/service/btm/btm.h + hle/service/btm/btm_debug.cpp + hle/service/btm/btm_debug.h + hle/service/btm/btm_system.cpp + hle/service/btm/btm_system.h + hle/service/btm/btm_system_core.cpp + hle/service/btm/btm_system_core.h + hle/service/btm/btm_user.cpp + hle/service/btm/btm_user.h + hle/service/btm/btm_user_core.cpp + hle/service/btm/btm_user_core.h hle/service/caps/caps.cpp hle/service/caps/caps.h hle/service/caps/caps_a.cpp @@ -602,8 +608,6 @@ add_library(core STATIC hle/service/filesystem/romfs_controller.h hle/service/filesystem/save_data_controller.cpp hle/service/filesystem/save_data_controller.h - hle/service/fgm/fgm.cpp - hle/service/fgm/fgm.h hle/service/friend/friend.cpp hle/service/friend/friend.h hle/service/friend/friend_interface.cpp @@ -668,6 +672,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 @@ -729,15 +745,48 @@ add_library(core STATIC hle/service/nim/nim.h hle/service/npns/npns.cpp hle/service/npns/npns.h - hle/service/ns/errors.h - hle/service/ns/iplatform_service_manager.cpp - hle/service/ns/iplatform_service_manager.h + hle/service/ns/account_proxy_interface.cpp + hle/service/ns/account_proxy_interface.h + hle/service/ns/application_manager_interface.cpp + hle/service/ns/application_manager_interface.h + hle/service/ns/application_version_interface.cpp + hle/service/ns/application_version_interface.h + hle/service/ns/content_management_interface.cpp + hle/service/ns/content_management_interface.h + hle/service/ns/develop_interface.cpp + hle/service/ns/develop_interface.h + hle/service/ns/document_interface.cpp + hle/service/ns/document_interface.h + hle/service/ns/download_task_interface.cpp + hle/service/ns/download_task_interface.h + hle/service/ns/dynamic_rights_interface.cpp + hle/service/ns/dynamic_rights_interface.h + hle/service/ns/ecommerce_interface.cpp + hle/service/ns/ecommerce_interface.h + hle/service/ns/factory_reset_interface.cpp + hle/service/ns/factory_reset_interface.h hle/service/ns/language.cpp hle/service/ns/language.h hle/service/ns/ns.cpp hle/service/ns/ns.h - hle/service/ns/pdm_qry.cpp - hle/service/ns/pdm_qry.h + hle/service/ns/ns_results.h + hle/service/ns/ns_types.h + hle/service/ns/platform_service_manager.cpp + hle/service/ns/platform_service_manager.h + hle/service/ns/query_service.cpp + hle/service/ns/query_service.h + hle/service/ns/read_only_application_control_data_interface.cpp + hle/service/ns/read_only_application_control_data_interface.h + hle/service/ns/read_only_application_record_interface.cpp + hle/service/ns/read_only_application_record_interface.h + hle/service/ns/service_getter_interface.cpp + hle/service/ns/service_getter_interface.h + hle/service/ns/system_update_control.cpp + hle/service/ns/system_update_control.h + hle/service/ns/system_update_interface.cpp + hle/service/ns/system_update_interface.h + hle/service/ns/vulnerability_manager_interface.cpp + hle/service/ns/vulnerability_manager_interface.h hle/service/nvdrv/core/container.cpp hle/service/nvdrv/core/container.h hle/service/nvdrv/core/heap_mapper.cpp @@ -790,14 +839,14 @@ add_library(core STATIC hle/service/nvnflinger/consumer_base.cpp hle/service/nvnflinger/consumer_base.h hle/service/nvnflinger/consumer_listener.h - hle/service/nvnflinger/fb_share_buffer_manager.cpp - hle/service/nvnflinger/fb_share_buffer_manager.h hle/service/nvnflinger/graphic_buffer_producer.cpp hle/service/nvnflinger/graphic_buffer_producer.h - hle/service/nvnflinger/hos_binder_driver_server.cpp - hle/service/nvnflinger/hos_binder_driver_server.h hle/service/nvnflinger/hardware_composer.cpp hle/service/nvnflinger/hardware_composer.h + hle/service/nvnflinger/hos_binder_driver.cpp + hle/service/nvnflinger/hos_binder_driver.h + hle/service/nvnflinger/hos_binder_driver_server.cpp + hle/service/nvnflinger/hos_binder_driver_server.h hle/service/nvnflinger/hwc_layer.h hle/service/nvnflinger/nvnflinger.cpp hle/service/nvnflinger/nvnflinger.h @@ -805,19 +854,29 @@ add_library(core STATIC hle/service/nvnflinger/pixel_format.h hle/service/nvnflinger/producer_listener.h hle/service/nvnflinger/status.h + hle/service/nvnflinger/surface_flinger.cpp + hle/service/nvnflinger/surface_flinger.h hle/service/nvnflinger/ui/fence.h hle/service/nvnflinger/ui/graphic_buffer.cpp hle/service/nvnflinger/ui/graphic_buffer.h hle/service/nvnflinger/window.h hle/service/olsc/olsc.cpp hle/service/olsc/olsc.h + hle/service/omm/omm.cpp + hle/service/omm/omm.h + hle/service/omm/operation_mode_manager.cpp + hle/service/omm/operation_mode_manager.h + hle/service/omm/policy_manager_system.cpp + hle/service/omm/policy_manager_system.h + hle/service/omm/power_state_interface.cpp + hle/service/omm/power_state_interface.h hle/service/os/event.cpp hle/service/os/event.h + hle/service/os/multi_wait.cpp + hle/service/os/multi_wait.h hle/service/os/multi_wait_holder.cpp hle/service/os/multi_wait_holder.h hle/service/os/multi_wait_utils.h - hle/service/os/multi_wait.cpp - hle/service/os/multi_wait.h hle/service/os/mutex.cpp hle/service/os/mutex.h hle/service/pcie/pcie.cpp @@ -855,15 +914,17 @@ add_library(core STATIC hle/service/psc/time/common.cpp hle/service/psc/time/common.h hle/service/psc/time/errors.h - hle/service/psc/time/shared_memory.cpp - hle/service/psc/time/shared_memory.h - hle/service/psc/time/static.cpp - hle/service/psc/time/static.h hle/service/psc/time/manager.h + hle/service/psc/time/power_state_request_manager.cpp + hle/service/psc/time/power_state_request_manager.h hle/service/psc/time/power_state_service.cpp hle/service/psc/time/power_state_service.h hle/service/psc/time/service_manager.cpp hle/service/psc/time/service_manager.h + hle/service/psc/time/shared_memory.cpp + hle/service/psc/time/shared_memory.h + hle/service/psc/time/static.cpp + hle/service/psc/time/static.h hle/service/psc/time/steady_clock.cpp hle/service/psc/time/steady_clock.h hle/service/psc/time/system_clock.cpp @@ -872,8 +933,6 @@ add_library(core STATIC hle/service/psc/time/time_zone.h hle/service/psc/time/time_zone_service.cpp hle/service/psc/time/time_zone_service.h - hle/service/psc/time/power_state_request_manager.cpp - hle/service/psc/time/power_state_request_manager.h hle/service/ptm/psm.cpp hle/service/ptm/psm.h hle/service/ptm/ptm.cpp @@ -890,18 +949,21 @@ add_library(core STATIC hle/service/server_manager.h hle/service/service.cpp hle/service/service.h + hle/service/services.cpp + hle/service/services.h + hle/service/set/factory_settings_server.cpp + hle/service/set/factory_settings_server.h + hle/service/set/firmware_debug_settings_server.cpp + hle/service/set/firmware_debug_settings_server.h + hle/service/set/key_code_map.h hle/service/set/setting_formats/appln_settings.cpp hle/service/set/setting_formats/appln_settings.h hle/service/set/setting_formats/device_settings.cpp hle/service/set/setting_formats/device_settings.h - hle/service/set/setting_formats/system_settings.cpp - hle/service/set/setting_formats/system_settings.h hle/service/set/setting_formats/private_settings.cpp hle/service/set/setting_formats/private_settings.h - hle/service/set/factory_settings_server.cpp - hle/service/set/factory_settings_server.h - hle/service/set/firmware_debug_settings_server.cpp - hle/service/set/firmware_debug_settings_server.h + hle/service/set/setting_formats/system_settings.cpp + hle/service/set/setting_formats/system_settings.h hle/service/set/settings.cpp hle/service/set/settings.h hle/service/set/settings_server.cpp @@ -936,18 +998,36 @@ add_library(core STATIC hle/service/ssl/ssl_backend.h hle/service/usb/usb.cpp hle/service/usb/usb.h - hle/service/vi/display/vi_display.cpp - hle/service/vi/display/vi_display.h - hle/service/vi/layer/vi_layer.cpp - hle/service/vi/layer/vi_layer.h + hle/service/vi/application_display_service.cpp + hle/service/vi/application_display_service.h + hle/service/vi/application_root_service.cpp + hle/service/vi/application_root_service.h + hle/service/vi/conductor.cpp + hle/service/vi/conductor.h + hle/service/vi/container.cpp + hle/service/vi/container.h + hle/service/vi/display.h + hle/service/vi/display_list.h + hle/service/vi/layer.h + hle/service/vi/layer_list.h + hle/service/vi/manager_display_service.cpp + hle/service/vi/manager_display_service.h + hle/service/vi/manager_root_service.cpp + hle/service/vi/manager_root_service.h + hle/service/vi/service_creator.cpp + hle/service/vi/service_creator.h + hle/service/vi/shared_buffer_manager.cpp + hle/service/vi/shared_buffer_manager.h + hle/service/vi/system_display_service.cpp + hle/service/vi/system_display_service.h + hle/service/vi/system_root_service.cpp + hle/service/vi/system_root_service.h hle/service/vi/vi.cpp hle/service/vi/vi.h - hle/service/vi/vi_m.cpp - hle/service/vi/vi_m.h - hle/service/vi/vi_s.cpp - hle/service/vi/vi_s.h - hle/service/vi/vi_u.cpp - hle/service/vi/vi_u.h + hle/service/vi/vi_results.h + hle/service/vi/vi_types.h + hle/service/vi/vsync_manager.cpp + hle/service/vi/vsync_manager.h internal_network/network.cpp internal_network/network.h internal_network/network_interface.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 435ef6793..9e8936728 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -47,6 +47,7 @@ #include "core/hle/service/psc/time/system_clock.h" #include "core/hle/service/psc/time/time_zone_service.h" #include "core/hle/service/service.h" +#include "core/hle/service/services.h" #include "core/hle/service/set/system_settings_server.h" #include "core/hle/service/sm/sm.h" #include "core/internal_network/network.h" @@ -242,7 +243,7 @@ struct System::Impl { void Run() { std::unique_lock<std::mutex> lk(suspend_guard); - kernel.SuspendApplication(false); + kernel.SuspendEmulation(false); core_timing.SyncPause(false); is_paused.store(false, std::memory_order_relaxed); } @@ -251,7 +252,7 @@ struct System::Impl { std::unique_lock<std::mutex> lk(suspend_guard); core_timing.SyncPause(true); - kernel.SuspendApplication(true); + kernel.SuspendEmulation(true); is_paused.store(true, std::memory_order_relaxed); } @@ -261,7 +262,7 @@ struct System::Impl { std::unique_lock<std::mutex> StallApplication() { std::unique_lock<std::mutex> lk(suspend_guard); - kernel.SuspendApplication(true); + kernel.SuspendEmulation(true); core_timing.SyncPause(true); return lk; } @@ -269,7 +270,7 @@ struct System::Impl { void UnstallApplication() { if (!IsPaused()) { core_timing.SyncPause(false); - kernel.SuspendApplication(false); + kernel.SuspendEmulation(false); } } @@ -310,7 +311,8 @@ struct System::Impl { audio_core = std::make_unique<AudioCore::AudioCore>(system); service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); - services = std::make_unique<Service::Services>(service_manager, system); + services = + std::make_unique<Service::Services>(service_manager, system, stop_event.get_token()); is_powered_on = true; exit_locked = false; @@ -458,11 +460,10 @@ struct System::Impl { gpu_core->NotifyShutdown(); } + stop_event.request_stop(); + core_timing.SyncPause(false); Network::CancelPendingSocketOperations(); - kernel.SuspendApplication(true); - if (services) { - services->KillNVNFlinger(); - } + kernel.SuspendEmulation(true); kernel.CloseServices(); kernel.ShutdownCores(); applet_manager.Reset(); @@ -480,6 +481,7 @@ struct System::Impl { cpu_manager.Shutdown(); debugger.reset(); kernel.Shutdown(); + stop_event = {}; Network::RestartSocketOperations(); if (auto room_member = room_network.GetRoomMember().lock()) { @@ -615,6 +617,7 @@ struct System::Impl { ExecuteProgramCallback execute_program_callback; ExitCallback exit_callback; + std::stop_source stop_event; std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 7a5c22f78..9b1c77387 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -199,10 +199,10 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) { data.host_context = Common::Fiber::ThreadToFiber(); // Cleanup - SCOPE_EXIT({ + SCOPE_EXIT { data.host_context->Exit(); MicroProfileOnThreadExit(); - }); + }; // Running if (!gpu_barrier->Sync(token)) { diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index 6dfee806c..37c1e69c3 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -391,12 +391,12 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o std::min((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size); const auto current_vaddr = static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset); - SCOPE_EXIT({ + SCOPE_EXIT{ page_index += next_pages; page_offset = 0; increment(copy_amount); remaining_size -= copy_amount; - }); + }; auto phys_addr = compressed_physical_ptr[page_index]; if (phys_addr == 0) { diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 285fe4db6..665252358 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -172,6 +172,10 @@ u32 NCA::GetSDKVersion() const { return reader->GetSdkAddonVersion(); } +u8 NCA::GetKeyGeneration() const { + return reader->GetKeyGeneration(); +} + bool NCA::IsUpdate() const { return is_update; } diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index f68464eb0..8560617f5 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -77,6 +77,7 @@ public: u64 GetTitleId() const; RightsId GetRightsId() const; u32 GetSDKVersion() const; + u8 GetKeyGeneration() const; bool IsUpdate() const; VirtualFile GetRomFS() const; diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 555b9d8f7..667efbbab 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -64,8 +64,8 @@ struct RawNACP { u64_le cache_storage_size; u64_le cache_storage_journal_size; u64_le cache_storage_data_and_journal_max_size; - u64_le cache_storage_max_index; - INSERT_PADDING_BYTES(0xE70); + u16_le cache_storage_max_index; + INSERT_PADDING_BYTES(0xE76); }; static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size."); diff --git a/src/core/file_sys/fs_directory.h b/src/core/file_sys/fs_directory.h index 25c9cb18a..3f90abb8f 100644 --- a/src/core/file_sys/fs_directory.h +++ b/src/core/file_sys/fs_directory.h @@ -3,6 +3,10 @@ #pragma once +#include <string_view> +#include "common/common_funcs.h" +#include "common/common_types.h" + namespace FileSys { constexpr inline size_t EntryNameLengthMax = 0x300; diff --git a/src/core/file_sys/fs_path_utility.h b/src/core/file_sys/fs_path_utility.h index e9011d065..5643141f9 100644 --- a/src/core/file_sys/fs_path_utility.h +++ b/src/core/file_sys/fs_path_utility.h @@ -447,7 +447,7 @@ public: char* replacement_path = nullptr; size_t replacement_path_size = 0; - SCOPE_EXIT({ + SCOPE_EXIT { if (replacement_path != nullptr) { if (std::is_constant_evaluated()) { delete[] replacement_path; @@ -455,7 +455,7 @@ public: Deallocate(replacement_path, replacement_path_size); } } - }); + }; // Perform path replacement, if necessary if (IsParentDirectoryPathReplacementNeeded(cur_path)) { @@ -1102,8 +1102,8 @@ public: R_SUCCEED(); } - static Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len, - const PathFlags& flags) { + static constexpr Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len, + const PathFlags& flags) { // Use StringTraits names for remainder of scope using namespace StringTraits; @@ -1199,7 +1199,7 @@ public: const size_t replaced_src_len = path_len - (src - path); char* replaced_src = nullptr; - SCOPE_EXIT({ + SCOPE_EXIT { if (replaced_src != nullptr) { if (std::is_constant_evaluated()) { delete[] replaced_src; @@ -1207,7 +1207,7 @@ public: Deallocate(replaced_src, replaced_src_len); } } - }); + }; if (std::is_constant_evaluated()) { replaced_src = new char[replaced_src_len]; diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp index caea0b8f8..a68fd973c 100644 --- a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp +++ b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp @@ -36,7 +36,9 @@ Result HierarchicalSha256Storage::Initialize(VirtualFile* base_storages, s32 lay // Get the base storage size. m_base_storage_size = base_storages[2]->GetSize(); { - auto size_guard = SCOPE_GUARD({ m_base_storage_size = 0; }); + auto size_guard = SCOPE_GUARD { + m_base_storage_size = 0; + }; R_UNLESS(m_base_storage_size <= static_cast<s64>(HashSize) << m_log_size_ratio << m_log_size_ratio, ResultHierarchicalSha256BaseStorageTooLarge); diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index ae4e441c9..289969cc4 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -98,7 +98,9 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) { const u64 original_program_id = aci_header.title_id; - SCOPE_EXIT({ aci_header.title_id = original_program_id; }); + SCOPE_EXIT { + aci_header.title_id = original_program_id; + }; return this->Load(file); } diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp index deb52069d..9ea16aa59 100644 --- a/src/core/file_sys/system_archive/shared_font.cpp +++ b/src/core/file_sys/system_archive/shared_font.cpp @@ -9,7 +9,7 @@ #include "core/file_sys/system_archive/data/font_standard.h" #include "core/file_sys/system_archive/shared_font.h" #include "core/file_sys/vfs/vfs_vector.h" -#include "core/hle/service/ns/iplatform_service_manager.h" +#include "core/hle/service/ns/platform_service_manager.h" namespace FileSys::SystemArchive { diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp index 472e8571c..3e01e3b67 100644 --- a/src/core/hle/kernel/k_client_session.cpp +++ b/src/core/hle/kernel/k_client_session.cpp @@ -24,7 +24,9 @@ Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) { // Create a session request. KSessionRequest* request = KSessionRequest::Create(m_kernel); R_UNLESS(request != nullptr, ResultOutOfResource); - SCOPE_EXIT({ request->Close(); }); + SCOPE_EXIT { + request->Close(); + }; // Initialize the request. request->Initialize(nullptr, address, size); @@ -37,7 +39,9 @@ Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t // Create a session request. KSessionRequest* request = KSessionRequest::Create(m_kernel); R_UNLESS(request != nullptr, ResultOutOfResource); - SCOPE_EXIT({ request->Close(); }); + SCOPE_EXIT { + request->Close(); + }; // Initialize the request. request->Initialize(event, address, size); diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index 1dd86fb3c..19cdf4f3a 100644 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -1305,11 +1305,11 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr // Ensure that we maintain the instruction cache. bool reprotected_pages = false; - SCOPE_EXIT({ + SCOPE_EXIT { if (reprotected_pages && any_code_pages) { InvalidateInstructionCache(m_kernel, this, dst_address, size); } - }); + }; // Unmap. { @@ -1397,7 +1397,9 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) { // Close the opened pages when we're done with them. // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed // automatically. - SCOPE_EXIT({ pg.Close(); }); + SCOPE_EXIT { + pg.Close(); + }; // Clear all the newly allocated pages. for (const auto& it : pg) { @@ -1603,7 +1605,9 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option)); // Ensure that the page group is closed when we're done working with it. - SCOPE_EXIT({ pg.Close(); }); + SCOPE_EXIT { + pg.Close(); + }; // Clear all pages. for (const auto& it : pg) { @@ -2191,7 +2195,9 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) { // Close the opened pages when we're done with them. // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed // automatically. - SCOPE_EXIT({ pg.Close(); }); + SCOPE_EXIT { + pg.Close(); + }; // Clear all the newly allocated pages. for (const auto& it : pg) { @@ -2592,7 +2598,9 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre // Temporarily unlock ourselves, so that other operations can occur while we flush the // region. m_general_lock.Unlock(); - SCOPE_EXIT({ m_general_lock.Lock(); }); + SCOPE_EXIT { + m_general_lock.Lock(); + }; // Flush the region. R_ASSERT(FlushDataCache(dst_address, size)); @@ -3311,10 +3319,10 @@ Result KPageTableBase::ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddre // Ensure we unmap the io memory when we're done with it. const KPageProperties unmap_properties = KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None}; - SCOPE_EXIT({ + SCOPE_EXIT { R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false, unmap_properties, OperationType::Unmap, true)); - }); + }; // Read the memory. const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); @@ -3347,10 +3355,10 @@ Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAdd // Ensure we unmap the io memory when we're done with it. const KPageProperties unmap_properties = KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None}; - SCOPE_EXIT({ + SCOPE_EXIT { R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false, unmap_properties, OperationType::Unmap, true)); - }); + }; // Write the memory. const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); @@ -4491,14 +4499,14 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size, // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll // free on scope exit. - SCOPE_EXIT({ + SCOPE_EXIT { if (start_partial_page != 0) { m_kernel.MemoryManager().Close(start_partial_page, 1); } if (end_partial_page != 0) { m_kernel.MemoryManager().Close(end_partial_page, 1); } - }); + }; ON_RESULT_FAILURE { if (cur_mapped_addr != dst_addr) { @@ -5166,10 +5174,10 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) { GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value)); // If we fail in the next bit (or retry), we need to cleanup the pages. - auto pg_guard = SCOPE_GUARD({ + auto pg_guard = SCOPE_GUARD { pg.OpenFirst(); pg.Close(); - }); + }; // Map the memory. { @@ -5694,7 +5702,9 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a // Ensure that any pages we track are closed on exit. KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager()); - SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); + SCOPE_EXIT { + pages_to_close.CloseAndReset(); + }; // Make a page group representing the region to unmap. this->MakePageGroup(pages_to_close, virt_addr, num_pages); diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 0b08e877e..cb9a11a63 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -4,8 +4,9 @@ #include <random> #include "common/scope_exit.h" #include "common/settings.h" +#include "core/arm/dynarmic/arm_dynarmic.h" +#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/core.h" -#include "core/gpu_dirty_memory_manager.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_shared_memory.h" @@ -76,7 +77,9 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, } // Terminate and close the thread. - SCOPE_EXIT({ cur_child->Close(); }); + SCOPE_EXIT { + cur_child->Close(); + }; if (const Result terminate_result = cur_child->Terminate(); ResultTerminationRequested == terminate_result) { @@ -465,11 +468,11 @@ void KProcess::DoWorkerTaskImpl() { Result KProcess::StartTermination() { // Finalize the handle table when we're done, if the process isn't immortal. - SCOPE_EXIT({ + SCOPE_EXIT { if (!m_is_immortal) { this->FinalizeHandleTable(); } - }); + }; // Terminate child threads other than the current one. R_RETURN(TerminateChildren(m_kernel, this, GetCurrentThreadPointer(m_kernel))); @@ -963,7 +966,9 @@ Result KProcess::Run(s32 priority, size_t stack_size) { // Create a new thread for the process. KThread* main_thread = KThread::Create(m_kernel); R_UNLESS(main_thread != nullptr, ResultOutOfResource); - SCOPE_EXIT({ main_thread->Close(); }); + SCOPE_EXIT { + main_thread->Close(); + }; // Initialize the thread. R_TRY(KThread::InitializeUserThread(m_kernel.System(), main_thread, this->GetEntryPoint(), 0, @@ -1154,7 +1159,9 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size); // Ensure we maintain a clean state on exit. - SCOPE_EXIT({ res_limit->Close(); }); + SCOPE_EXIT { + res_limit->Close(); + }; // Declare flags and code address. Svc::CreateProcessFlag flag{}; @@ -1258,6 +1265,10 @@ void KProcess::InitializeInterfaces() { #ifdef HAS_NCE if (this->IsApplication() && Settings::IsNceEnabled()) { + // Register the scoped JIT handler before creating any NCE instances + // so that its signal handler will appear first in the signal chain. + Core::ScopedJitExecution::RegisterHandler(); + for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i); } diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index adaabdd6d..40c3323ef 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -651,11 +651,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m // Process any special data. if (src_header.GetHasSpecialHeader()) { // After we process, make sure we track whether the receive list is broken. - SCOPE_EXIT({ + SCOPE_EXIT { if (offset > dst_recv_list_idx) { recv_list_broken = true; } - }); + }; // Process special data. R_TRY(ProcessMessageSpecialData<false>(offset, dst_process, src_process, src_thread, @@ -665,11 +665,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m // Process any pointer buffers. for (auto i = 0; i < src_header.GetPointerCount(); ++i) { // After we process, make sure we track whether the receive list is broken. - SCOPE_EXIT({ + SCOPE_EXIT { if (offset > dst_recv_list_idx) { recv_list_broken = true; } - }); + }; R_TRY(ProcessReceiveMessagePointerDescriptors( offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list, @@ -680,11 +680,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m // Process any map alias buffers. for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) { // After we process, make sure we track whether the receive list is broken. - SCOPE_EXIT({ + SCOPE_EXIT { if (offset > dst_recv_list_idx) { recv_list_broken = true; } - }); + }; // We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite. const KMemoryPermission perm = (i >= src_header.GetSendCount()) @@ -702,11 +702,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m // Process any raw data. if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) { // After we process, make sure we track whether the receive list is broken. - SCOPE_EXIT({ + SCOPE_EXIT { if (offset + raw_count > dst_recv_list_idx) { recv_list_broken = true; } - }); + }; // Get the offset and size. const size_t offset_words = offset * sizeof(u32); @@ -1124,7 +1124,9 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server client_thread->Open(); } - SCOPE_EXIT({ client_thread->Close(); }); + SCOPE_EXIT { + client_thread->Close(); + }; // Set the request as our current. m_current_request = request; @@ -1174,7 +1176,9 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server // Reply to the client. { // After we reply, close our reference to the request. - SCOPE_EXIT({ request->Close(); }); + SCOPE_EXIT { + request->Close(); + }; // Get the event to check whether the request is async. if (KEvent* event = request->GetEvent(); event != nullptr) { @@ -1236,7 +1240,9 @@ Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buff } // Close reference to the request once we're done processing it. - SCOPE_EXIT({ request->Close(); }); + SCOPE_EXIT { + request->Close(); + }; // Extract relevant information from the request. const uint64_t client_message = request->GetAddress(); @@ -1394,7 +1400,9 @@ void KServerSession::CleanupRequests() { } // Close a reference to the request once it's cleaned up. - SCOPE_EXIT({ request->Close(); }); + SCOPE_EXIT { + request->Close(); + }; // Extract relevant information from the request. const uint64_t client_message = request->GetAddress(); @@ -1491,7 +1499,9 @@ void KServerSession::OnClientClosed() { ASSERT(thread != nullptr); // Ensure that we close the request when done. - SCOPE_EXIT({ request->Close(); }); + SCOPE_EXIT { + request->Close(); + }; // If we're terminating, close a reference to the thread and event. if (terminate) { diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index f13e232b2..e928cfebc 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -66,6 +66,7 @@ enum class SuspendType : u32 { Debug = 2, Backtrace = 3, Init = 4, + System = 5, Count, }; @@ -84,8 +85,9 @@ enum class ThreadState : u16 { DebugSuspended = (1 << (2 + SuspendShift)), BacktraceSuspended = (1 << (3 + SuspendShift)), InitSuspended = (1 << (4 + SuspendShift)), + SystemSuspended = (1 << (5 + SuspendShift)), - SuspendFlagMask = ((1 << 5) - 1) << SuspendShift, + SuspendFlagMask = ((1 << 6) - 1) << SuspendShift, }; DECLARE_ENUM_FLAG_OPERATORS(ThreadState); diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp index a632d1634..1952c0083 100644 --- a/src/core/hle/kernel/k_thread_local_page.cpp +++ b/src/core/hle/kernel/k_thread_local_page.cpp @@ -21,7 +21,9 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) { // Allocate a new page. KPageBuffer* page_buf = KPageBuffer::Allocate(kernel); R_UNLESS(page_buf != nullptr, ResultOutOfMemory); - auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); }); + auto page_buf_guard = SCOPE_GUARD { + KPageBuffer::Free(kernel, page_buf); + }; // Map the address in. const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf); diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index cbb1b02bb..09295e8ad 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp @@ -24,7 +24,9 @@ Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size, // Construct the page group, guarding to make sure our state is valid on exit. m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager()); - auto pg_guard = SCOPE_GUARD({ m_page_group.reset(); }); + auto pg_guard = SCOPE_GUARD { + m_page_group.reset(); + }; // Lock the memory. R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size, diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 34b25be66..9e5eaeec4 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -109,7 +109,9 @@ struct KernelCore::Impl { void Shutdown() { is_shutting_down.store(true, std::memory_order_relaxed); - SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); }); + SCOPE_EXIT { + is_shutting_down.store(false, std::memory_order_relaxed); + }; CloseServices(); @@ -1080,7 +1082,9 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name, process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); // Ensure that we don't hold onto any extra references. - SCOPE_EXIT({ process->Close(); }); + SCOPE_EXIT { + process->Close(); + }; // Register the new process. KProcess::Register(*this, process); @@ -1108,7 +1112,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); // Ensure that we don't hold onto any extra references. - SCOPE_EXIT({ process->Close(); }); + SCOPE_EXIT { + process->Close(); + }; // Register the new process. KProcess::Register(*this, process); @@ -1204,39 +1210,48 @@ const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const { return *impl->hidbus_shared_mem; } -void KernelCore::SuspendApplication(bool suspended) { +void KernelCore::SuspendEmulation(bool suspended) { const bool should_suspend{exception_exited || suspended}; - const auto activity = - should_suspend ? Svc::ProcessActivity::Paused : Svc::ProcessActivity::Runnable; + auto processes = GetProcessList(); - // Get the application process. - KScopedAutoObject<KProcess> process = ApplicationProcess(); - if (process.IsNull()) { - return; + for (auto& process : processes) { + KScopedLightLock ll{process->GetListLock()}; + + for (auto& thread : process->GetThreadList()) { + if (should_suspend) { + thread.RequestSuspend(SuspendType::System); + } else { + thread.Resume(SuspendType::System); + } + } } - // Set the new activity. - process->SetActivity(activity); + if (!should_suspend) { + return; + } // Wait for process execution to stop. - bool must_wait{should_suspend}; - - // KernelCore::SuspendApplication must be called from locked context, - // or we could race another call to SetActivity, interfering with waiting. - while (must_wait) { + // KernelCore::SuspendEmulation must be called from locked context, + // or we could race another call, interfering with waiting. + const auto TryWait = [&]() { KScopedSchedulerLock sl{*this}; - // Assume that all threads have finished running. - must_wait = false; - - for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { - if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() == - process.GetPointerUnsafe()) { - // A thread has not finished running yet. - // Continue waiting. - must_wait = true; + for (auto& process : processes) { + for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { + if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() == + process.GetPointerUnsafe()) { + // A thread has not finished running yet. + // Continue waiting. + return false; + } } } + + return true; + }; + + while (!TryWait()) { + // ... } } @@ -1260,7 +1275,7 @@ bool KernelCore::IsShuttingDown() const { void KernelCore::ExceptionalExitApplication() { exception_exited = true; - SuspendApplication(true); + SuspendEmulation(true); } void KernelCore::EnterSVCProfile() { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 8ea5bed1c..57182c0c8 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -258,8 +258,8 @@ public: /// Gets the shared memory object for HIDBus services. const Kernel::KSharedMemory& GetHidBusSharedMem() const; - /// Suspend/unsuspend application process. - void SuspendApplication(bool suspend); + /// Suspend/unsuspend emulated processes. + void SuspendEmulation(bool suspend); /// Exceptional exit application process. void ExceptionalExitApplication(); diff --git a/src/core/hle/kernel/svc/svc_code_memory.cpp b/src/core/hle/kernel/svc/svc_code_memory.cpp index bae4cb0cd..7be2802f0 100644 --- a/src/core/hle/kernel/svc/svc_code_memory.cpp +++ b/src/core/hle/kernel/svc/svc_code_memory.cpp @@ -45,7 +45,9 @@ Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t KCodeMemory* code_mem = KCodeMemory::Create(kernel); R_UNLESS(code_mem != nullptr, ResultOutOfResource); - SCOPE_EXIT({ code_mem->Close(); }); + SCOPE_EXIT { + code_mem->Close(); + }; // Verify that the region is in range. R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size), diff --git a/src/core/hle/kernel/svc/svc_device_address_space.cpp b/src/core/hle/kernel/svc/svc_device_address_space.cpp index 42add9473..ac828320f 100644 --- a/src/core/hle/kernel/svc/svc_device_address_space.cpp +++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp @@ -28,7 +28,9 @@ Result CreateDeviceAddressSpace(Core::System& system, Handle* out, uint64_t das_ // Create the device address space. KDeviceAddressSpace* das = KDeviceAddressSpace::Create(system.Kernel()); R_UNLESS(das != nullptr, ResultOutOfResource); - SCOPE_EXIT({ das->Close(); }); + SCOPE_EXIT { + das->Close(); + }; // Initialize the device address space. R_TRY(das->Initialize(das_address, das_size)); diff --git a/src/core/hle/kernel/svc/svc_event.cpp b/src/core/hle/kernel/svc/svc_event.cpp index 901202e6a..8e4beb396 100644 --- a/src/core/hle/kernel/svc/svc_event.cpp +++ b/src/core/hle/kernel/svc/svc_event.cpp @@ -72,10 +72,10 @@ Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { event_reservation.Commit(); // Ensure that we clean up the event (and its only references are handle table) on function end. - SCOPE_EXIT({ + SCOPE_EXIT { event->GetReadableEvent().Close(); event->Close(); - }); + }; // Register the event. KEvent::Register(kernel, event); diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp index 85cc4f561..b619bd70a 100644 --- a/src/core/hle/kernel/svc/svc_ipc.cpp +++ b/src/core/hle/kernel/svc/svc_ipc.cpp @@ -129,11 +129,11 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes } // Ensure handles are closed when we're done. - SCOPE_EXIT({ + SCOPE_EXIT { for (auto i = 0; i < num_handles; ++i) { objs[i]->Close(); } - }); + }; R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs, num_handles, reply_target, timeout_ns)); @@ -208,10 +208,10 @@ Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_ha event_reservation.Commit(); // At end of scope, kill the standing references to the sub events. - SCOPE_EXIT({ + SCOPE_EXIT { event->GetReadableEvent().Close(); event->Close(); - }); + }; // Register the event. KEvent::Register(system.Kernel(), event); diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp index 737749f7d..9a22dadaf 100644 --- a/src/core/hle/kernel/svc/svc_port.cpp +++ b/src/core/hle/kernel/svc/svc_port.cpp @@ -68,10 +68,10 @@ Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client, port->Initialize(max_sessions, is_light, name); // Ensure that we clean up the port (and its only references are handle table) on function end. - SCOPE_EXIT({ + SCOPE_EXIT { port->GetServerPort().Close(); port->GetClientPort().Close(); - }); + }; // Register the port. KPort::Register(kernel, port); @@ -150,10 +150,10 @@ Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t KPort::Register(system.Kernel(), port); // Ensure that our only reference to the port is in the handle table when we're done. - SCOPE_EXIT({ + SCOPE_EXIT { port->GetClientPort().Close(); port->GetServerPort().Close(); - }); + }; // Register the handle in the table. R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort()))); diff --git a/src/core/hle/kernel/svc/svc_resource_limit.cpp b/src/core/hle/kernel/svc/svc_resource_limit.cpp index c8e820b6a..6f3972482 100644 --- a/src/core/hle/kernel/svc/svc_resource_limit.cpp +++ b/src/core/hle/kernel/svc/svc_resource_limit.cpp @@ -18,7 +18,9 @@ Result CreateResourceLimit(Core::System& system, Handle* out_handle) { R_UNLESS(resource_limit != nullptr, ResultOutOfResource); // Ensure we don't leak a reference to the limit. - SCOPE_EXIT({ resource_limit->Close(); }); + SCOPE_EXIT { + resource_limit->Close(); + }; // Initialize the resource limit. resource_limit->Initialize(); diff --git a/src/core/hle/kernel/svc/svc_session.cpp b/src/core/hle/kernel/svc/svc_session.cpp index 2f5905f32..b034d21d1 100644 --- a/src/core/hle/kernel/svc/svc_session.cpp +++ b/src/core/hle/kernel/svc/svc_session.cpp @@ -69,10 +69,10 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien // Ensure that we clean up the session (and its only references are handle table) on function // end. - SCOPE_EXIT({ + SCOPE_EXIT { session->GetClientSession().Close(); session->GetServerSession().Close(); - }); + }; // Register the session. T::Register(system.Kernel(), session); diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp index 6c79cfd8d..fb03908d7 100644 --- a/src/core/hle/kernel/svc/svc_synchronization.cpp +++ b/src/core/hle/kernel/svc/svc_synchronization.cpp @@ -78,11 +78,11 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha } // Ensure handles are closed when we're done. - SCOPE_EXIT({ + SCOPE_EXIT { for (auto i = 0; i < num_handles; ++i) { objs[i]->Close(); } - }); + }; // Convert the timeout from nanoseconds to ticks. s64 timeout; diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp index 7681afa33..7517bb9d3 100644 --- a/src/core/hle/kernel/svc/svc_thread.cpp +++ b/src/core/hle/kernel/svc/svc_thread.cpp @@ -51,7 +51,9 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u // Create the thread. KThread* thread = KThread::Create(kernel); R_UNLESS(thread != nullptr, ResultOutOfResource) - SCOPE_EXIT({ thread->Close(); }); + SCOPE_EXIT { + thread->Close(); + }; // Initialize the thread. { diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp index 671bca23f..2ea0d4421 100644 --- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp +++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp @@ -52,7 +52,9 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64 R_UNLESS(trmem != nullptr, ResultOutOfResource); // Ensure the only reference is in the handle table when we're done. - SCOPE_EXIT({ trmem->Close(); }); + SCOPE_EXIT { + trmem->Close(); + }; // Ensure that the region is in range. R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory); diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 29a10ad13..ee9795532 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -329,9 +329,8 @@ bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& /// Returns if the system is allowing user registrations or not bool ProfileManager::CanSystemRegisterUser() const { - return false; // TODO(ogniK): Games shouldn't have - // access to user registration, when we - // emulate qlaunch. Update this to dynamically change. + // TODO: Both games and applets can register users. Determine when this condition is not meet. + return true; } bool ProfileManager::RemoveUser(UUID uuid) { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 8f90eba34..8c4e14f08 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -2,25 +2,19 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/am/am.h" -#include "core/hle/service/am/applet_ae.h" -#include "core/hle/service/am/applet_oe.h" -#include "core/hle/service/am/idle.h" -#include "core/hle/service/am/omm.h" -#include "core/hle/service/am/spsm.h" +#include "core/hle/service/am/service/all_system_applet_proxies_service.h" +#include "core/hle/service/am/service/application_proxy_service.h" #include "core/hle/service/server_manager.h" namespace Service::AM { -void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { +void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); server_manager->RegisterNamedService("appletAE", - std::make_shared<AppletAE>(nvnflinger, system)); + std::make_shared<IAllSystemAppletProxiesService>(system)); server_manager->RegisterNamedService("appletOE", - std::make_shared<AppletOE>(nvnflinger, system)); - server_manager->RegisterNamedService("idle:sys", std::make_shared<IdleSys>(system)); - server_manager->RegisterNamedService("omm", std::make_shared<OMM>(system)); - server_manager->RegisterNamedService("spsm", std::make_shared<SPSM>(system)); + std::make_shared<IApplicationProxyService>(system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 4a2d797bd..1afe253ae 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -7,12 +7,8 @@ namespace Core { class System; } -namespace Service::Nvnflinger { -class Nvnflinger; -} - namespace Service::AM { -void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system); +void LoopProcess(Core::System& system); } // namespace Service::AM diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h index a2b852b12..46afb3996 100644 --- a/src/core/hle/service/am/am_types.h +++ b/src/core/hle/service/am/am_types.h @@ -18,7 +18,7 @@ enum class AppletType { SystemApplet, }; -enum class GameplayRecordingState : u32 { +enum class GamePlayRecordingState : u32 { Disabled, Enabled, }; @@ -67,10 +67,9 @@ enum class ScreenshotPermission : u32 { }; struct FocusHandlingMode { - bool unknown0; - bool unknown1; - bool unknown2; - bool unknown3; + bool notify; + bool background; + bool suspend; }; enum class IdleTimeDetectionExtension : u32 { @@ -128,14 +127,53 @@ enum class AppletProgramId : u64 { MaxProgramId = 0x0100000000001FFFull, }; +// This is nn::am::AppletMessage +enum class AppletMessage : u32 { + None = 0, + ChangeIntoForeground = 1, + ChangeIntoBackground = 2, + Exit = 4, + ApplicationExited = 6, + FocusStateChanged = 15, + Resume = 16, + DetectShortPressingHomeButton = 20, + DetectLongPressingHomeButton = 21, + DetectShortPressingPowerButton = 22, + DetectMiddlePressingPowerButton = 23, + DetectLongPressingPowerButton = 24, + RequestToPrepareSleep = 25, + FinishedSleepSequence = 26, + SleepRequiredByHighTemperature = 27, + SleepRequiredByLowBattery = 28, + AutoPowerDown = 29, + OperationModeChanged = 30, + PerformanceModeChanged = 31, + DetectReceivingCecSystemStandby = 32, + SdCardRemoved = 33, + LaunchApplicationRequested = 50, + RequestToDisplay = 51, + ShowApplicationLogo = 55, + HideApplicationLogo = 56, + ForceHideApplicationLogo = 57, + FloatingApplicationDetected = 60, + DetectShortPressingCaptureButton = 90, + AlbumScreenShotTaken = 92, + AlbumRecordingSaved = 93, +}; + enum class LibraryAppletMode : u32 { AllForeground = 0, - Background = 1, - NoUI = 2, - BackgroundIndirectDisplay = 3, + PartialForeground = 1, + NoUi = 2, + PartialForegroundIndirectDisplay = 3, AllForegroundInitiallyHidden = 4, }; +enum class LaunchParameterKind : u32 { + UserChannel = 1, + AccountPreselectedUser = 2, +}; + enum class CommonArgumentVersion : u32 { Version0, Version1, @@ -152,6 +190,22 @@ enum class ThemeColor : u32 { BasicBlack = 3, }; +enum class InputDetectionPolicy : u32 { + Unknown0 = 0, + Unknown1 = 1, +}; + +enum class WindowOriginMode : u32 { + LowerLeft = 0, + UpperLeft = 1, +}; + +enum class ProgramSpecifyKind : u32 { + ExecuteProgram = 0, + JumpToSubApplicationProgramForDevelopment = 1, + RestartProgram = 2, +}; + struct CommonArguments { CommonArgumentVersion arguments_version; CommonArgumentSize size; @@ -169,6 +223,27 @@ struct AppletIdentityInfo { }; static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size."); +struct AppletAttribute { + u8 flag; + INSERT_PADDING_BYTES_NOINIT(0x7F); +}; +static_assert(sizeof(AppletAttribute) == 0x80, "AppletAttribute has incorrect size."); + +// This is nn::oe::DisplayVersion +struct DisplayVersion { + std::array<char, 0x10> string; +}; +static_assert(sizeof(DisplayVersion) == 0x10, "DisplayVersion has incorrect size."); + +// This is nn::pdm::ApplicationPlayStatistics +struct ApplicationPlayStatistics { + u64 application_id; + u64 play_time_ns; + u64 launch_count; +}; +static_assert(sizeof(ApplicationPlayStatistics) == 0x18, + "ApplicationPlayStatistics has incorrect size."); + using AppletResourceUserId = u64; using ProgramId = u64; diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h index b29ecdfed..ad602153e 100644 --- a/src/core/hle/service/am/applet.h +++ b/src/core/hle/service/am/applet.h @@ -3,7 +3,6 @@ #pragma once -#include <list> #include <mutex> #include "common/math_util.h" @@ -15,11 +14,9 @@ #include "core/hle/service/am/am_types.h" #include "core/hle/service/am/applet_message_queue.h" +#include "core/hle/service/am/display_layer_manager.h" #include "core/hle/service/am/hid_registration.h" -#include "core/hle/service/am/managed_layer_holder.h" #include "core/hle/service/am/process.h" -#include "core/hle/service/am/storage.h" -#include "core/hle/service/am/system_buffer_manager.h" namespace Service::AM { @@ -56,8 +53,7 @@ struct Applet { HidRegistration hid_registration; // vi state - SystemBufferManager system_buffer_manager{}; - ManagedLayerHolder managed_layer_holder{}; + DisplayLayerManager display_layer_manager{}; // Applet common functions Result terminate_result{}; @@ -76,8 +72,8 @@ struct Applet { u32 application_core_usage_mode{}; // Application functions - bool gameplay_recording_supported{}; - GameplayRecordingState gameplay_recording_state{GameplayRecordingState::Disabled}; + bool game_play_recording_supported{}; + GamePlayRecordingState game_play_recording_state{GamePlayRecordingState::Disabled}; bool jit_service_launched{}; bool is_running{}; bool application_crash_report_enabled{}; diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp deleted file mode 100644 index 1b715dea6..000000000 --- a/src/core/hle/service/am/applet_ae.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/applet_ae.h" -#include "core/hle/service/am/applet_manager.h" -#include "core/hle/service/am/library_applet_proxy.h" -#include "core/hle/service/am/system_applet_proxy.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_) - : ServiceFramework{system_, "appletAE"}, nvnflinger{nvnflinger_} { - // clang-format off - static const FunctionInfo functions[] = { - {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, - {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"}, - {201, &AppletAE::OpenLibraryAppletProxy, "OpenLibraryAppletProxy"}, - {300, nullptr, "OpenOverlayAppletProxy"}, - {350, nullptr, "OpenSystemApplicationProxy"}, - {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"}, - {410, nullptr, "GetSystemAppletControllerForDebug"}, - {1000, nullptr, "GetDebugFunctions"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -AppletAE::~AppletAE() = default; - -void AppletAE::OpenSystemAppletProxy(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - if (const auto applet = GetAppletFromContext(ctx)) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemAppletProxy>(nvnflinger, applet, system); - } else { - UNIMPLEMENTED(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - } -} - -void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - if (const auto applet = GetAppletFromContext(ctx)) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ILibraryAppletProxy>(nvnflinger, applet, system); - } else { - UNIMPLEMENTED(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - } -} - -void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - return OpenLibraryAppletProxy(ctx); -} - -std::shared_ptr<Applet> AppletAE::GetAppletFromContext(HLERequestContext& ctx) { - const auto aruid = ctx.GetPID(); - return system.GetAppletManager().GetByAppletResourceUserId(aruid); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h deleted file mode 100644 index 3d7961fa1..000000000 --- a/src/core/hle/service/am/applet_ae.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> - -#include "core/hle/service/service.h" - -namespace Service { -namespace FileSystem { -class FileSystemController; -} - -namespace Nvnflinger { -class Nvnflinger; -} - -namespace AM { - -struct Applet; - -class AppletAE final : public ServiceFramework<AppletAE> { -public: - explicit AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_); - ~AppletAE() override; - -private: - void OpenSystemAppletProxy(HLERequestContext& ctx); - void OpenLibraryAppletProxy(HLERequestContext& ctx); - void OpenLibraryAppletProxyOld(HLERequestContext& ctx); - - std::shared_ptr<Applet> GetAppletFromContext(HLERequestContext& ctx); - - Nvnflinger::Nvnflinger& nvnflinger; -}; - -} // namespace AM -} // namespace Service diff --git a/src/core/hle/service/am/applet_common_functions.cpp b/src/core/hle/service/am/applet_common_functions.cpp deleted file mode 100644 index 130614ae5..000000000 --- a/src/core/hle/service/am/applet_common_functions.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/applet.h" -#include "core/hle/service/am/applet_common_functions.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_, - std::shared_ptr<Applet> applet_) - : ServiceFramework{system_, "IAppletCommonFunctions"}, applet{std::move(applet_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "SetTerminateResult"}, - {10, nullptr, "ReadThemeStorage"}, - {11, nullptr, "WriteThemeStorage"}, - {20, nullptr, "PushToAppletBoundChannel"}, - {21, nullptr, "TryPopFromAppletBoundChannel"}, - {40, nullptr, "GetDisplayLogicalResolution"}, - {42, nullptr, "SetDisplayMagnification"}, - {50, nullptr, "SetHomeButtonDoubleClickEnabled"}, - {51, nullptr, "GetHomeButtonDoubleClickEnabled"}, - {52, nullptr, "IsHomeButtonShortPressedBlocked"}, - {60, nullptr, "IsVrModeCurtainRequired"}, - {61, nullptr, "IsSleepRequiredByHighTemperature"}, - {62, nullptr, "IsSleepRequiredByLowBattery"}, - {70, &IAppletCommonFunctions::SetCpuBoostRequestPriority, "SetCpuBoostRequestPriority"}, - {80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"}, - {81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"}, - {90, nullptr, "OpenNamedChannelAsParent"}, - {91, nullptr, "OpenNamedChannelAsChild"}, - {100, nullptr, "SetApplicationCoreUsageMode"}, - {300, &IAppletCommonFunctions::GetCurrentApplicationId, "GetCurrentApplicationId"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IAppletCommonFunctions::~IAppletCommonFunctions() = default; - -void IAppletCommonFunctions::SetCpuBoostRequestPriority(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::RequestParser rp{ctx}; - - std::scoped_lock lk{applet->lock}; - applet->cpu_boost_request_priority = rp.Pop<s32>(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IAppletCommonFunctions::GetCurrentApplicationId(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(system.GetApplicationProcessProgramID() & ~0xFFFULL); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_common_functions.h b/src/core/hle/service/am/applet_common_functions.h deleted file mode 100644 index b86adf5cb..000000000 --- a/src/core/hle/service/am/applet_common_functions.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -struct Applet; - -class IAppletCommonFunctions final : public ServiceFramework<IAppletCommonFunctions> { -public: - explicit IAppletCommonFunctions(Core::System& system_, std::shared_ptr<Applet> applet_); - ~IAppletCommonFunctions() override; - -private: - void SetCpuBoostRequestPriority(HLERequestContext& ctx); - void GetCurrentApplicationId(HLERequestContext& ctx); - - const std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_data_broker.cpp b/src/core/hle/service/am/applet_data_broker.cpp index 4d58c4db5..9057244a9 100644 --- a/src/core/hle/service/am/applet_data_broker.cpp +++ b/src/core/hle/service/am/applet_data_broker.cpp @@ -24,11 +24,11 @@ void AppletStorageChannel::Push(std::shared_ptr<IStorage> storage) { Result AppletStorageChannel::Pop(std::shared_ptr<IStorage>* out_storage) { std::scoped_lock lk{m_lock}; - SCOPE_EXIT({ + SCOPE_EXIT { if (m_data.empty()) { m_event.Clear(); } - }); + }; R_UNLESS(!m_data.empty(), AM::ResultNoDataInChannel); diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp index 52200d5b2..2e109181d 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -12,6 +12,7 @@ #include "core/hle/service/am/frontend/applet_controller.h" #include "core/hle/service/am/frontend/applet_mii_edit_types.h" #include "core/hle/service/am/frontend/applet_software_keyboard_types.h" +#include "core/hle/service/am/service/storage.h" #include "hid_core/hid_types.h" namespace Service::AM { @@ -34,6 +35,21 @@ AppletStorageChannel& InitializeFakeCallerApplet(Core::System& system, return applet->caller_applet_broker->GetInData(); } +void PushInShowQlaunch(Core::System& system, AppletStorageChannel& channel) { + const CommonArguments arguments{ + .arguments_version = CommonArgumentVersion::Version3, + .size = CommonArgumentSize::Version3, + .library_version = 0, + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + std::vector<u8> argument_data(sizeof(arguments)); + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + channel.Push(std::make_shared<IStorage>(system, std::move(argument_data))); +} + void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) { const CommonArguments arguments{ .arguments_version = CommonArgumentVersion::Version3, @@ -283,6 +299,9 @@ void AppletManager::CreateAndInsertByFrontendAppletParameters( // Starting from frontend, some applets require input data. switch (applet->applet_id) { + case AppletId::QLaunch: + PushInShowQlaunch(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; case AppletId::Cabinet: PushInShowCabinetData(m_system, InitializeFakeCallerApplet(m_system, applet)); break; @@ -303,8 +322,8 @@ void AppletManager::CreateAndInsertByFrontendAppletParameters( } // Applet was started by frontend, so it is foreground. - applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); - applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); + applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground); + applet->message_queue.PushMessage(AppletMessage::FocusStateChanged); applet->focus_state = FocusState::InFocus; this->InsertApplet(std::move(applet)); diff --git a/src/core/hle/service/am/applet_message_queue.cpp b/src/core/hle/service/am/applet_message_queue.cpp index 5ed996b70..83c3c5a55 100644 --- a/src/core/hle/service/am/applet_message_queue.cpp +++ b/src/core/hle/service/am/applet_message_queue.cpp @@ -33,7 +33,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) { on_new_message->Signal(); } -AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { +AppletMessage AppletMessageQueue::PopMessage() { std::scoped_lock lk{lock}; if (messages.empty()) { on_new_message->Clear(); diff --git a/src/core/hle/service/am/applet_message_queue.h b/src/core/hle/service/am/applet_message_queue.h index 5cb236d47..429b77d37 100644 --- a/src/core/hle/service/am/applet_message_queue.h +++ b/src/core/hle/service/am/applet_message_queue.h @@ -5,6 +5,7 @@ #include <queue> +#include "core/hle/service/am/am_types.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" @@ -16,40 +17,6 @@ namespace Service::AM { class AppletMessageQueue { public: - // This is nn::am::AppletMessage - enum class AppletMessage : u32 { - None = 0, - ChangeIntoForeground = 1, - ChangeIntoBackground = 2, - Exit = 4, - ApplicationExited = 6, - FocusStateChanged = 15, - Resume = 16, - DetectShortPressingHomeButton = 20, - DetectLongPressingHomeButton = 21, - DetectShortPressingPowerButton = 22, - DetectMiddlePressingPowerButton = 23, - DetectLongPressingPowerButton = 24, - RequestToPrepareSleep = 25, - FinishedSleepSequence = 26, - SleepRequiredByHighTemperature = 27, - SleepRequiredByLowBattery = 28, - AutoPowerDown = 29, - OperationModeChanged = 30, - PerformanceModeChanged = 31, - DetectReceivingCecSystemStandby = 32, - SdCardRemoved = 33, - LaunchApplicationRequested = 50, - RequestToDisplay = 51, - ShowApplicationLogo = 55, - HideApplicationLogo = 56, - ForceHideApplicationLogo = 57, - FloatingApplicationDetected = 60, - DetectShortPressingCaptureButton = 90, - AlbumScreenShotTaken = 92, - AlbumRecordingSaved = 93, - }; - explicit AppletMessageQueue(Core::System& system); ~AppletMessageQueue(); diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp deleted file mode 100644 index 56bafd162..000000000 --- a/src/core/hle/service/am/applet_oe.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applet_manager.h" -#include "core/hle/service/am/applet_oe.h" -#include "core/hle/service/am/application_proxy.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_) - : ServiceFramework{system_, "appletOE"}, nvnflinger{nvnflinger_} { - static const FunctionInfo functions[] = { - {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, - }; - RegisterHandlers(functions); -} - -AppletOE::~AppletOE() = default; - -void AppletOE::OpenApplicationProxy(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - if (const auto applet = GetAppletFromContext(ctx)) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IApplicationProxy>(nvnflinger, applet, system); - } else { - UNIMPLEMENTED(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - } -} - -std::shared_ptr<Applet> AppletOE::GetAppletFromContext(HLERequestContext& ctx) { - const auto aruid = ctx.GetPID(); - return system.GetAppletManager().GetByAppletResourceUserId(aruid); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h deleted file mode 100644 index f2ba1c924..000000000 --- a/src/core/hle/service/am/applet_oe.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> - -#include "core/hle/service/service.h" - -namespace Service { -namespace FileSystem { -class FileSystemController; -} - -namespace Nvnflinger { -class Nvnflinger; -} - -namespace AM { - -struct Applet; - -class AppletOE final : public ServiceFramework<AppletOE> { -public: - explicit AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_); - ~AppletOE() override; - -private: - void OpenApplicationProxy(HLERequestContext& ctx); - - std::shared_ptr<Applet> GetAppletFromContext(HLERequestContext& ctx); - - Nvnflinger::Nvnflinger& nvnflinger; -}; - -} // namespace AM -} // namespace Service diff --git a/src/core/hle/service/am/application_creator.cpp b/src/core/hle/service/am/application_creator.cpp deleted file mode 100644 index 79ea045a3..000000000 --- a/src/core/hle/service/am/application_creator.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/application_creator.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -IApplicationCreator::IApplicationCreator(Core::System& system_) - : ServiceFramework{system_, "IApplicationCreator"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "CreateApplication"}, - {1, nullptr, "PopLaunchRequestedApplication"}, - {10, nullptr, "CreateSystemApplication"}, - {100, nullptr, "PopFloatingApplicationForDevelopment"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IApplicationCreator::~IApplicationCreator() = default; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/application_creator.h b/src/core/hle/service/am/application_creator.h deleted file mode 100644 index 375a3c476..000000000 --- a/src/core/hle/service/am/application_creator.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -class IApplicationCreator final : public ServiceFramework<IApplicationCreator> { -public: - explicit IApplicationCreator(Core::System& system_); - ~IApplicationCreator() override; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/application_functions.cpp b/src/core/hle/service/am/application_functions.cpp deleted file mode 100644 index 51c5be2d1..000000000 --- a/src/core/hle/service/am/application_functions.cpp +++ /dev/null @@ -1,594 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/settings.h" -#include "common/uuid.h" -#include "core/file_sys/control_metadata.h" -#include "core/file_sys/patch_manager.h" -#include "core/file_sys/registered_cache.h" -#include "core/file_sys/savedata_factory.h" -#include "core/hle/service/acc/profile_manager.h" -#include "core/hle/service/am/am_results.h" -#include "core/hle/service/am/applet.h" -#include "core/hle/service/am/application_functions.h" -#include "core/hle/service/am/storage.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/filesystem/save_data_controller.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/ns/ns.h" -#include "core/hle/service/sm/sm.h" - -namespace Service::AM { - -enum class LaunchParameterKind : u32 { - UserChannel = 1, - AccountPreselectedUser = 2, -}; - -IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_) - : ServiceFramework{system_, "IApplicationFunctions"}, applet{std::move(applet_)} { - // clang-format off - static const FunctionInfo functions[] = { - {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, - {10, nullptr, "CreateApplicationAndPushAndRequestToStart"}, - {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, - {12, nullptr, "CreateApplicationAndRequestToStart"}, - {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"}, - {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"}, - {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"}, - {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, - {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, - {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, - {23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"}, - {24, nullptr, "GetLaunchStorageInfoForDebug"}, - {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, - {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, - {27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"}, - {28, &IApplicationFunctions::GetSaveDataSizeMax, "GetSaveDataSizeMax"}, - {29, nullptr, "GetCacheStorageMax"}, - {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, - {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, - {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, - {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, - {34, nullptr, "SelectApplicationLicense"}, - {35, nullptr, "GetDeviceSaveDataSizeMax"}, - {36, nullptr, "GetLimitedApplicationLicense"}, - {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"}, - {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, - {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, - {60, nullptr, "SetMediaPlaybackStateForApplication"}, - {65, &IApplicationFunctions::IsGamePlayRecordingSupported, "IsGamePlayRecordingSupported"}, - {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"}, - {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"}, - {68, nullptr, "RequestFlushGamePlayingMovieForDebug"}, - {70, nullptr, "RequestToShutdown"}, - {71, nullptr, "RequestToReboot"}, - {72, nullptr, "RequestToSleep"}, - {80, nullptr, "ExitAndRequestToShowThanksMessage"}, - {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"}, - {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"}, - {101, &IApplicationFunctions::SetApplicationCopyrightImage, "SetApplicationCopyrightImage"}, - {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"}, - {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"}, - {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"}, - {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"}, - {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"}, - {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"}, - {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"}, - {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, - {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, - {131, nullptr, "SetDelayTimeToAbortOnGpuError"}, - {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, - {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"}, - {150, &IApplicationFunctions::GetNotificationStorageChannelEvent, "GetNotificationStorageChannelEvent"}, - {151, nullptr, "TryPopFromNotificationStorageChannel"}, - {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"}, - {170, nullptr, "SetHdcpAuthenticationActivated"}, - {180, nullptr, "GetLaunchRequiredVersion"}, - {181, nullptr, "UpgradeLaunchRequiredVersion"}, - {190, nullptr, "SendServerMaintenanceOverlayNotification"}, - {200, nullptr, "GetLastApplicationExitReason"}, - {500, nullptr, "StartContinuousRecordingFlushForDebug"}, - {1000, nullptr, "CreateMovieMaker"}, - {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IApplicationFunctions::~IApplicationFunctions() = default; - -void IApplicationFunctions::EnableApplicationCrashReport(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - applet->application_crash_report_enabled = true; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::SetApplicationCopyrightImage(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::SetApplicationCopyrightVisibility(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto is_visible = rp.Pop<bool>(); - - LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", is_visible); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - applet->home_button_long_pressed_blocked = true; - applet->home_button_short_pressed_blocked = true; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - applet->home_button_long_pressed_blocked = false; - applet->home_button_short_pressed_blocked = false; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::BeginBlockingHomeButton(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - applet->home_button_long_pressed_blocked = true; - applet->home_button_short_pressed_blocked = true; - applet->home_button_double_click_enabled = true; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::EndBlockingHomeButton(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - applet->home_button_long_pressed_blocked = false; - applet->home_button_short_pressed_blocked = false; - applet->home_button_double_click_enabled = false; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto kind = rp.PopEnum<LaunchParameterKind>(); - - LOG_INFO(Service_AM, "called, kind={:08X}", kind); - - std::scoped_lock lk{applet->lock}; - - auto& channel = kind == LaunchParameterKind::UserChannel - ? applet->user_channel_launch_parameter - : applet->preselected_user_launch_parameter; - - if (channel.empty()) { - LOG_WARNING(Service_AM, "Attempted to pop parameter {} but none was found!", kind); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultNoDataInChannel); - return; - } - - auto data = channel.back(); - channel.pop_back(); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IStorage>(system, std::move(data)); -} - -void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - u128 user_id = rp.PopRaw<u128>(); - - LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); - - FileSys::SaveDataAttribute attribute{}; - attribute.title_id = applet->program_id; - attribute.user_id = user_id; - attribute.type = FileSys::SaveDataType::SaveData; - - FileSys::VirtualDir save_data{}; - const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData( - &save_data, FileSys::SaveDataSpaceId::NandUser, attribute); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.Push<u64>(0); -} - -void IApplicationFunctions::SetTerminateResult(HLERequestContext& ctx) { - // Takes an input u32 Result, no output. - // For example, in some cases official apps use this with error 0x2A2 then - // uses svcBreak. - - IPC::RequestParser rp{ctx}; - u32 result = rp.Pop<u32>(); - LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result); - - std::scoped_lock lk{applet->lock}; - applet->terminate_result = Result(result); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::GetDisplayVersion(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - std::array<u8, 0x10> version_string{}; - - const auto res = [this] { - const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(), - system.GetContentProvider()}; - auto metadata = pm.GetControlMetadata(); - if (metadata.first != nullptr) { - return metadata; - } - - const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id), - system.GetFileSystemController(), - system.GetContentProvider()}; - return pm_update.GetControlMetadata(); - }(); - - if (res.first != nullptr) { - const auto& version = res.first->GetVersionString(); - std::copy(version.begin(), version.end(), version_string.begin()); - } else { - static constexpr char default_version[]{"1.0.0"}; - std::memcpy(version_string.data(), default_version, sizeof(default_version)); - } - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(version_string); -} - -void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) { - // TODO(bunnei): This should be configurable - LOG_DEBUG(Service_AM, "called"); - - // Get supported languages from NACP, if possible - // Default to 0 (all languages supported) - u32 supported_languages = 0; - - const auto res = [this] { - const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(), - system.GetContentProvider()}; - auto metadata = pm.GetControlMetadata(); - if (metadata.first != nullptr) { - return metadata; - } - - const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id), - system.GetFileSystemController(), - system.GetContentProvider()}; - return pm_update.GetControlMetadata(); - }(); - - if (res.first != nullptr) { - supported_languages = res.first->GetSupportedLanguages(); - } - - // Call IApplicationManagerInterface implementation. - auto& service_manager = system.ServiceManager(); - auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2"); - auto app_man = ns_am2->GetApplicationManagerInterface(); - - // Get desired application language - u8 desired_language{}; - const auto res_lang = - app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages); - if (res_lang != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res_lang); - return; - } - - // Convert to settings language code. - u64 language_code{}; - const auto res_code = - app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language); - if (res_code != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res_code); - return; - } - - LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(language_code); -} - -void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(applet->gameplay_recording_supported); -} - -void IApplicationFunctions::InitializeGamePlayRecording(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::SetGamePlayRecordingState(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::RequestParser rp{ctx}; - - std::scoped_lock lk{applet->lock}; - applet->gameplay_recording_state = rp.PopRaw<GameplayRecordingState>(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::NotifyRunning(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - applet->is_running = true; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(0); // Unknown, seems to be ignored by official processes -} - -void IApplicationFunctions::GetPseudoDeviceId(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - - // Returns a 128-bit UUID - rb.Push<u64>(0); - rb.Push<u64>(0); -} - -void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) { - struct Parameters { - FileSys::SaveDataType type; - u128 user_id; - u64 new_normal_size; - u64 new_journal_size; - }; - static_assert(sizeof(Parameters) == 40); - - IPC::RequestParser rp{ctx}; - const auto [type, user_id, new_normal_size, new_journal_size] = rp.PopRaw<Parameters>(); - - LOG_DEBUG(Service_AM, - "called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, " - "new_journal={:016X}", - static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); - - system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize( - type, applet->program_id, user_id, {new_normal_size, new_journal_size}); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - - // The following value is used upon failure to help the system recover. - // Since we always succeed, this should be 0. - rb.Push<u64>(0); -} - -void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) { - struct Parameters { - FileSys::SaveDataType type; - u128 user_id; - }; - static_assert(sizeof(Parameters) == 24); - - IPC::RequestParser rp{ctx}; - const auto [type, user_id] = rp.PopRaw<Parameters>(); - - LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1], - user_id[0]); - - const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize( - type, applet->program_id, user_id); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(size.normal); - rb.Push(size.journal); -} - -void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) { - struct InputParameters { - u16 index; - s64 size; - s64 journal_size; - }; - static_assert(sizeof(InputParameters) == 24); - - struct OutputParameters { - u32 storage_target; - u64 required_size; - }; - static_assert(sizeof(OutputParameters) == 16); - - IPC::RequestParser rp{ctx}; - const auto params = rp.PopRaw<InputParameters>(); - - LOG_WARNING(Service_AM, "(STUBBED) called with index={}, size={:#x}, journal_size={:#x}", - params.index, params.size, params.journal_size); - - const OutputParameters resp{ - .storage_target = 1, - .required_size = 0, - }; - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(resp); -} - -void IApplicationFunctions::GetSaveDataSizeMax(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - constexpr u64 size_max_normal = 0xFFFFFFF; - constexpr u64 size_max_journal = 0xFFFFFFF; - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(size_max_normal); - rb.Push(size_max_journal); -} - -void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(0); -} - -void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(0); -} - -void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::RequestParser rp{ctx}; - [[maybe_unused]] const auto unk_1 = rp.Pop<u32>(); - [[maybe_unused]] const auto unk_2 = rp.Pop<u32>(); - const auto program_index = rp.Pop<u64>(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - - // Swap user channel ownership into the system so that it will be preserved - system.GetUserChannel().swap(applet->user_channel_launch_parameter); - system.ExecuteProgram(program_index); -} - -void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - applet->user_channel_launch_parameter.clear(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::RequestParser rp{ctx}; - const auto storage = rp.PopIpcInterface<IStorage>().lock(); - if (storage) { - applet->user_channel_launch_parameter.push_back(storage->GetData()); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::GetPreviousProgramIndex(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<s32>(applet->previous_program_index); -} - -void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->gpu_error_detected_event.GetHandle()); -} - -void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->friend_invitation_storage_channel_event.GetHandle()); -} - -void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultNoDataInChannel); -} - -void IApplicationFunctions::GetNotificationStorageChannelEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->notification_storage_channel_event.GetHandle()); -} - -void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->health_warning_disappeared_system_event.GetHandle()); -} - -void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - applet->jit_service_launched = true; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/application_functions.h b/src/core/hle/service/am/application_functions.h deleted file mode 100644 index 55eb21d39..000000000 --- a/src/core/hle/service/am/application_functions.h +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Service::AM { - -struct Applet; - -class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { -public: - explicit IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_); - ~IApplicationFunctions() override; - -private: - void PopLaunchParameter(HLERequestContext& ctx); - void CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx); - void EnsureSaveData(HLERequestContext& ctx); - void SetTerminateResult(HLERequestContext& ctx); - void GetDisplayVersion(HLERequestContext& ctx); - void GetDesiredLanguage(HLERequestContext& ctx); - void IsGamePlayRecordingSupported(HLERequestContext& ctx); - void InitializeGamePlayRecording(HLERequestContext& ctx); - void SetGamePlayRecordingState(HLERequestContext& ctx); - void NotifyRunning(HLERequestContext& ctx); - void GetPseudoDeviceId(HLERequestContext& ctx); - void ExtendSaveData(HLERequestContext& ctx); - void GetSaveDataSize(HLERequestContext& ctx); - void CreateCacheStorage(HLERequestContext& ctx); - void GetSaveDataSizeMax(HLERequestContext& ctx); - void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx); - void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx); - void BeginBlockingHomeButton(HLERequestContext& ctx); - void EndBlockingHomeButton(HLERequestContext& ctx); - void EnableApplicationCrashReport(HLERequestContext& ctx); - void InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx); - void SetApplicationCopyrightImage(HLERequestContext& ctx); - void SetApplicationCopyrightVisibility(HLERequestContext& ctx); - void QueryApplicationPlayStatistics(HLERequestContext& ctx); - void QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx); - void ExecuteProgram(HLERequestContext& ctx); - void ClearUserChannel(HLERequestContext& ctx); - void UnpopToUserChannel(HLERequestContext& ctx); - void GetPreviousProgramIndex(HLERequestContext& ctx); - void GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx); - void GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx); - void TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx); - void GetNotificationStorageChannelEvent(HLERequestContext& ctx); - void GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx); - void PrepareForJit(HLERequestContext& ctx); - - const std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/application_proxy.cpp b/src/core/hle/service/am/application_proxy.cpp deleted file mode 100644 index a6fd6d37f..000000000 --- a/src/core/hle/service/am/application_proxy.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/applet_common_functions.h" -#include "core/hle/service/am/application_functions.h" -#include "core/hle/service/am/application_proxy.h" -#include "core/hle/service/am/audio_controller.h" -#include "core/hle/service/am/common_state_getter.h" -#include "core/hle/service/am/debug_functions.h" -#include "core/hle/service/am/display_controller.h" -#include "core/hle/service/am/library_applet_creator.h" -#include "core/hle/service/am/library_applet_self_accessor.h" -#include "core/hle/service/am/process_winding_controller.h" -#include "core/hle/service/am/self_controller.h" -#include "core/hle/service/am/window_controller.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -IApplicationProxy::IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr<Applet> applet_, Core::System& system_) - : ServiceFramework{system_, "IApplicationProxy"}, nvnflinger{nvnflinger_}, applet{std::move( - applet_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, - {1, &IApplicationProxy::GetSelfController, "GetSelfController"}, - {2, &IApplicationProxy::GetWindowController, "GetWindowController"}, - {3, &IApplicationProxy::GetAudioController, "GetAudioController"}, - {4, &IApplicationProxy::GetDisplayController, "GetDisplayController"}, - {10, &IApplicationProxy::GetProcessWindingController, "GetProcessWindingController"}, - {11, &IApplicationProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, - {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"}, - {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IApplicationProxy::~IApplicationProxy() = default; - -void IApplicationProxy::GetAudioController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioController>(system); -} - -void IApplicationProxy::GetDisplayController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDisplayController>(system, applet); -} - -void IApplicationProxy::GetProcessWindingController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IProcessWindingController>(system, applet); -} - -void IApplicationProxy::GetDebugFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDebugFunctions>(system); -} - -void IApplicationProxy::GetWindowController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IWindowController>(system, applet); -} - -void IApplicationProxy::GetSelfController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger); -} - -void IApplicationProxy::GetCommonStateGetter(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ICommonStateGetter>(system, applet); -} - -void IApplicationProxy::GetLibraryAppletCreator(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ILibraryAppletCreator>(system, applet); -} - -void IApplicationProxy::GetApplicationFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IApplicationFunctions>(system, applet); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/application_proxy.h b/src/core/hle/service/am/application_proxy.h deleted file mode 100644 index eb98b095c..000000000 --- a/src/core/hle/service/am/application_proxy.h +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -struct Applet; - -class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { -public: - explicit IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr<Applet> msg_queue_, Core::System& system_); - ~IApplicationProxy(); - -private: - void GetAudioController(HLERequestContext& ctx); - void GetDisplayController(HLERequestContext& ctx); - void GetProcessWindingController(HLERequestContext& ctx); - void GetDebugFunctions(HLERequestContext& ctx); - void GetWindowController(HLERequestContext& ctx); - void GetSelfController(HLERequestContext& ctx); - void GetCommonStateGetter(HLERequestContext& ctx); - void GetLibraryAppletCreator(HLERequestContext& ctx); - void GetApplicationFunctions(HLERequestContext& ctx); - - Nvnflinger::Nvnflinger& nvnflinger; - std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/audio_controller.cpp b/src/core/hle/service/am/audio_controller.cpp deleted file mode 100644 index ae75db174..000000000 --- a/src/core/hle/service/am/audio_controller.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/audio_controller.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -IAudioController::IAudioController(Core::System& system_) - : ServiceFramework{system_, "IAudioController"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"}, - {1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"}, - {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"}, - {3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"}, - {4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IAudioController::~IAudioController() = default; - -void IAudioController::SetExpectedMasterVolume(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const float main_applet_volume_tmp = rp.Pop<float>(); - const float library_applet_volume_tmp = rp.Pop<float>(); - - LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}", - main_applet_volume_tmp, library_applet_volume_tmp); - - // Ensure the volume values remain within the 0-100% range - main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume); - library_applet_volume = - std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IAudioController::GetMainAppletExpectedMasterVolume(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(main_applet_volume); -} - -void IAudioController::GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(library_applet_volume); -} - -void IAudioController::ChangeMainAppletMasterVolume(HLERequestContext& ctx) { - struct Parameters { - float volume; - s64 fade_time_ns; - }; - static_assert(sizeof(Parameters) == 16); - - IPC::RequestParser rp{ctx}; - const auto parameters = rp.PopRaw<Parameters>(); - - LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume, - parameters.fade_time_ns); - - main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume); - fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns}; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IAudioController::SetTransparentAudioRate(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const float transparent_volume_rate_tmp = rp.Pop<float>(); - - LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp); - - // Clamp volume range to 0-100%. - transparent_volume_rate = - std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/audio_controller.h b/src/core/hle/service/am/audio_controller.h deleted file mode 100644 index a47e3bad8..000000000 --- a/src/core/hle/service/am/audio_controller.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -class IAudioController final : public ServiceFramework<IAudioController> { -public: - explicit IAudioController(Core::System& system_); - ~IAudioController() override; - -private: - void SetExpectedMasterVolume(HLERequestContext& ctx); - void GetMainAppletExpectedMasterVolume(HLERequestContext& ctx); - void GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx); - void ChangeMainAppletMasterVolume(HLERequestContext& ctx); - void SetTransparentAudioRate(HLERequestContext& ctx); - - static constexpr float min_allowed_volume = 0.0f; - static constexpr float max_allowed_volume = 1.0f; - - float main_applet_volume{0.25f}; - float library_applet_volume{max_allowed_volume}; - float transparent_volume_rate{min_allowed_volume}; - - // Volume transition fade time in nanoseconds. - // e.g. If the main applet volume was 0% and was changed to 50% - // with a fade of 50ns, then over the course of 50ns, - // the volume will gradually fade up to 50% - std::chrono::nanoseconds fade_time_ns{0}; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/common_state_getter.cpp b/src/core/hle/service/am/common_state_getter.cpp deleted file mode 100644 index 937ac0beb..000000000 --- a/src/core/hle/service/am/common_state_getter.cpp +++ /dev/null @@ -1,314 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/settings.h" -#include "core/hle/service/am/am_results.h" -#include "core/hle/service/am/applet.h" -#include "core/hle/service/am/common_state_getter.h" -#include "core/hle/service/am/lock_accessor.h" -#include "core/hle/service/apm/apm_controller.h" -#include "core/hle/service/apm/apm_interface.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/pm/pm.h" -#include "core/hle/service/sm/sm.h" -#include "core/hle/service/vi/vi.h" - -namespace Service::AM { - -ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_) - : ServiceFramework{system_, "ICommonStateGetter"}, applet{std::move(applet_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, - {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, - {2, nullptr, "GetThisAppletKind"}, - {3, nullptr, "AllowToEnterSleep"}, - {4, nullptr, "DisallowToEnterSleep"}, - {5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"}, - {6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"}, - {7, nullptr, "GetCradleStatus"}, - {8, &ICommonStateGetter::GetBootMode, "GetBootMode"}, - {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"}, - {10, &ICommonStateGetter::RequestToAcquireSleepLock, "RequestToAcquireSleepLock"}, - {11, nullptr, "ReleaseSleepLock"}, - {12, nullptr, "ReleaseSleepLockTransiently"}, - {13, &ICommonStateGetter::GetAcquiredSleepLockEvent, "GetAcquiredSleepLockEvent"}, - {14, nullptr, "GetWakeupCount"}, - {20, nullptr, "PushToGeneralChannel"}, - {30, nullptr, "GetHomeButtonReaderLockAccessor"}, - {31, &ICommonStateGetter::GetReaderLockAccessorEx, "GetReaderLockAccessorEx"}, - {32, nullptr, "GetWriterLockAccessorEx"}, - {40, nullptr, "GetCradleFwVersion"}, - {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, - {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"}, - {52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"}, - {53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"}, - {54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"}, - {55, nullptr, "IsInControllerFirmwareUpdateSection"}, - {59, nullptr, "SetVrPositionForDebug"}, - {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, - {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, - {62, nullptr, "GetHdcpAuthenticationState"}, - {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, - {64, nullptr, "SetTvPowerStateMatchingMode"}, - {65, nullptr, "GetApplicationIdByContentActionName"}, - {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, - {67, nullptr, "CancelCpuBoostMode"}, - {68, &ICommonStateGetter::GetBuiltInDisplayType, "GetBuiltInDisplayType"}, - {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"}, - {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, - {91, nullptr, "GetCurrentPerformanceConfiguration"}, - {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"}, - {110, nullptr, "OpenMyGpuErrorHandler"}, - {120, &ICommonStateGetter::GetAppletLaunchedHistory, "GetAppletLaunchedHistory"}, - {200, nullptr, "GetOperationModeSystemInfo"}, - {300, &ICommonStateGetter::GetSettingsPlatformRegion, "GetSettingsPlatformRegion"}, - {400, nullptr, "ActivateMigrationService"}, - {401, nullptr, "DeactivateMigrationService"}, - {500, nullptr, "DisableSleepTillShutdown"}, - {501, nullptr, "SuppressDisablingSleepTemporarily"}, - {502, nullptr, "IsSleepEnabled"}, - {503, nullptr, "IsDisablingSleepSuppressed"}, - {900, &ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -ICommonStateGetter::~ICommonStateGetter() = default; - -void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode -} - -void ICommonStateGetter::GetEventHandle(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->message_queue.GetMessageReceiveEvent()); -} - -void ICommonStateGetter::ReceiveMessage(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - const auto message = applet->message_queue.PopMessage(); - IPC::ResponseBuilder rb{ctx, 3}; - - if (message == AppletMessageQueue::AppletMessage::None) { - LOG_ERROR(Service_AM, "Message queue is empty"); - rb.Push(AM::ResultNoMessages); - rb.PushEnum<AppletMessageQueue::AppletMessage>(message); - return; - } - - rb.Push(ResultSuccess); - rb.PushEnum<AppletMessageQueue::AppletMessage>(message); -} - -void ICommonStateGetter::GetCurrentFocusState(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u8>(applet->focus_state)); -} - -void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) { - const bool use_docked_mode{Settings::IsDockedMode()}; - LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld)); -} - -void ICommonStateGetter::GetPerformanceMode(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(system.GetAPMController().GetCurrentPerformanceMode()); -} - -void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - // Sleep lock is acquired immediately. - applet->sleep_lock_event.Signal(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::GetReaderLockAccessorEx(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto unknown = rp.Pop<u32>(); - - LOG_INFO(Service_AM, "called, unknown={}", unknown); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - - rb.Push(ResultSuccess); - rb.PushIpcInterface<ILockAccessor>(system); -} - -void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->sleep_lock_event.GetHandle()); -} - -void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - std::scoped_lock lk{applet->lock}; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(applet->vr_mode_enabled); -} - -void ICommonStateGetter::SetVrModeEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - std::scoped_lock lk{applet->lock}; - applet->vr_mode_enabled = rp.Pop<bool>(); - LOG_WARNING(Service_AM, "VR Mode is {}", applet->vr_mode_enabled ? "on" : "off"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::SetLcdBacklighOffEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto is_lcd_backlight_off_enabled = rp.Pop<bool>(); - - LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}", - is_lcd_backlight_off_enabled); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::BeginVrModeEx(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - applet->vr_mode_enabled = true; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::EndVrModeEx(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - applet->vr_mode_enabled = false; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->message_queue.GetOperationModeChangedEvent()); -} - -void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - - if (Settings::IsDockedMode()) { - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); - } else { - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); - } -} - -void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS"); - - const auto& sm = system.ServiceManager(); - const auto apm_sys = sm.GetService<APM::APM_Sys>("apm:sys"); - ASSERT(apm_sys != nullptr); - - apm_sys->SetCpuBoostMode(ctx); -} - -void ICommonStateGetter::GetBuiltInDisplayType(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); -} - -void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto system_button{rp.PopEnum<SystemButtonType>()}; - - LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::GetAppletLaunchedHistory(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::shared_ptr<Applet> current_applet = applet; - std::vector<AppletId> result; - - const size_t count = ctx.GetWriteBufferNumElements<AppletId>(); - size_t i; - - for (i = 0; i < count && current_applet != nullptr; i++) { - result.push_back(current_applet->applet_id); - current_applet = current_applet->caller_applet.lock(); - } - - ctx.WriteBuffer(result); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(i)); -} - -void ICommonStateGetter::GetSettingsPlatformRegion(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(SysPlatformRegion::Global); -} - -void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled( - HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - applet->request_exit_to_library_applet_at_execute_next_program_enabled = true; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/common_state_getter.h b/src/core/hle/service/am/common_state_getter.h deleted file mode 100644 index bf652790c..000000000 --- a/src/core/hle/service/am/common_state_getter.h +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -#include "core/hle/service/am/applet_message_queue.h" - -namespace Service::AM { - -struct Applet; - -class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { -public: - explicit ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_); - ~ICommonStateGetter() override; - -private: - // This is nn::oe::FocusState - enum class FocusState : u8 { - InFocus = 1, - NotInFocus = 2, - Background = 3, - }; - - // This is nn::oe::OperationMode - enum class OperationMode : u8 { - Handheld = 0, - Docked = 1, - }; - - // This is nn::am::service::SystemButtonType - enum class SystemButtonType { - None, - HomeButtonShortPressing, - HomeButtonLongPressing, - PowerButtonShortPressing, - PowerButtonLongPressing, - ShutdownSystem, - CaptureButtonShortPressing, - CaptureButtonLongPressing, - }; - - enum class SysPlatformRegion : s32 { - Global = 1, - Terra = 2, - }; - - void GetEventHandle(HLERequestContext& ctx); - void ReceiveMessage(HLERequestContext& ctx); - void GetCurrentFocusState(HLERequestContext& ctx); - void RequestToAcquireSleepLock(HLERequestContext& ctx); - void GetAcquiredSleepLockEvent(HLERequestContext& ctx); - void GetReaderLockAccessorEx(HLERequestContext& ctx); - void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx); - void GetOperationMode(HLERequestContext& ctx); - void GetPerformanceMode(HLERequestContext& ctx); - void GetBootMode(HLERequestContext& ctx); - void IsVrModeEnabled(HLERequestContext& ctx); - void SetVrModeEnabled(HLERequestContext& ctx); - void SetLcdBacklighOffEnabled(HLERequestContext& ctx); - void BeginVrModeEx(HLERequestContext& ctx); - void EndVrModeEx(HLERequestContext& ctx); - void GetDefaultDisplayResolution(HLERequestContext& ctx); - void SetCpuBoostMode(HLERequestContext& ctx); - void GetBuiltInDisplayType(HLERequestContext& ctx); - void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx); - void GetAppletLaunchedHistory(HLERequestContext& ctx); - void GetSettingsPlatformRegion(HLERequestContext& ctx); - void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx); - - const std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/debug_functions.cpp b/src/core/hle/service/am/debug_functions.cpp deleted file mode 100644 index f80b970f2..000000000 --- a/src/core/hle/service/am/debug_functions.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/debug_functions.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -IDebugFunctions::IDebugFunctions(Core::System& system_) - : ServiceFramework{system_, "IDebugFunctions"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "NotifyMessageToHomeMenuForDebug"}, - {1, nullptr, "OpenMainApplication"}, - {10, nullptr, "PerformSystemButtonPressing"}, - {20, nullptr, "InvalidateTransitionLayer"}, - {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, - {31, nullptr, "RequestLaunchApplicationByApplicationLaunchInfoForDebug"}, - {40, nullptr, "GetAppletResourceUsageInfo"}, - {50, nullptr, "AddSystemProgramIdAndAppletIdForDebug"}, - {51, nullptr, "AddOperationConfirmedLibraryAppletIdForDebug"}, - {100, nullptr, "SetCpuBoostModeForApplet"}, - {101, nullptr, "CancelCpuBoostModeForApplet"}, - {110, nullptr, "PushToAppletBoundChannelForDebug"}, - {111, nullptr, "TryPopFromAppletBoundChannelForDebug"}, - {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"}, - {121, nullptr, "AlarmSettingNotificationDisableAppEventReserve"}, - {122, nullptr, "AlarmSettingNotificationPushAppEventNotify"}, - {130, nullptr, "FriendInvitationSetApplicationParameter"}, - {131, nullptr, "FriendInvitationClearApplicationParameter"}, - {132, nullptr, "FriendInvitationPushApplicationParameter"}, - {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"}, - {200, nullptr, "CreateFloatingLibraryAppletAccepterForDebug"}, - {300, nullptr, "TerminateAllRunningApplicationsForDebug"}, - {900, nullptr, "GetGrcProcessLaunchedSystemEvent"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IDebugFunctions::~IDebugFunctions() = default; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/display_controller.cpp b/src/core/hle/service/am/display_controller.cpp deleted file mode 100644 index 4d6858348..000000000 --- a/src/core/hle/service/am/display_controller.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/applet.h" -#include "core/hle/service/am/display_controller.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -namespace { -struct OutputParameters { - bool was_written; - s32 fbshare_layer_index; -}; - -static_assert(sizeof(OutputParameters) == 8, "OutputParameters has wrong size"); -} // namespace - -IDisplayController::IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_) - : ServiceFramework{system_, "IDisplayController"}, applet(std::move(applet_)) { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetLastForegroundCaptureImage"}, - {1, nullptr, "UpdateLastForegroundCaptureImage"}, - {2, nullptr, "GetLastApplicationCaptureImage"}, - {3, nullptr, "GetCallerAppletCaptureImage"}, - {4, nullptr, "UpdateCallerAppletCaptureImage"}, - {5, nullptr, "GetLastForegroundCaptureImageEx"}, - {6, nullptr, "GetLastApplicationCaptureImageEx"}, - {7, &IDisplayController::GetCallerAppletCaptureImageEx, "GetCallerAppletCaptureImageEx"}, - {8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"}, - {9, nullptr, "CopyBetweenCaptureBuffers"}, - {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, - {11, nullptr, "ReleaseLastApplicationCaptureBuffer"}, - {12, nullptr, "AcquireLastForegroundCaptureBuffer"}, - {13, nullptr, "ReleaseLastForegroundCaptureBuffer"}, - {14, nullptr, "AcquireCallerAppletCaptureBuffer"}, - {15, nullptr, "ReleaseCallerAppletCaptureBuffer"}, - {16, nullptr, "AcquireLastApplicationCaptureBufferEx"}, - {17, nullptr, "AcquireLastForegroundCaptureBufferEx"}, - {18, nullptr, "AcquireCallerAppletCaptureBufferEx"}, - {20, nullptr, "ClearCaptureBuffer"}, - {21, nullptr, "ClearAppletTransitionBuffer"}, - {22, &IDisplayController::AcquireLastApplicationCaptureSharedBuffer, "AcquireLastApplicationCaptureSharedBuffer"}, - {23, &IDisplayController::ReleaseLastApplicationCaptureSharedBuffer, "ReleaseLastApplicationCaptureSharedBuffer"}, - {24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"}, - {25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"}, - {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"}, - {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"}, - {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IDisplayController::~IDisplayController() = default; - -void IDisplayController::GetCallerAppletCaptureImageEx(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - OutputParameters params{}; - const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer( - ¶ms.was_written, ¶ms.fbshare_layer_index); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.PushRaw(params); -} - -void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IDisplayController::AcquireLastApplicationCaptureSharedBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - OutputParameters params{}; - const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer( - ¶ms.was_written, ¶ms.fbshare_layer_index); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.PushRaw(params); -} - -void IDisplayController::ReleaseLastApplicationCaptureSharedBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - OutputParameters params{}; - const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer( - ¶ms.was_written, ¶ms.fbshare_layer_index); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.PushRaw(params); -} - -void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - OutputParameters params{}; - const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer( - ¶ms.was_written, ¶ms.fbshare_layer_index); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.PushRaw(params); -} - -void IDisplayController::ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/display_controller.h b/src/core/hle/service/am/display_controller.h deleted file mode 100644 index 75172580c..000000000 --- a/src/core/hle/service/am/display_controller.h +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -struct Applet; - -class IDisplayController final : public ServiceFramework<IDisplayController> { -public: - explicit IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_); - ~IDisplayController() override; - -private: - void GetCallerAppletCaptureImageEx(HLERequestContext& ctx); - void TakeScreenShotOfOwnLayer(HLERequestContext& ctx); - void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); - void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); - void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); - void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); - void AcquireLastApplicationCaptureSharedBuffer(HLERequestContext& ctx); - void ReleaseLastApplicationCaptureSharedBuffer(HLERequestContext& ctx); - - const std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/display_layer_manager.cpp b/src/core/hle/service/am/display_layer_manager.cpp new file mode 100644 index 000000000..85ff6fb88 --- /dev/null +++ b/src/core/hle/service/am/display_layer_manager.cpp @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/am/display_layer_manager.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/vi/application_display_service.h" +#include "core/hle/service/vi/container.h" +#include "core/hle/service/vi/manager_display_service.h" +#include "core/hle/service/vi/manager_root_service.h" +#include "core/hle/service/vi/shared_buffer_manager.h" +#include "core/hle/service/vi/vi_results.h" +#include "core/hle/service/vi/vi_types.h" + +namespace Service::AM { + +DisplayLayerManager::DisplayLayerManager() = default; +DisplayLayerManager::~DisplayLayerManager() { + this->Finalize(); +} + +void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* process, + AppletId applet_id, LibraryAppletMode mode) { + R_ASSERT(system.ServiceManager() + .GetService<VI::IManagerRootService>("vi:m", true) + ->GetDisplayService(&m_display_service, VI::Policy::Compositor)); + R_ASSERT(m_display_service->GetManagerDisplayService(&m_manager_display_service)); + + m_process = process; + m_system_shared_buffer_id = 0; + m_system_shared_layer_id = 0; + m_applet_id = applet_id; + m_buffer_sharing_enabled = false; + m_blending_enabled = mode == LibraryAppletMode::PartialForeground || + mode == LibraryAppletMode::PartialForegroundIndirectDisplay; +} + +void DisplayLayerManager::Finalize() { + if (!m_manager_display_service) { + return; + } + + // Clean up managed layers. + for (const auto& layer : m_managed_display_layers) { + m_manager_display_service->DestroyManagedLayer(layer); + } + + for (const auto& layer : m_managed_display_recording_layers) { + m_manager_display_service->DestroyManagedLayer(layer); + } + + // Clean up shared layers. + if (m_buffer_sharing_enabled) { + m_manager_display_service->DestroySharedLayerSession(m_process); + } + + m_manager_display_service = nullptr; + m_display_service = nullptr; +} + +Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) { + R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed); + + // TODO(Subv): Find out how AM determines the display to use, for now just + // create the layer in the Default display. + u64 display_id; + R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"})); + R_TRY(m_manager_display_service->CreateManagedLayer( + out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()})); + + m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id); + m_managed_display_layers.emplace(*out_layer_id); + + R_SUCCEED(); +} + +Result DisplayLayerManager::CreateManagedDisplaySeparableLayer(u64* out_layer_id, + u64* out_recording_layer_id) { + R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed); + + // TODO(Subv): Find out how AM determines the display to use, for now just + // create the layer in the Default display. + // This calls nn::vi::CreateRecordingLayer() which creates another layer. + // Currently we do not support more than 1 layer per display, output 1 layer id for now. + // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse + // side effects. + *out_recording_layer_id = 0; + R_RETURN(this->CreateManagedDisplayLayer(out_layer_id)); +} + +Result DisplayLayerManager::IsSystemBufferSharingEnabled() { + // Succeed if already enabled. + R_SUCCEED_IF(m_buffer_sharing_enabled); + + // Ensure we can access shared layers. + R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed); + R_UNLESS(m_applet_id != AppletId::Application, VI::ResultPermissionDenied); + + // Create the shared layer. + u64 display_id; + R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"})); + R_TRY(m_manager_display_service->CreateSharedLayerSession(m_process, &m_system_shared_buffer_id, + &m_system_shared_layer_id, display_id, + m_blending_enabled)); + + // We succeeded, so set up remaining state. + m_buffer_sharing_enabled = true; + m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id); + R_SUCCEED(); +} + +Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id, + u64* out_system_shared_layer_id) { + R_TRY(this->IsSystemBufferSharingEnabled()); + + *out_system_shared_buffer_id = m_system_shared_buffer_id; + *out_system_shared_layer_id = m_system_shared_layer_id; + + R_SUCCEED(); +} + +void DisplayLayerManager::SetWindowVisibility(bool visible) { + if (m_visible == visible) { + return; + } + + m_visible = visible; + + if (m_manager_display_service) { + if (m_system_shared_layer_id) { + m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id); + } + + for (const auto layer_id : m_managed_display_layers) { + m_manager_display_service->SetLayerVisibility(m_visible, layer_id); + } + } +} + +bool DisplayLayerManager::GetWindowVisibility() const { + return m_visible; +} + +Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written, + s32* out_fbshare_layer_index) { + R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied); + R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer( + out_was_written, out_fbshare_layer_index)); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/display_layer_manager.h b/src/core/hle/service/am/display_layer_manager.h new file mode 100644 index 000000000..a66509c04 --- /dev/null +++ b/src/core/hle/service/am/display_layer_manager.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <set> + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am_types.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KProcess; +} + +namespace Service::VI { +class IApplicationDisplayService; +class IManagerDisplayService; +} // namespace Service::VI + +namespace Service::AM { + +class DisplayLayerManager { +public: + explicit DisplayLayerManager(); + ~DisplayLayerManager(); + + void Initialize(Core::System& system, Kernel::KProcess* process, AppletId applet_id, + LibraryAppletMode mode); + void Finalize(); + + Result CreateManagedDisplayLayer(u64* out_layer_id); + Result CreateManagedDisplaySeparableLayer(u64* out_layer_id, u64* out_recording_layer_id); + + Result IsSystemBufferSharingEnabled(); + Result GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id, + u64* out_system_shared_layer_id); + + void SetWindowVisibility(bool visible); + bool GetWindowVisibility() const; + + Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index); + +private: + Kernel::KProcess* m_process{}; + std::shared_ptr<VI::IApplicationDisplayService> m_display_service{}; + std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{}; + std::set<u64> m_managed_display_layers{}; + std::set<u64> m_managed_display_recording_layers{}; + u64 m_system_shared_buffer_id{}; + u64 m_system_shared_layer_id{}; + AppletId m_applet_id{}; + bool m_buffer_sharing_enabled{}; + bool m_blending_enabled{}; + bool m_visible{true}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/frontend/applet_cabinet.cpp b/src/core/hle/service/am/frontend/applet_cabinet.cpp index 0862c81b6..4cbc80d63 100644 --- a/src/core/hle/service/am/frontend/applet_cabinet.cpp +++ b/src/core/hle/service/am/frontend/applet_cabinet.cpp @@ -9,7 +9,7 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/frontend/applet_cabinet.h" -#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/service/storage.h" #include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/nfc/common/device.h" #include "hid_core/hid_core.h" diff --git a/src/core/hle/service/am/frontend/applet_controller.cpp b/src/core/hle/service/am/frontend/applet_controller.cpp index bd3e49fc4..66f52686d 100644 --- a/src/core/hle/service/am/frontend/applet_controller.cpp +++ b/src/core/hle/service/am/frontend/applet_controller.cpp @@ -12,7 +12,7 @@ #include "core/hle/result.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/frontend/applet_controller.h" -#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/service/storage.h" #include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" #include "hid_core/hid_types.h" diff --git a/src/core/hle/service/am/frontend/applet_error.cpp b/src/core/hle/service/am/frontend/applet_error.cpp index b97a5f3ea..34ec7013b 100644 --- a/src/core/hle/service/am/frontend/applet_error.cpp +++ b/src/core/hle/service/am/frontend/applet_error.cpp @@ -10,7 +10,7 @@ #include "core/frontend/applets/error.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/frontend/applet_error.h" -#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/service/storage.h" #include "core/reporter.h" namespace Service::AM::Frontend { diff --git a/src/core/hle/service/am/frontend/applet_general.cpp b/src/core/hle/service/am/frontend/applet_general.cpp index 3c091a602..d2cabb7b5 100644 --- a/src/core/hle/service/am/frontend/applet_general.cpp +++ b/src/core/hle/service/am/frontend/applet_general.cpp @@ -10,7 +10,7 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_data_broker.h" #include "core/hle/service/am/frontend/applet_general.h" -#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/service/storage.h" #include "core/reporter.h" namespace Service::AM::Frontend { diff --git a/src/core/hle/service/am/frontend/applet_mii_edit.cpp b/src/core/hle/service/am/frontend/applet_mii_edit.cpp index e3d19fb3d..0180ab761 100644 --- a/src/core/hle/service/am/frontend/applet_mii_edit.cpp +++ b/src/core/hle/service/am/frontend/applet_mii_edit.cpp @@ -7,7 +7,7 @@ #include "core/frontend/applets/mii_edit.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/frontend/applet_mii_edit.h" -#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/service/storage.h" #include "core/hle/service/mii/mii.h" #include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/sm/sm.h" diff --git a/src/core/hle/service/am/frontend/applet_profile_select.cpp b/src/core/hle/service/am/frontend/applet_profile_select.cpp index efb4053b8..89b5a1eab 100644 --- a/src/core/hle/service/am/frontend/applet_profile_select.cpp +++ b/src/core/hle/service/am/frontend/applet_profile_select.cpp @@ -10,7 +10,7 @@ #include "core/hle/service/acc/errors.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/frontend/applet_profile_select.h" -#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/service/storage.h" namespace Service::AM::Frontend { diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.cpp b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp index fbf75d379..d1bc03018 100644 --- a/src/core/hle/service/am/frontend/applet_software_keyboard.cpp +++ b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp @@ -6,7 +6,7 @@ #include "core/frontend/applets/software_keyboard.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/frontend/applet_software_keyboard.h" -#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/service/storage.h" namespace Service::AM::Frontend { @@ -68,9 +68,9 @@ void SoftwareKeyboard::Initialize() { case LibraryAppletMode::AllForeground: InitializeForeground(); break; - case LibraryAppletMode::Background: - case LibraryAppletMode::BackgroundIndirectDisplay: - InitializeBackground(applet_mode); + case LibraryAppletMode::PartialForeground: + case LibraryAppletMode::PartialForegroundIndirectDisplay: + InitializePartialForeground(applet_mode); break; default: ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode); @@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() { InitializeFrontendNormalKeyboard(); } -void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { +void SoftwareKeyboard::InitializePartialForeground(LibraryAppletMode library_applet_mode) { LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); is_background = true; @@ -258,9 +258,9 @@ void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mod swkbd_inline_initialize_arg.size()); if (swkbd_initialize_arg.library_applet_mode_flag) { - ASSERT(library_applet_mode == LibraryAppletMode::Background); + ASSERT(library_applet_mode == LibraryAppletMode::PartialForeground); } else { - ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); + ASSERT(library_applet_mode == LibraryAppletMode::PartialForegroundIndirectDisplay); } } diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.h b/src/core/hle/service/am/frontend/applet_software_keyboard.h index f464b7e15..2a7d01b96 100644 --- a/src/core/hle/service/am/frontend/applet_software_keyboard.h +++ b/src/core/hle/service/am/frontend/applet_software_keyboard.h @@ -62,7 +62,7 @@ private: void InitializeForeground(); /// Initializes the inline software keyboard. - void InitializeBackground(LibraryAppletMode library_applet_mode); + void InitializePartialForeground(LibraryAppletMode library_applet_mode); /// Processes the text check sent by the application. void ProcessTextCheck(); diff --git a/src/core/hle/service/am/frontend/applet_web_browser.cpp b/src/core/hle/service/am/frontend/applet_web_browser.cpp index 6ee4caf34..835c20c4e 100644 --- a/src/core/hle/service/am/frontend/applet_web_browser.cpp +++ b/src/core/hle/service/am/frontend/applet_web_browser.cpp @@ -20,9 +20,9 @@ #include "core/hle/result.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/frontend/applet_web_browser.h" -#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/service/storage.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/ns/iplatform_service_manager.h" +#include "core/hle/service/ns/platform_service_manager.h" #include "core/loader/loader.h" namespace Service::AM::Frontend { diff --git a/src/core/hle/service/am/frontend/applets.cpp b/src/core/hle/service/am/frontend/applets.cpp index db2b04575..e662c6cd6 100644 --- a/src/core/hle/service/am/frontend/applets.cpp +++ b/src/core/hle/service/am/frontend/applets.cpp @@ -15,11 +15,8 @@ #include "core/frontend/applets/web_browser.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/am/am.h" -#include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_data_broker.h" #include "core/hle/service/am/applet_manager.h" -#include "core/hle/service/am/applet_message_queue.h" -#include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/frontend/applet_cabinet.h" #include "core/hle/service/am/frontend/applet_controller.h" #include "core/hle/service/am/frontend/applet_error.h" @@ -29,7 +26,7 @@ #include "core/hle/service/am/frontend/applet_software_keyboard.h" #include "core/hle/service/am/frontend/applet_web_browser.h" #include "core/hle/service/am/frontend/applets.h" -#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/service/storage.h" #include "core/hle/service/sm/sm.h" namespace Service::AM::Frontend { diff --git a/src/core/hle/service/am/global_state_controller.cpp b/src/core/hle/service/am/global_state_controller.cpp deleted file mode 100644 index ed0eb7108..000000000 --- a/src/core/hle/service/am/global_state_controller.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/global_state_controller.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -IGlobalStateController::IGlobalStateController(Core::System& system_) - : ServiceFramework{system_, "IGlobalStateController"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "RequestToEnterSleep"}, - {1, nullptr, "EnterSleep"}, - {2, nullptr, "StartSleepSequence"}, - {3, nullptr, "StartShutdownSequence"}, - {4, nullptr, "StartRebootSequence"}, - {9, nullptr, "IsAutoPowerDownRequested"}, - {10, nullptr, "LoadAndApplyIdlePolicySettings"}, - {11, nullptr, "NotifyCecSettingsChanged"}, - {12, nullptr, "SetDefaultHomeButtonLongPressTime"}, - {13, nullptr, "UpdateDefaultDisplayResolution"}, - {14, nullptr, "ShouldSleepOnBoot"}, - {15, nullptr, "GetHdcpAuthenticationFailedEvent"}, - {30, nullptr, "OpenCradleFirmwareUpdater"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IGlobalStateController::~IGlobalStateController() = default; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/global_state_controller.h b/src/core/hle/service/am/global_state_controller.h deleted file mode 100644 index 7125464a1..000000000 --- a/src/core/hle/service/am/global_state_controller.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -class IGlobalStateController final : public ServiceFramework<IGlobalStateController> { -public: - explicit IGlobalStateController(Core::System& system_); - ~IGlobalStateController() override; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/home_menu_functions.cpp b/src/core/hle/service/am/home_menu_functions.cpp deleted file mode 100644 index 640e9fbb7..000000000 --- a/src/core/hle/service/am/home_menu_functions.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/home_menu_functions.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_) - : ServiceFramework{system_, "IHomeMenuFunctions"}, service_context{system, - "IHomeMenuFunctions"} { - // clang-format off - static const FunctionInfo functions[] = { - {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"}, - {11, nullptr, "LockForeground"}, - {12, nullptr, "UnlockForeground"}, - {20, nullptr, "PopFromGeneralChannel"}, - {21, &IHomeMenuFunctions::GetPopFromGeneralChannelEvent, "GetPopFromGeneralChannelEvent"}, - {30, nullptr, "GetHomeButtonWriterLockAccessor"}, - {31, nullptr, "GetWriterLockAccessorEx"}, - {40, nullptr, "IsSleepEnabled"}, - {41, nullptr, "IsRebootEnabled"}, - {50, nullptr, "LaunchSystemApplet"}, - {51, nullptr, "LaunchStarter"}, - {100, nullptr, "PopRequestLaunchApplicationForDebug"}, - {110, nullptr, "IsForceTerminateApplicationDisabledForDebug"}, - {200, nullptr, "LaunchDevMenu"}, - {1000, nullptr, "SetLastApplicationExitReason"}, - }; - // clang-format on - - RegisterHandlers(functions); - - pop_from_general_channel_event = - service_context.CreateEvent("IHomeMenuFunctions:PopFromGeneralChannelEvent"); -} - -IHomeMenuFunctions::~IHomeMenuFunctions() { - service_context.CloseEvent(pop_from_general_channel_event); -} - -void IHomeMenuFunctions::RequestToGetForeground(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IHomeMenuFunctions::GetPopFromGeneralChannelEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(pop_from_general_channel_event->GetReadableEvent()); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/home_menu_functions.h b/src/core/hle/service/am/home_menu_functions.h deleted file mode 100644 index e082d5d73..000000000 --- a/src/core/hle/service/am/home_menu_functions.h +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Service::AM { - -class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { -public: - explicit IHomeMenuFunctions(Core::System& system_); - ~IHomeMenuFunctions() override; - -private: - void RequestToGetForeground(HLERequestContext& ctx); - void GetPopFromGeneralChannelEvent(HLERequestContext& ctx); - - KernelHelpers::ServiceContext service_context; - - Kernel::KEvent* pop_from_general_channel_event; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp deleted file mode 100644 index 603515284..000000000 --- a/src/core/hle/service/am/idle.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/idle.h" - -namespace Service::AM { - -IdleSys::IdleSys(Core::System& system_) : ServiceFramework{system_, "idle:sys"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetAutoPowerDownEvent"}, - {1, nullptr, "IsAutoPowerDownRequested"}, - {2, nullptr, "Unknown2"}, - {3, nullptr, "SetHandlingContext"}, - {4, nullptr, "LoadAndApplySettings"}, - {5, nullptr, "ReportUserIsActive"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IdleSys::~IdleSys() = default; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/idle.h b/src/core/hle/service/am/idle.h deleted file mode 100644 index 15b31f67e..000000000 --- a/src/core/hle/service/am/idle.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Core { -class System; -} - -namespace Service::AM { - -class IdleSys final : public ServiceFramework<IdleSys> { -public: - explicit IdleSys(Core::System& system_); - ~IdleSys() override; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_accessor.cpp b/src/core/hle/service/am/library_applet_accessor.cpp deleted file mode 100644 index 6b20814f8..000000000 --- a/src/core/hle/service/am/library_applet_accessor.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/scope_exit.h" -#include "core/hle/service/am/am_results.h" -#include "core/hle/service/am/applet_data_broker.h" -#include "core/hle/service/am/frontend/applets.h" -#include "core/hle/service/am/library_applet_accessor.h" -#include "core/hle/service/am/storage.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_, - std::shared_ptr<AppletDataBroker> broker_, - std::shared_ptr<Applet> applet_) - : ServiceFramework{system_, "ILibraryAppletAccessor"}, broker{std::move(broker_)}, - applet{std::move(applet_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, - {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"}, - {10, &ILibraryAppletAccessor::Start, "Start"}, - {20, &ILibraryAppletAccessor::RequestExit, "RequestExit"}, - {25, nullptr, "Terminate"}, - {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, - {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, - {60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"}, - {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, - {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, - {102, nullptr, "PushExtraStorage"}, - {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"}, - {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"}, - {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"}, - {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"}, - {110, nullptr, "NeedsToExitProcess"}, - {120, nullptr, "GetLibraryAppletInfo"}, - {150, nullptr, "RequestForAppletToGetForeground"}, - {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -ILibraryAppletAccessor::~ILibraryAppletAccessor() = default; - -void ILibraryAppletAccessor::GetAppletStateChangedEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(broker->GetStateChangedEvent().GetHandle()); -} - -void ILibraryAppletAccessor::IsCompleted(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - std::scoped_lock lk{applet->lock}; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(broker->IsCompleted()); -} - -void ILibraryAppletAccessor::GetResult(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(applet->terminate_result); -} - -void ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILibraryAppletAccessor::Start(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - applet->process->Run(); - FrontendExecute(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILibraryAppletAccessor::RequestExit(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - ASSERT(applet != nullptr); - applet->message_queue.RequestExit(); - FrontendRequestExit(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILibraryAppletAccessor::PushInData(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::RequestParser rp{ctx}; - broker->GetInData().Push(rp.PopIpcInterface<IStorage>().lock()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILibraryAppletAccessor::PopOutData(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - std::shared_ptr<IStorage> data; - const auto res = broker->GetOutData().Pop(&data); - - if (res.IsSuccess()) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(res); - rb.PushIpcInterface(std::move(data)); - } else { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - } -} - -void ILibraryAppletAccessor::PushInteractiveInData(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::RequestParser rp{ctx}; - broker->GetInteractiveInData().Push(rp.PopIpcInterface<IStorage>().lock()); - FrontendExecuteInteractive(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILibraryAppletAccessor::PopInteractiveOutData(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - std::shared_ptr<IStorage> data; - const auto res = broker->GetInteractiveOutData().Pop(&data); - - if (res.IsSuccess()) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(res); - rb.PushIpcInterface(std::move(data)); - } else { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - } -} - -void ILibraryAppletAccessor::GetPopOutDataEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(broker->GetOutData().GetEvent()); -} - -void ILibraryAppletAccessor::GetPopInteractiveOutDataEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(broker->GetInteractiveOutData().GetEvent()); -} - -void ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is - // actually used anywhere - constexpr u64 handle = 0xdeadbeef; - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(handle); -} - -void ILibraryAppletAccessor::FrontendExecute() { - if (applet->frontend) { - applet->frontend->Initialize(); - applet->frontend->Execute(); - } -} - -void ILibraryAppletAccessor::FrontendExecuteInteractive() { - if (applet->frontend) { - applet->frontend->ExecuteInteractive(); - applet->frontend->Execute(); - } -} - -void ILibraryAppletAccessor::FrontendRequestExit() { - if (applet->frontend) { - applet->frontend->RequestExit(); - } -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_accessor.h b/src/core/hle/service/am/library_applet_accessor.h deleted file mode 100644 index 8be29e003..000000000 --- a/src/core/hle/service/am/library_applet_accessor.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -class AppletDataBroker; -struct Applet; - -class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { -public: - explicit ILibraryAppletAccessor(Core::System& system_, - std::shared_ptr<AppletDataBroker> broker_, - std::shared_ptr<Applet> applet_); - ~ILibraryAppletAccessor(); - -protected: - void GetAppletStateChangedEvent(HLERequestContext& ctx); - void IsCompleted(HLERequestContext& ctx); - void GetResult(HLERequestContext& ctx); - void PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx); - void Start(HLERequestContext& ctx); - void RequestExit(HLERequestContext& ctx); - void PushInData(HLERequestContext& ctx); - void PopOutData(HLERequestContext& ctx); - void PushInteractiveInData(HLERequestContext& ctx); - void PopInteractiveOutData(HLERequestContext& ctx); - void GetPopOutDataEvent(HLERequestContext& ctx); - void GetPopInteractiveOutDataEvent(HLERequestContext& ctx); - void GetIndirectLayerConsumerHandle(HLERequestContext& ctx); - - void FrontendExecute(); - void FrontendExecuteInteractive(); - void FrontendRequestExit(); - - const std::shared_ptr<AppletDataBroker> broker; - const std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_creator.cpp b/src/core/hle/service/am/library_applet_creator.cpp deleted file mode 100644 index 47bab7528..000000000 --- a/src/core/hle/service/am/library_applet_creator.cpp +++ /dev/null @@ -1,271 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/service/am/applet_data_broker.h" -#include "core/hle/service/am/applet_manager.h" -#include "core/hle/service/am/frontend/applets.h" -#include "core/hle/service/am/library_applet_accessor.h" -#include "core/hle/service/am/library_applet_creator.h" -#include "core/hle/service/am/library_applet_storage.h" -#include "core/hle/service/am/storage.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/sm/sm.h" - -namespace Service::AM { - -namespace { - -AppletProgramId AppletIdToProgramId(AppletId applet_id) { - switch (applet_id) { - case AppletId::OverlayDisplay: - return AppletProgramId::OverlayDisplay; - case AppletId::QLaunch: - return AppletProgramId::QLaunch; - case AppletId::Starter: - return AppletProgramId::Starter; - case AppletId::Auth: - return AppletProgramId::Auth; - case AppletId::Cabinet: - return AppletProgramId::Cabinet; - case AppletId::Controller: - return AppletProgramId::Controller; - case AppletId::DataErase: - return AppletProgramId::DataErase; - case AppletId::Error: - return AppletProgramId::Error; - case AppletId::NetConnect: - return AppletProgramId::NetConnect; - case AppletId::ProfileSelect: - return AppletProgramId::ProfileSelect; - case AppletId::SoftwareKeyboard: - return AppletProgramId::SoftwareKeyboard; - case AppletId::MiiEdit: - return AppletProgramId::MiiEdit; - case AppletId::Web: - return AppletProgramId::Web; - case AppletId::Shop: - return AppletProgramId::Shop; - case AppletId::PhotoViewer: - return AppletProgramId::PhotoViewer; - case AppletId::Settings: - return AppletProgramId::Settings; - case AppletId::OfflineWeb: - return AppletProgramId::OfflineWeb; - case AppletId::LoginShare: - return AppletProgramId::LoginShare; - case AppletId::WebAuth: - return AppletProgramId::WebAuth; - case AppletId::MyPage: - return AppletProgramId::MyPage; - default: - return static_cast<AppletProgramId>(0); - } -} - -[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet( - Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id, - LibraryAppletMode mode) { - const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); - if (program_id == 0) { - // Unknown applet - return {}; - } - - auto process = std::make_unique<Process>(system); - if (!process->Initialize(program_id)) { - // Couldn't initialize the guest process - return {}; - } - - const auto applet = std::make_shared<Applet>(system, std::move(process)); - applet->program_id = program_id; - applet->applet_id = applet_id; - applet->type = AppletType::LibraryApplet; - applet->library_applet_mode = mode; - - // Set focus state - switch (mode) { - case LibraryAppletMode::AllForeground: - case LibraryAppletMode::NoUI: - applet->focus_state = FocusState::InFocus; - applet->hid_registration.EnableAppletToGetInput(true); - applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); - applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); - break; - case LibraryAppletMode::AllForegroundInitiallyHidden: - applet->system_buffer_manager.SetWindowVisibility(false); - applet->focus_state = FocusState::NotInFocus; - applet->hid_registration.EnableAppletToGetInput(false); - applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); - break; - case LibraryAppletMode::Background: - case LibraryAppletMode::BackgroundIndirectDisplay: - default: - applet->focus_state = FocusState::Background; - applet->hid_registration.EnableAppletToGetInput(true); - applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); - break; - } - - auto broker = std::make_shared<AppletDataBroker>(system); - applet->caller_applet = caller_applet; - applet->caller_applet_broker = broker; - - system.GetAppletManager().InsertApplet(applet); - - return std::make_shared<ILibraryAppletAccessor>(system, broker, applet); -} - -[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet( - Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id, - LibraryAppletMode mode) { - const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); - - auto process = std::make_unique<Process>(system); - auto applet = std::make_shared<Applet>(system, std::move(process)); - applet->program_id = program_id; - applet->applet_id = applet_id; - applet->type = AppletType::LibraryApplet; - applet->library_applet_mode = mode; - - auto storage = std::make_shared<AppletDataBroker>(system); - applet->caller_applet = caller_applet; - applet->caller_applet_broker = storage; - applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode); - - return std::make_shared<ILibraryAppletAccessor>(system, storage, applet); -} - -} // namespace - -ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet_) - : ServiceFramework{system_, "ILibraryAppletCreator"}, applet{std::move(applet_)} { - static const FunctionInfo functions[] = { - {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, - {1, nullptr, "TerminateAllLibraryApplets"}, - {2, nullptr, "AreAnyLibraryAppletsLeft"}, - {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, - {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"}, - {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"}, - }; - RegisterHandlers(functions); -} - -ILibraryAppletCreator::~ILibraryAppletCreator() = default; - -void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto applet_id = rp.PopRaw<AppletId>(); - const auto applet_mode = rp.PopRaw<LibraryAppletMode>(); - - LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id, - applet_mode); - - auto library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode); - if (!library_applet) { - LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - // Applet is created, can now be launched. - applet->library_applet_launchable_event.Signal(); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ILibraryAppletAccessor>(library_applet); -} - -void ILibraryAppletCreator::CreateStorage(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const s64 size{rp.Pop<s64>()}; - - LOG_DEBUG(Service_AM, "called, size={}", size); - - if (size <= 0) { - LOG_ERROR(Service_AM, "size is less than or equal to 0"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - std::vector<u8> data(size); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IStorage>(system, AM::CreateStorage(std::move(data))); -} - -void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - struct Parameters { - bool is_writable; - s64 size; - }; - - const auto params{rp.PopRaw<Parameters>()}; - const auto handle{ctx.GetCopyHandle(0)}; - - LOG_DEBUG(Service_AM, "called, is_writable={}, size={}, handle={:08X}", params.is_writable, - params.size, handle); - - if (params.size <= 0) { - LOG_ERROR(Service_AM, "size is less than or equal to 0"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle); - - if (transfer_mem.IsNull()) { - LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IStorage>( - system, AM::CreateTransferMemoryStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(), - params.is_writable, params.size)); -} - -void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const s64 size{rp.Pop<s64>()}; - const auto handle{ctx.GetCopyHandle(0)}; - - LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle); - - if (size <= 0) { - LOG_ERROR(Service_AM, "size is less than or equal to 0"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle); - - if (transfer_mem.IsNull()) { - LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IStorage>( - system, AM::CreateHandleStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(), size)); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_creator.h b/src/core/hle/service/am/library_applet_creator.h deleted file mode 100644 index 551f287bd..000000000 --- a/src/core/hle/service/am/library_applet_creator.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -struct Applet; - -class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { -public: - explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet_); - ~ILibraryAppletCreator() override; - -private: - void CreateLibraryApplet(HLERequestContext& ctx); - void CreateStorage(HLERequestContext& ctx); - void CreateTransferMemoryStorage(HLERequestContext& ctx); - void CreateHandleStorage(HLERequestContext& ctx); - - const std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_proxy.cpp b/src/core/hle/service/am/library_applet_proxy.cpp deleted file mode 100644 index d6108fba3..000000000 --- a/src/core/hle/service/am/library_applet_proxy.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/applet_common_functions.h" -#include "core/hle/service/am/audio_controller.h" -#include "core/hle/service/am/common_state_getter.h" -#include "core/hle/service/am/debug_functions.h" -#include "core/hle/service/am/display_controller.h" -#include "core/hle/service/am/global_state_controller.h" -#include "core/hle/service/am/home_menu_functions.h" -#include "core/hle/service/am/library_applet_creator.h" -#include "core/hle/service/am/library_applet_proxy.h" -#include "core/hle/service/am/library_applet_self_accessor.h" -#include "core/hle/service/am/process_winding_controller.h" -#include "core/hle/service/am/self_controller.h" -#include "core/hle/service/am/window_controller.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -ILibraryAppletProxy::ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr<Applet> applet_, Core::System& system_) - : ServiceFramework{system_, "ILibraryAppletProxy"}, nvnflinger{nvnflinger_}, applet{std::move( - applet_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, - {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, - {2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"}, - {3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"}, - {4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"}, - {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"}, - {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, - {20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"}, - {21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"}, - {22, &ILibraryAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, - {23, &ILibraryAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, - {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -ILibraryAppletProxy::~ILibraryAppletProxy() = default; - -void ILibraryAppletProxy::GetCommonStateGetter(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ICommonStateGetter>(system, applet); -} - -void ILibraryAppletProxy::GetSelfController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger); -} - -void ILibraryAppletProxy::GetWindowController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IWindowController>(system, applet); -} - -void ILibraryAppletProxy::GetAudioController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioController>(system); -} - -void ILibraryAppletProxy::GetDisplayController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDisplayController>(system, applet); -} - -void ILibraryAppletProxy::GetProcessWindingController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IProcessWindingController>(system, applet); -} - -void ILibraryAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ILibraryAppletCreator>(system, applet); -} - -void ILibraryAppletProxy::OpenLibraryAppletSelfAccessor(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ILibraryAppletSelfAccessor>(system, applet); -} - -void ILibraryAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IAppletCommonFunctions>(system, applet); -} - -void ILibraryAppletProxy::GetHomeMenuFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IHomeMenuFunctions>(system); -} - -void ILibraryAppletProxy::GetGlobalStateController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IGlobalStateController>(system); -} - -void ILibraryAppletProxy::GetDebugFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDebugFunctions>(system); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_proxy.h b/src/core/hle/service/am/library_applet_proxy.h deleted file mode 100644 index 8f7a25897..000000000 --- a/src/core/hle/service/am/library_applet_proxy.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -struct Applet; - -class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { -public: - explicit ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr<Applet> applet_, Core::System& system_); - ~ILibraryAppletProxy(); - -private: - void GetCommonStateGetter(HLERequestContext& ctx); - void GetSelfController(HLERequestContext& ctx); - void GetWindowController(HLERequestContext& ctx); - void GetAudioController(HLERequestContext& ctx); - void GetDisplayController(HLERequestContext& ctx); - void GetProcessWindingController(HLERequestContext& ctx); - void GetLibraryAppletCreator(HLERequestContext& ctx); - void OpenLibraryAppletSelfAccessor(HLERequestContext& ctx); - void GetAppletCommonFunctions(HLERequestContext& ctx); - void GetHomeMenuFunctions(HLERequestContext& ctx); - void GetGlobalStateController(HLERequestContext& ctx); - void GetDebugFunctions(HLERequestContext& ctx); - - Nvnflinger::Nvnflinger& nvnflinger; - std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_self_accessor.cpp b/src/core/hle/service/am/library_applet_self_accessor.cpp deleted file mode 100644 index b560f580b..000000000 --- a/src/core/hle/service/am/library_applet_self_accessor.cpp +++ /dev/null @@ -1,338 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/scope_exit.h" -#include "core/core_timing.h" -#include "core/file_sys/control_metadata.h" -#include "core/file_sys/patch_manager.h" -#include "core/file_sys/registered_cache.h" -#include "core/hle/service/acc/profile_manager.h" -#include "core/hle/service/am/am_results.h" -#include "core/hle/service/am/applet_data_broker.h" -#include "core/hle/service/am/applet_manager.h" -#include "core/hle/service/am/frontend/applet_cabinet.h" -#include "core/hle/service/am/frontend/applet_controller.h" -#include "core/hle/service/am/frontend/applet_mii_edit_types.h" -#include "core/hle/service/am/frontend/applet_software_keyboard_types.h" -#include "core/hle/service/am/frontend/applets.h" -#include "core/hle/service/am/library_applet_self_accessor.h" -#include "core/hle/service/am/storage.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/ns/ns.h" -#include "core/hle/service/sm/sm.h" -#include "hid_core/hid_types.h" - -namespace Service::AM { - -namespace { - -AppletIdentityInfo GetCallerIdentity(std::shared_ptr<Applet> applet) { - if (const auto caller_applet = applet->caller_applet.lock(); caller_applet) { - // TODO: is this actually the application ID? - return { - .applet_id = caller_applet->applet_id, - .application_id = caller_applet->program_id, - }; - } else { - return { - .applet_id = AppletId::QLaunch, - .application_id = 0x0100000000001000ull, - }; - } -} - -} // namespace - -ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_, - std::shared_ptr<Applet> applet_) - : ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, applet{std::move(applet_)}, - broker{applet->caller_applet_broker} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ILibraryAppletSelfAccessor::PopInData, "PopInData"}, - {1, &ILibraryAppletSelfAccessor::PushOutData, "PushOutData"}, - {2, &ILibraryAppletSelfAccessor::PopInteractiveInData, "PopInteractiveInData"}, - {3, &ILibraryAppletSelfAccessor::PushInteractiveOutData, "PushInteractiveOutData"}, - {5, &ILibraryAppletSelfAccessor::GetPopInDataEvent, "GetPopInDataEvent"}, - {6, &ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent, "GetPopInteractiveInDataEvent"}, - {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"}, - {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"}, - {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"}, - {13, &ILibraryAppletSelfAccessor::CanUseApplicationCore, "CanUseApplicationCore"}, - {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"}, - {15, nullptr, "GetMainAppletApplicationControlProperty"}, - {16, nullptr, "GetMainAppletStorageId"}, - {17, nullptr, "GetCallerAppletIdentityInfoStack"}, - {18, nullptr, "GetNextReturnDestinationAppletIdentityInfo"}, - {19, &ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout, "GetDesirableKeyboardLayout"}, - {20, nullptr, "PopExtraStorage"}, - {25, nullptr, "GetPopExtraStorageEvent"}, - {30, nullptr, "UnpopInData"}, - {31, nullptr, "UnpopExtraStorage"}, - {40, nullptr, "GetIndirectLayerProducerHandle"}, - {50, nullptr, "ReportVisibleError"}, - {51, nullptr, "ReportVisibleErrorWithErrorContext"}, - {60, &ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage, "GetMainAppletApplicationDesiredLanguage"}, - {70, &ILibraryAppletSelfAccessor::GetCurrentApplicationId, "GetCurrentApplicationId"}, - {80, nullptr, "RequestExitToSelf"}, - {90, nullptr, "CreateApplicationAndPushAndRequestToLaunch"}, - {100, nullptr, "CreateGameMovieTrimmer"}, - {101, nullptr, "ReserveResourceForMovieOperation"}, - {102, nullptr, "UnreserveResourceForMovieOperation"}, - {110, &ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers, "GetMainAppletAvailableUsers"}, - {120, nullptr, "GetLaunchStorageInfoForDebug"}, - {130, nullptr, "GetGpuErrorDetectedSystemEvent"}, - {140, nullptr, "SetApplicationMemoryReservation"}, - {150, &ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually, "ShouldSetGpuTimeSliceManually"}, - {160, &ILibraryAppletSelfAccessor::Cmd160, "Cmd160"}, - }; - // clang-format on - RegisterHandlers(functions); -} - -ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default; - -void ILibraryAppletSelfAccessor::PopInData(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - std::shared_ptr<IStorage> data; - const auto res = broker->GetInData().Pop(&data); - - if (res.IsSuccess()) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(res); - rb.PushIpcInterface(std::move(data)); - } else { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - } -} - -void ILibraryAppletSelfAccessor::PushOutData(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - IPC::RequestParser rp{ctx}; - broker->GetOutData().Push(rp.PopIpcInterface<IStorage>().lock()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILibraryAppletSelfAccessor::PopInteractiveInData(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - std::shared_ptr<IStorage> data; - const auto res = broker->GetInteractiveInData().Pop(&data); - - if (res.IsSuccess()) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(res); - rb.PushIpcInterface(std::move(data)); - } else { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - } -} - -void ILibraryAppletSelfAccessor::PushInteractiveOutData(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - IPC::RequestParser rp{ctx}; - broker->GetInteractiveOutData().Push(rp.PopIpcInterface<IStorage>().lock()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILibraryAppletSelfAccessor::GetPopInDataEvent(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(broker->GetInData().GetEvent()); -} - -void ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(broker->GetInteractiveInData().GetEvent()); -} - -void ILibraryAppletSelfAccessor::ExitProcessAndReturn(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - system.GetAppletManager().TerminateAndRemoveApplet(applet->aruid); - broker->SignalCompletion(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) { - struct LibraryAppletInfo { - AppletId applet_id; - LibraryAppletMode library_applet_mode; - }; - - LOG_WARNING(Service_AM, "(STUBBED) called"); - - const LibraryAppletInfo applet_info{ - .applet_id = applet->applet_id, - .library_applet_mode = applet->library_applet_mode, - }; - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushRaw(applet_info); -} - -void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - const AppletIdentityInfo applet_info{ - .applet_id = AppletId::QLaunch, - .application_id = 0x0100000000001000ull, - }; - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(applet_info); -} - -void ILibraryAppletSelfAccessor::CanUseApplicationCore(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - // TODO: This appears to read the NPDM from state and check the core mask of the applet. - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(0); -} - -void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(GetCallerIdentity(applet)); -} - -void ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(0); -} - -void ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage(HLERequestContext& ctx) { - // FIXME: this is copied from IApplicationFunctions::GetDesiredLanguage - auto identity = GetCallerIdentity(applet); - - // TODO(bunnei): This should be configurable - LOG_DEBUG(Service_AM, "called"); - - // Get supported languages from NACP, if possible - // Default to 0 (all languages supported) - u32 supported_languages = 0; - - const auto res = [this, identity] { - const FileSys::PatchManager pm{identity.application_id, system.GetFileSystemController(), - system.GetContentProvider()}; - auto metadata = pm.GetControlMetadata(); - if (metadata.first != nullptr) { - return metadata; - } - - const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(identity.application_id), - system.GetFileSystemController(), - system.GetContentProvider()}; - return pm_update.GetControlMetadata(); - }(); - - if (res.first != nullptr) { - supported_languages = res.first->GetSupportedLanguages(); - } - - // Call IApplicationManagerInterface implementation. - auto& service_manager = system.ServiceManager(); - auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2"); - auto app_man = ns_am2->GetApplicationManagerInterface(); - - // Get desired application language - u8 desired_language{}; - const auto res_lang = - app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages); - if (res_lang != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res_lang); - return; - } - - // Convert to settings language code. - u64 language_code{}; - const auto res_code = - app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language); - if (res_code != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res_code); - return; - } - - LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(language_code); -} - -void ILibraryAppletSelfAccessor::GetCurrentApplicationId(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - u64 application_id = 0; - if (auto caller_applet = applet->caller_applet.lock(); caller_applet) { - application_id = caller_applet->program_id; - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(application_id); -} - -void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) { - const Service::Account::ProfileManager manager{}; - bool is_empty{true}; - s32 user_count{-1}; - - LOG_INFO(Service_AM, "called"); - - if (manager.GetUserCount() > 0) { - is_empty = false; - user_count = static_cast<s32>(manager.GetUserCount()); - ctx.WriteBuffer(manager.GetAllUsers()); - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u8>(is_empty); - rb.Push(user_count); -} - -void ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(0); -} - -void ILibraryAppletSelfAccessor::Cmd160(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(0); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_self_accessor.h b/src/core/hle/service/am/library_applet_self_accessor.h deleted file mode 100644 index 8717a989a..000000000 --- a/src/core/hle/service/am/library_applet_self_accessor.h +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <deque> -#include <vector> - -#include "core/hle/service/service.h" - -namespace Service::AM { - -class AppletDataBroker; -struct Applet; - -class ILibraryAppletSelfAccessor final : public ServiceFramework<ILibraryAppletSelfAccessor> { -public: - explicit ILibraryAppletSelfAccessor(Core::System& system_, std::shared_ptr<Applet> applet_); - ~ILibraryAppletSelfAccessor() override; - -private: - void PopInData(HLERequestContext& ctx); - void PushOutData(HLERequestContext& ctx); - void PopInteractiveInData(HLERequestContext& ctx); - void PushInteractiveOutData(HLERequestContext& ctx); - void GetPopInDataEvent(HLERequestContext& ctx); - void GetPopInteractiveInDataEvent(HLERequestContext& ctx); - void GetLibraryAppletInfo(HLERequestContext& ctx); - void GetMainAppletIdentityInfo(HLERequestContext& ctx); - void CanUseApplicationCore(HLERequestContext& ctx); - void ExitProcessAndReturn(HLERequestContext& ctx); - void GetCallerAppletIdentityInfo(HLERequestContext& ctx); - void GetDesirableKeyboardLayout(HLERequestContext& ctx); - void GetMainAppletApplicationDesiredLanguage(HLERequestContext& ctx); - void GetCurrentApplicationId(HLERequestContext& ctx); - void GetMainAppletAvailableUsers(HLERequestContext& ctx); - void ShouldSetGpuTimeSliceManually(HLERequestContext& ctx); - void Cmd160(HLERequestContext& ctx); - - const std::shared_ptr<Applet> applet; - const std::shared_ptr<AppletDataBroker> broker; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_storage.cpp b/src/core/hle/service/am/library_applet_storage.cpp index 46e6c0111..0412c215d 100644 --- a/src/core/hle/service/am/library_applet_storage.cpp +++ b/src/core/hle/service/am/library_applet_storage.cpp @@ -70,7 +70,7 @@ public: Result Read(s64 offset, void* buffer, size_t size) override { R_TRY(ValidateOffset(offset, size, m_size)); - m_memory.ReadBlock(m_trmem->GetSourceAddress(), buffer, size); + m_memory.ReadBlock(m_trmem->GetSourceAddress() + offset, buffer, size); R_SUCCEED(); } @@ -79,7 +79,7 @@ public: R_UNLESS(m_is_writable, ResultUnknown); R_TRY(ValidateOffset(offset, size, m_size)); - m_memory.WriteBlock(m_trmem->GetSourceAddress(), buffer, size); + m_memory.WriteBlock(m_trmem->GetSourceAddress() + offset, buffer, size); R_SUCCEED(); } diff --git a/src/core/hle/service/am/lock_accessor.cpp b/src/core/hle/service/am/lock_accessor.cpp deleted file mode 100644 index d0bd8d95e..000000000 --- a/src/core/hle/service/am/lock_accessor.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/lock_accessor.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -ILockAccessor::ILockAccessor(Core::System& system_) - : ServiceFramework{system_, "ILockAccessor"}, service_context{system_, "ILockAccessor"} { - // clang-format off - static const FunctionInfo functions[] = { - {1, &ILockAccessor::TryLock, "TryLock"}, - {2, &ILockAccessor::Unlock, "Unlock"}, - {3, &ILockAccessor::GetEvent, "GetEvent"}, - {4,&ILockAccessor::IsLocked, "IsLocked"}, - }; - // clang-format on - - RegisterHandlers(functions); - - lock_event = service_context.CreateEvent("ILockAccessor::LockEvent"); -} - -ILockAccessor::~ILockAccessor() { - service_context.CloseEvent(lock_event); -}; - -void ILockAccessor::TryLock(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto return_handle = rp.Pop<bool>(); - - LOG_WARNING(Service_AM, "(STUBBED) called, return_handle={}", return_handle); - - // TODO: When return_handle is true this function should return the lock handle - - is_locked = true; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(is_locked); -} - -void ILockAccessor::Unlock(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - is_locked = false; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILockAccessor::GetEvent(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - lock_event->Signal(); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(lock_event->GetReadableEvent()); -} - -void ILockAccessor::IsLocked(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - rb.Push<u8>(is_locked); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/lock_accessor.h b/src/core/hle/service/am/lock_accessor.h deleted file mode 100644 index 626f60e07..000000000 --- a/src/core/hle/service/am/lock_accessor.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Service::AM { - -class ILockAccessor final : public ServiceFramework<ILockAccessor> { -public: - explicit ILockAccessor(Core::System& system_); - ~ILockAccessor() override; - -private: - void TryLock(HLERequestContext& ctx); - void Unlock(HLERequestContext& ctx); - void GetEvent(HLERequestContext& ctx); - void IsLocked(HLERequestContext& ctx); - - bool is_locked{}; - - Kernel::KEvent* lock_event; - KernelHelpers::ServiceContext service_context; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/managed_layer_holder.cpp b/src/core/hle/service/am/managed_layer_holder.cpp deleted file mode 100644 index 61eb8641a..000000000 --- a/src/core/hle/service/am/managed_layer_holder.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/managed_layer_holder.h" -#include "core/hle/service/nvnflinger/nvnflinger.h" - -namespace Service::AM { - -ManagedLayerHolder::ManagedLayerHolder() = default; -ManagedLayerHolder::~ManagedLayerHolder() { - if (!m_nvnflinger) { - return; - } - - for (const auto& layer : m_managed_display_layers) { - m_nvnflinger->DestroyLayer(layer); - } - - for (const auto& layer : m_managed_display_recording_layers) { - m_nvnflinger->DestroyLayer(layer); - } - - m_nvnflinger = nullptr; -} - -void ManagedLayerHolder::Initialize(Nvnflinger::Nvnflinger* nvnflinger) { - m_nvnflinger = nvnflinger; -} - -void ManagedLayerHolder::CreateManagedDisplayLayer(u64* out_layer) { - // TODO(Subv): Find out how AM determines the display to use, for now just - // create the layer in the Default display. - const auto display_id = m_nvnflinger->OpenDisplay("Default"); - const auto layer_id = m_nvnflinger->CreateLayer(*display_id); - - m_managed_display_layers.emplace(*layer_id); - - *out_layer = *layer_id; -} - -void ManagedLayerHolder::CreateManagedDisplaySeparableLayer(u64* out_layer, - u64* out_recording_layer) { - // TODO(Subv): Find out how AM determines the display to use, for now just - // create the layer in the Default display. - // This calls nn::vi::CreateRecordingLayer() which creates another layer. - // Currently we do not support more than 1 layer per display, output 1 layer id for now. - // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse - // side effects. - // TODO: Support multiple layers - const auto display_id = m_nvnflinger->OpenDisplay("Default"); - const auto layer_id = m_nvnflinger->CreateLayer(*display_id); - - m_managed_display_layers.emplace(*layer_id); - - *out_layer = *layer_id; - *out_recording_layer = 0; -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/managed_layer_holder.h b/src/core/hle/service/am/managed_layer_holder.h deleted file mode 100644 index f7fe03f24..000000000 --- a/src/core/hle/service/am/managed_layer_holder.h +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <set> - -#include "common/common_funcs.h" -#include "common/common_types.h" - -namespace Service::Nvnflinger { -class Nvnflinger; -} - -namespace Service::AM { - -class ManagedLayerHolder { -public: - ManagedLayerHolder(); - ~ManagedLayerHolder(); - - void Initialize(Nvnflinger::Nvnflinger* nvnflinger); - void CreateManagedDisplayLayer(u64* out_layer); - void CreateManagedDisplaySeparableLayer(u64* out_layer, u64* out_recording_layer); - -private: - Nvnflinger::Nvnflinger* m_nvnflinger{}; - std::set<u64> m_managed_display_layers{}; - std::set<u64> m_managed_display_recording_layers{}; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp deleted file mode 100644 index 66824e495..000000000 --- a/src/core/hle/service/am/omm.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/omm.h" - -namespace Service::AM { - -OMM::OMM(Core::System& system_) : ServiceFramework{system_, "omm"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetOperationMode"}, - {1, nullptr, "GetOperationModeChangeEvent"}, - {2, nullptr, "EnableAudioVisual"}, - {3, nullptr, "DisableAudioVisual"}, - {4, nullptr, "EnterSleepAndWait"}, - {5, nullptr, "GetCradleStatus"}, - {6, nullptr, "FadeInDisplay"}, - {7, nullptr, "FadeOutDisplay"}, - {8, nullptr, "GetCradleFwVersion"}, - {9, nullptr, "NotifyCecSettingsChanged"}, - {10, nullptr, "SetOperationModePolicy"}, - {11, nullptr, "GetDefaultDisplayResolution"}, - {12, nullptr, "GetDefaultDisplayResolutionChangeEvent"}, - {13, nullptr, "UpdateDefaultDisplayResolution"}, - {14, nullptr, "ShouldSleepOnBoot"}, - {15, nullptr, "NotifyHdcpApplicationExecutionStarted"}, - {16, nullptr, "NotifyHdcpApplicationExecutionFinished"}, - {17, nullptr, "NotifyHdcpApplicationDrawingStarted"}, - {18, nullptr, "NotifyHdcpApplicationDrawingFinished"}, - {19, nullptr, "GetHdcpAuthenticationFailedEvent"}, - {20, nullptr, "GetHdcpAuthenticationFailedEmulationEnabled"}, - {21, nullptr, "SetHdcpAuthenticationFailedEmulation"}, - {22, nullptr, "GetHdcpStateChangeEvent"}, - {23, nullptr, "GetHdcpState"}, - {24, nullptr, "ShowCardUpdateProcessing"}, - {25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"}, - {26, nullptr, "GetOperationModeSystemInfo"}, - {27, nullptr, "GetAppletFullAwakingSystemEvent"}, - {28, nullptr, "CreateCradleFirmwareUpdater"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -OMM::~OMM() = default; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/omm.h b/src/core/hle/service/am/omm.h deleted file mode 100644 index 73d0c82d5..000000000 --- a/src/core/hle/service/am/omm.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Core { -class System; -} - -namespace Service::AM { - -class OMM final : public ServiceFramework<OMM> { -public: - explicit OMM(Core::System& system_); - ~OMM() override; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/process.cpp b/src/core/hle/service/am/process.cpp index 16b685f86..388d2045c 100644 --- a/src/core/hle/service/am/process.cpp +++ b/src/core/hle/service/am/process.cpp @@ -3,6 +3,7 @@ #include "common/scope_exit.h" +#include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/registered_cache.h" #include "core/hle/kernel/k_process.h" @@ -20,7 +21,7 @@ Process::~Process() { this->Finalize(); } -bool Process::Initialize(u64 program_id) { +bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) { // First, ensure we are not holding another process. this->Finalize(); @@ -29,21 +30,33 @@ bool Process::Initialize(u64 program_id) { // Attempt to load program NCA. const FileSys::RegisteredCache* bis_system{}; - FileSys::VirtualFile nca{}; + FileSys::VirtualFile nca_raw{}; // Get the program NCA from built-in storage. bis_system = fsc.GetSystemNANDContents(); if (bis_system) { - nca = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program); + nca_raw = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program); } // Ensure we retrieved a program NCA. - if (!nca) { + if (!nca_raw) { return false; } + // Ensure we have a suitable version. + if (minimum_key_generation > 0) { + FileSys::NCA nca(nca_raw); + if (nca.GetStatus() == Loader::ResultStatus::Success && + (nca.GetKeyGeneration() < minimum_key_generation || + nca.GetKeyGeneration() > maximum_key_generation)) { + LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, + nca.GetKeyGeneration()); + return false; + } + } + // Get the appropriate loader to parse this NCA. - auto app_loader = Loader::GetLoader(m_system, nca, program_id, 0); + auto app_loader = Loader::GetLoader(m_system, nca_raw, program_id, 0); // Ensure we have a loader which can parse the NCA. if (!app_loader) { @@ -55,7 +68,9 @@ bool Process::Initialize(u64 program_id) { Kernel::KProcess::Register(m_system.Kernel(), process); // On exit, ensure we free the additional reference to the process. - SCOPE_EXIT({ process->Close(); }); + SCOPE_EXIT { + process->Close(); + }; // Insert process modules into memory. const auto [load_result, load_parameters] = app_loader->Load(*process, m_system); diff --git a/src/core/hle/service/am/process.h b/src/core/hle/service/am/process.h index 4b908ade4..4b8102fb6 100644 --- a/src/core/hle/service/am/process.h +++ b/src/core/hle/service/am/process.h @@ -21,7 +21,7 @@ public: explicit Process(Core::System& system); ~Process(); - bool Initialize(u64 program_id); + bool Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation); void Finalize(); bool Run(); diff --git a/src/core/hle/service/am/process_winding_controller.cpp b/src/core/hle/service/am/process_winding_controller.cpp deleted file mode 100644 index b48b52797..000000000 --- a/src/core/hle/service/am/process_winding_controller.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/frontend/applets.h" -#include "core/hle/service/am/library_applet_accessor.h" -#include "core/hle/service/am/process_winding_controller.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -IProcessWindingController::IProcessWindingController(Core::System& system_, - std::shared_ptr<Applet> applet_) - : ServiceFramework{system_, "IProcessWindingController"}, applet{std::move(applet_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IProcessWindingController::GetLaunchReason, "GetLaunchReason"}, - {11, &IProcessWindingController::OpenCallingLibraryApplet, "OpenCallingLibraryApplet"}, - {21, nullptr, "PushContext"}, - {22, nullptr, "PopContext"}, - {23, nullptr, "CancelWindingReservation"}, - {30, nullptr, "WindAndDoReserved"}, - {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, - {41, nullptr, "ReserveToStartAndWait"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IProcessWindingController::~IProcessWindingController() = default; - -void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw(applet->launch_reason); -} - -void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) { - const auto caller_applet = applet->caller_applet.lock(); - if (caller_applet == nullptr) { - LOG_ERROR(Service_AM, "No calling applet available"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet->caller_applet_broker, - caller_applet); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/process_winding_controller.h b/src/core/hle/service/am/process_winding_controller.h deleted file mode 100644 index 71ae4c4f5..000000000 --- a/src/core/hle/service/am/process_winding_controller.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -struct Applet; - -class IProcessWindingController final : public ServiceFramework<IProcessWindingController> { -public: - explicit IProcessWindingController(Core::System& system_, std::shared_ptr<Applet> applet_); - ~IProcessWindingController() override; - -private: - void GetLaunchReason(HLERequestContext& ctx); - void OpenCallingLibraryApplet(HLERequestContext& ctx); - - const std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/self_controller.cpp b/src/core/hle/service/am/self_controller.cpp deleted file mode 100644 index 0289f5cf1..000000000 --- a/src/core/hle/service/am/self_controller.cpp +++ /dev/null @@ -1,456 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/am_results.h" -#include "core/hle/service/am/frontend/applets.h" -#include "core/hle/service/am/self_controller.h" -#include "core/hle/service/caps/caps_su.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" -#include "core/hle/service/nvnflinger/nvnflinger.h" -#include "core/hle/service/sm/sm.h" -#include "core/hle/service/vi/vi_results.h" - -namespace Service::AM { - -ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> applet_, - Nvnflinger::Nvnflinger& nvnflinger_) - : ServiceFramework{system_, "ISelfController"}, nvnflinger{nvnflinger_}, applet{std::move( - applet_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ISelfController::Exit, "Exit"}, - {1, &ISelfController::LockExit, "LockExit"}, - {2, &ISelfController::UnlockExit, "UnlockExit"}, - {3, &ISelfController::EnterFatalSection, "EnterFatalSection"}, - {4, &ISelfController::LeaveFatalSection, "LeaveFatalSection"}, - {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, - {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, - {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"}, - {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"}, - {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, - {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, - {15, &ISelfController::SetScreenShotAppletIdentityInfo, "SetScreenShotAppletIdentityInfo"}, - {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, - {17, nullptr, "SetControllerFirmwareUpdateSection"}, - {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, - {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"}, - {20, nullptr, "SetDesirableKeyboardLayout"}, - {21, nullptr, "GetScreenShotProgramId"}, - {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, - {41, &ISelfController::IsSystemBufferSharingEnabled, "IsSystemBufferSharingEnabled"}, - {42, &ISelfController::GetSystemSharedLayerHandle, "GetSystemSharedLayerHandle"}, - {43, &ISelfController::GetSystemSharedBufferHandle, "GetSystemSharedBufferHandle"}, - {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"}, - {45, nullptr, "SetManagedDisplayLayerSeparationMode"}, - {46, nullptr, "SetRecordingLayerCompositionEnabled"}, - {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, - {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"}, - {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, - {61, nullptr, "SetMediaPlaybackState"}, - {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, - {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, - {64, nullptr, "SetInputDetectionSourceSet"}, - {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"}, - {66, nullptr, "GetCurrentIlluminance"}, - {67, nullptr, "IsIlluminanceAvailable"}, - {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"}, - {69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"}, - {70, nullptr, "ReportMultimediaError"}, - {71, nullptr, "GetCurrentIlluminanceEx"}, - {72, nullptr, "SetInputDetectionPolicy"}, - {80, nullptr, "SetWirelessPriorityMode"}, - {90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"}, - {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"}, - {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, - {110, nullptr, "SetApplicationAlbumUserData"}, - {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"}, - {130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"}, - {1000, nullptr, "GetDebugStorageChannel"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -ISelfController::~ISelfController() = default; - -void ISelfController::Exit(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - - // TODO - system.Exit(); -} - -void ISelfController::LockExit(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - system.SetExitLocked(true); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::UnlockExit(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - system.SetExitLocked(false); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - - if (system.GetExitRequested()) { - system.Exit(); - } -} - -void ISelfController::EnterFatalSection(HLERequestContext& ctx) { - - std::scoped_lock lk{applet->lock}; - applet->fatal_section_count++; - LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", applet->fatal_section_count); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::LeaveFatalSection(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called."); - - // Entry and exit of fatal sections must be balanced. - std::scoped_lock lk{applet->lock}; - if (applet->fatal_section_count == 0) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultFatalSectionCountImbalance); - return; - } - - applet->fatal_section_count--; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::GetLibraryAppletLaunchableEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - applet->library_applet_launchable_event.Signal(); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->library_applet_launchable_event.GetHandle()); -} - -void ISelfController::SetScreenShotPermission(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto permission = rp.PopEnum<ScreenshotPermission>(); - LOG_DEBUG(Service_AM, "called, permission={}", permission); - - std::scoped_lock lk{applet->lock}; - applet->screenshot_permission = permission; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetOperationModeChangedNotification(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const bool notification_enabled = rp.Pop<bool>(); - LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled); - - std::scoped_lock lk{applet->lock}; - applet->operation_mode_changed_notification_enabled = notification_enabled; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetPerformanceModeChangedNotification(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const bool notification_enabled = rp.Pop<bool>(); - LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled); - - std::scoped_lock lk{applet->lock}; - applet->performance_mode_changed_notification_enabled = notification_enabled; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetFocusHandlingMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto flags = rp.PopRaw<FocusHandlingMode>(); - - LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}", - flags.unknown0, flags.unknown1, flags.unknown2); - - std::scoped_lock lk{applet->lock}; - applet->focus_handling_mode = flags; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetRestartMessageEnabled(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - applet->restart_message_enabled = true; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetScreenShotAppletIdentityInfo(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::RequestParser rp{ctx}; - std::scoped_lock lk{applet->lock}; - applet->screen_shot_identity = rp.PopRaw<AppletIdentityInfo>(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const bool enabled = rp.Pop<bool>(); - LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled); - - std::scoped_lock lk{applet->lock}; - ASSERT(applet->type == AppletType::Application); - applet->out_of_focus_suspension_enabled = enabled; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetAlbumImageOrientation(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto orientation = rp.PopRaw<Capture::AlbumImageOrientation>(); - LOG_WARNING(Service_AM, "(STUBBED) called, orientation={}", static_cast<s32>(orientation)); - - std::scoped_lock lk{applet->lock}; - applet->album_image_orientation = orientation; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - u64 layer_id{}; - applet->managed_layer_holder.Initialize(&nvnflinger); - applet->managed_layer_holder.CreateManagedDisplayLayer(&layer_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(layer_id); -} - -void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess())); -} - -void ISelfController::GetSystemSharedLayerHandle(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - u64 buffer_id, layer_id; - applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess())); - rb.Push<s64>(buffer_id); - rb.Push<s64>(layer_id); -} - -void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - u64 buffer_id, layer_id; - applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess())); - rb.Push<s64>(buffer_id); -} - -Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) { - if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id)) { - return ResultSuccess; - } - - return VI::ResultOperationFailed; -} - -void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - u64 layer_id{}; - u64 recording_layer_id{}; - applet->managed_layer_holder.Initialize(&nvnflinger); - applet->managed_layer_holder.CreateManagedDisplaySeparableLayer(&layer_id, &recording_layer_id); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(layer_id); - rb.Push(recording_layer_id); -} - -void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::ApproveToDisplay(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto extension = rp.PopRaw<IdleTimeDetectionExtension>(); - LOG_DEBUG(Service_AM, "(STUBBED) called extension={}", extension); - - std::scoped_lock lk{applet->lock}; - applet->idle_time_detection_extension = extension; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::GetIdleTimeDetectionExtension(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - std::scoped_lock lk{applet->lock}; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw<IdleTimeDetectionExtension>(applet->idle_time_detection_extension); -} - -void ISelfController::ReportUserIsActive(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetAutoSleepDisabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - std::scoped_lock lk{applet->lock}; - applet->auto_sleep_disabled = rp.Pop<bool>(); - - // On the system itself, if the previous state of is_auto_sleep_disabled - // differed from the current value passed in, it'd signify the internal - // window manager to update (and also increment some statistics like update counts) - // - // It'd also indicate this change to an idle handling context. - // - // However, given we're emulating this behavior, most of this can be ignored - // and it's sufficient to simply set the member variable for querying via - // IsAutoSleepDisabled(). - - LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", applet->auto_sleep_disabled); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::IsAutoSleepDisabled(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called."); - - std::scoped_lock lk{applet->lock}; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(applet->auto_sleep_disabled); -} - -void ISelfController::GetAccumulatedSuspendedTickValue(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called."); - - std::scoped_lock lk{applet->lock}; - // This command returns the total number of system ticks since ISelfController creation - // where the game was suspended. Since Yuzu doesn't implement game suspension, this command - // can just always return 0 ticks. - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(applet->suspended_ticks); -} - -void ISelfController::GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called."); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->accumulated_suspended_tick_changed_event.GetHandle()); -} - -void ISelfController::SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - // This service call sets an internal flag whether a notification is shown when an image is - // captured. Currently we do not support capturing images via the capture button, so this can be - // stubbed for now. - const bool enabled = rp.Pop<bool>(); - LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled); - - std::scoped_lock lk{applet->lock}; - applet->album_image_taken_notification_enabled = enabled; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SaveCurrentScreenshot(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto report_option = rp.PopEnum<Capture::AlbumReportOption>(); - - LOG_INFO(Service_AM, "called, report_option={}", report_option); - - const auto screenshot_service = - system.ServiceManager().GetService<Service::Capture::IScreenShotApplicationService>( - "caps:su"); - - if (screenshot_service) { - screenshot_service->CaptureAndSaveScreenshot(report_option); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetRecordVolumeMuted(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto enabled = rp.Pop<bool>(); - LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled); - - std::scoped_lock lk{applet->lock}; - applet->record_volume_muted = enabled; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/self_controller.h b/src/core/hle/service/am/self_controller.h deleted file mode 100644 index a63bc2e74..000000000 --- a/src/core/hle/service/am/self_controller.h +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Service::AM { - -struct Applet; - -class ISelfController final : public ServiceFramework<ISelfController> { -public: - explicit ISelfController(Core::System& system_, std::shared_ptr<Applet> applet_, - Nvnflinger::Nvnflinger& nvnflinger_); - ~ISelfController() override; - -private: - void Exit(HLERequestContext& ctx); - void LockExit(HLERequestContext& ctx); - void UnlockExit(HLERequestContext& ctx); - void EnterFatalSection(HLERequestContext& ctx); - void LeaveFatalSection(HLERequestContext& ctx); - void GetLibraryAppletLaunchableEvent(HLERequestContext& ctx); - void SetScreenShotPermission(HLERequestContext& ctx); - void SetOperationModeChangedNotification(HLERequestContext& ctx); - void SetPerformanceModeChangedNotification(HLERequestContext& ctx); - void SetFocusHandlingMode(HLERequestContext& ctx); - void SetRestartMessageEnabled(HLERequestContext& ctx); - void SetScreenShotAppletIdentityInfo(HLERequestContext& ctx); - void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx); - void SetAlbumImageOrientation(HLERequestContext& ctx); - void IsSystemBufferSharingEnabled(HLERequestContext& ctx); - void GetSystemSharedBufferHandle(HLERequestContext& ctx); - void GetSystemSharedLayerHandle(HLERequestContext& ctx); - void CreateManagedDisplayLayer(HLERequestContext& ctx); - void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx); - void SetHandlesRequestToDisplay(HLERequestContext& ctx); - void ApproveToDisplay(HLERequestContext& ctx); - void SetIdleTimeDetectionExtension(HLERequestContext& ctx); - void GetIdleTimeDetectionExtension(HLERequestContext& ctx); - void ReportUserIsActive(HLERequestContext& ctx); - void SetAutoSleepDisabled(HLERequestContext& ctx); - void IsAutoSleepDisabled(HLERequestContext& ctx); - void GetAccumulatedSuspendedTickValue(HLERequestContext& ctx); - void GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx); - void SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx); - void SaveCurrentScreenshot(HLERequestContext& ctx); - void SetRecordVolumeMuted(HLERequestContext& ctx); - - Result EnsureBufferSharingEnabled(Kernel::KProcess* process); - - Nvnflinger::Nvnflinger& nvnflinger; - const std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp b/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp new file mode 100644 index 000000000..21747783a --- /dev/null +++ b/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/service/all_system_applet_proxies_service.h" +#include "core/hle/service/am/service/library_applet_proxy.h" +#include "core/hle/service/am/service/system_applet_proxy.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& system_) + : ServiceFramework{system_, "appletAE"} { + // clang-format off + static const FunctionInfo functions[] = { + {100, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxy"}, + {200, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxyOld>, "OpenLibraryAppletProxyOld"}, + {201, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxy>, "OpenLibraryAppletProxy"}, + {300, nullptr, "OpenOverlayAppletProxy"}, + {350, nullptr, "OpenSystemApplicationProxy"}, + {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"}, + {410, nullptr, "GetSystemAppletControllerForDebug"}, + {1000, nullptr, "GetDebugFunctions"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IAllSystemAppletProxiesService::~IAllSystemAppletProxiesService() = default; + +Result IAllSystemAppletProxiesService::OpenSystemAppletProxy( + Out<SharedPointer<ISystemAppletProxy>> out_system_applet_proxy, ClientProcessId pid, + InCopyHandle<Kernel::KProcess> process_handle) { + LOG_DEBUG(Service_AM, "called"); + + if (const auto applet = this->GetAppletFromProcessId(pid); applet) { + *out_system_applet_proxy = + std::make_shared<ISystemAppletProxy>(system, applet, process_handle.Get()); + R_SUCCEED(); + } else { + UNIMPLEMENTED(); + R_THROW(ResultUnknown); + } +} + +Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy( + Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid, + InCopyHandle<Kernel::KProcess> process_handle, + InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) { + LOG_DEBUG(Service_AM, "called"); + + if (const auto applet = this->GetAppletFromProcessId(pid); applet) { + *out_library_applet_proxy = + std::make_shared<ILibraryAppletProxy>(system, applet, process_handle.Get()); + R_SUCCEED(); + } else { + UNIMPLEMENTED(); + R_THROW(ResultUnknown); + } +} + +Result IAllSystemAppletProxiesService::OpenLibraryAppletProxyOld( + Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid, + InCopyHandle<Kernel::KProcess> process_handle) { + LOG_DEBUG(Service_AM, "called"); + + AppletAttribute attribute{}; + R_RETURN( + this->OpenLibraryAppletProxy(out_library_applet_proxy, pid, process_handle, attribute)); +} + +std::shared_ptr<Applet> IAllSystemAppletProxiesService::GetAppletFromProcessId( + ProcessId process_id) { + return system.GetAppletManager().GetByAppletResourceUserId(process_id.pid); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/all_system_applet_proxies_service.h b/src/core/hle/service/am/service/all_system_applet_proxies_service.h new file mode 100644 index 000000000..0e2dcb86d --- /dev/null +++ b/src/core/hle/service/am/service/all_system_applet_proxies_service.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service { + +namespace AM { + +struct Applet; +struct AppletAttribute; +class ILibraryAppletProxy; +class ISystemAppletProxy; + +class IAllSystemAppletProxiesService final + : public ServiceFramework<IAllSystemAppletProxiesService> { +public: + explicit IAllSystemAppletProxiesService(Core::System& system_); + ~IAllSystemAppletProxiesService() override; + +private: + Result OpenSystemAppletProxy(Out<SharedPointer<ISystemAppletProxy>> out_system_applet_proxy, + ClientProcessId pid, + InCopyHandle<Kernel::KProcess> process_handle); + Result OpenLibraryAppletProxy(Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, + ClientProcessId pid, + InCopyHandle<Kernel::KProcess> process_handle, + InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute); + Result OpenLibraryAppletProxyOld( + Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid, + InCopyHandle<Kernel::KProcess> process_handle); + +private: + std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid); +}; + +} // namespace AM +} // namespace Service diff --git a/src/core/hle/service/am/service/applet_common_functions.cpp b/src/core/hle/service/am/service/applet_common_functions.cpp new file mode 100644 index 000000000..0f29ab285 --- /dev/null +++ b/src/core/hle/service/am/service/applet_common_functions.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/service/applet_common_functions.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_, + std::shared_ptr<Applet> applet_) + : ServiceFramework{system_, "IAppletCommonFunctions"}, applet{std::move(applet_)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "SetTerminateResult"}, + {10, nullptr, "ReadThemeStorage"}, + {11, nullptr, "WriteThemeStorage"}, + {20, nullptr, "PushToAppletBoundChannel"}, + {21, nullptr, "TryPopFromAppletBoundChannel"}, + {40, nullptr, "GetDisplayLogicalResolution"}, + {42, nullptr, "SetDisplayMagnification"}, + {50, nullptr, "SetHomeButtonDoubleClickEnabled"}, + {51, D<&IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled>, "GetHomeButtonDoubleClickEnabled"}, + {52, nullptr, "IsHomeButtonShortPressedBlocked"}, + {60, nullptr, "IsVrModeCurtainRequired"}, + {61, nullptr, "IsSleepRequiredByHighTemperature"}, + {62, nullptr, "IsSleepRequiredByLowBattery"}, + {70, D<&IAppletCommonFunctions::SetCpuBoostRequestPriority>, "SetCpuBoostRequestPriority"}, + {80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"}, + {81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"}, + {90, nullptr, "OpenNamedChannelAsParent"}, + {91, nullptr, "OpenNamedChannelAsChild"}, + {100, nullptr, "SetApplicationCoreUsageMode"}, + {300, D<&IAppletCommonFunctions::GetCurrentApplicationId>, "GetCurrentApplicationId"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IAppletCommonFunctions::~IAppletCommonFunctions() = default; + +Result IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled( + Out<bool> out_home_button_double_click_enabled) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_home_button_double_click_enabled = false; + R_SUCCEED(); +} + +Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + std::scoped_lock lk{applet->lock}; + applet->cpu_boost_request_priority = priority; + R_SUCCEED(); +} + +Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_id) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_application_id = system.GetApplicationProcessProgramID() & ~0xFFFULL; + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/applet_common_functions.h b/src/core/hle/service/am/service/applet_common_functions.h new file mode 100644 index 000000000..4424fc83d --- /dev/null +++ b/src/core/hle/service/am/service/applet_common_functions.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class IAppletCommonFunctions final : public ServiceFramework<IAppletCommonFunctions> { +public: + explicit IAppletCommonFunctions(Core::System& system_, std::shared_ptr<Applet> applet_); + ~IAppletCommonFunctions() override; + +private: + Result GetHomeButtonDoubleClickEnabled(Out<bool> out_home_button_double_click_enabled); + Result SetCpuBoostRequestPriority(s32 priority); + Result GetCurrentApplicationId(Out<u64> out_application_id); + + const std::shared_ptr<Applet> applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_accessor.cpp b/src/core/hle/service/am/service/application_accessor.cpp new file mode 100644 index 000000000..6e7d110e8 --- /dev/null +++ b/src/core/hle/service/am/service/application_accessor.cpp @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/result.h" +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/service/application_accessor.h" +#include "core/hle/service/am/service/library_applet_accessor.h" +#include "core/hle/service/am/service/storage.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IApplicationAccessor::IApplicationAccessor(Core::System& system_, std::shared_ptr<Applet> applet) + : ServiceFramework{system_, "IApplicationAccessor"}, m_applet(std::move(applet)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IApplicationAccessor::GetAppletStateChangedEvent>, "GetAppletStateChangedEvent"}, + {1, nullptr, "IsCompleted"}, + {10, D<&IApplicationAccessor::Start>, "Start"}, + {20, D<&IApplicationAccessor::RequestExit>, "RequestExit"}, + {25, D<&IApplicationAccessor::Terminate>, "Terminate"}, + {30, D<&IApplicationAccessor::GetResult>, "GetResult"}, + {101, D<&IApplicationAccessor::RequestForApplicationToGetForeground>, "RequestForApplicationToGetForeground"}, + {110, nullptr, "TerminateAllLibraryApplets"}, + {111, nullptr, "AreAnyLibraryAppletsLeft"}, + {112, D<&IApplicationAccessor::GetCurrentLibraryApplet>, "GetCurrentLibraryApplet"}, + {120, nullptr, "GetApplicationId"}, + {121, D<&IApplicationAccessor::PushLaunchParameter>, "PushLaunchParameter"}, + {122, D<&IApplicationAccessor::GetApplicationControlProperty>, "GetApplicationControlProperty"}, + {123, nullptr, "GetApplicationLaunchProperty"}, + {124, nullptr, "GetApplicationLaunchRequestInfo"}, + {130, D<&IApplicationAccessor::SetUsers>, "SetUsers"}, + {131, D<&IApplicationAccessor::CheckRightsEnvironmentAvailable>, "CheckRightsEnvironmentAvailable"}, + {132, D<&IApplicationAccessor::GetNsRightsEnvironmentHandle>, "GetNsRightsEnvironmentHandle"}, + {140, nullptr, "GetDesirableUids"}, + {150, D<&IApplicationAccessor::ReportApplicationExitTimeout>, "ReportApplicationExitTimeout"}, + {160, nullptr, "SetApplicationAttribute"}, + {170, nullptr, "HasSaveDataAccessPermission"}, + {180, nullptr, "PushToFriendInvitationStorageChannel"}, + {190, nullptr, "PushToNotificationStorageChannel"}, + {200, nullptr, "RequestApplicationSoftReset"}, + {201, nullptr, "RestartApplicationTimer"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IApplicationAccessor::~IApplicationAccessor() = default; + +Result IApplicationAccessor::Start() { + LOG_INFO(Service_AM, "called"); + m_applet->process->Run(); + R_SUCCEED(); +} + +Result IApplicationAccessor::RequestExit() { + LOG_INFO(Service_AM, "called"); + m_applet->message_queue.RequestExit(); + R_SUCCEED(); +} + +Result IApplicationAccessor::Terminate() { + LOG_INFO(Service_AM, "called"); + m_applet->process->Terminate(); + R_SUCCEED(); +} + +Result IApplicationAccessor::GetResult() { + LOG_INFO(Service_AM, "called"); + R_SUCCEED(); +} + +Result IApplicationAccessor::GetAppletStateChangedEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_INFO(Service_AM, "called"); + *out_event = m_applet->caller_applet_broker->GetStateChangedEvent().GetHandle(); + R_SUCCEED(); +} + +Result IApplicationAccessor::PushLaunchParameter(LaunchParameterKind kind, + SharedPointer<IStorage> storage) { + LOG_INFO(Service_AM, "called, kind={}", kind); + + switch (kind) { + case LaunchParameterKind::AccountPreselectedUser: + m_applet->preselected_user_launch_parameter.push_back(storage->GetData()); + R_SUCCEED(); + default: + R_THROW(ResultUnknown); + } +} + +Result IApplicationAccessor::GetApplicationControlProperty( + OutBuffer<BufferAttr_HipcMapAlias> out_control_property) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_THROW(ResultUnknown); +} + +Result IApplicationAccessor::SetUsers(bool enable, + InArray<Common::UUID, BufferAttr_HipcMapAlias> user_ids) { + LOG_INFO(Service_AM, "called, enable={} user_id_count={}", enable, user_ids.size()); + R_SUCCEED(); +} + +Result IApplicationAccessor::GetCurrentLibraryApplet( + Out<SharedPointer<ILibraryAppletAccessor>> out_accessor) { + LOG_INFO(Service_AM, "(STUBBED) called"); + *out_accessor = nullptr; + R_SUCCEED(); +} + +Result IApplicationAccessor::RequestForApplicationToGetForeground() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_THROW(ResultUnknown); +} + +Result IApplicationAccessor::CheckRightsEnvironmentAvailable(Out<bool> out_is_available) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_is_available = true; + R_SUCCEED(); +} + +Result IApplicationAccessor::GetNsRightsEnvironmentHandle(Out<u64> out_handle) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_handle = 0xdeadbeef; + R_SUCCEED(); +} + +Result IApplicationAccessor::ReportApplicationExitTimeout() { + LOG_ERROR(Service_AM, "called"); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_accessor.h b/src/core/hle/service/am/service/application_accessor.h new file mode 100644 index 000000000..39a9b2153 --- /dev/null +++ b/src/core/hle/service/am/service/application_accessor.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/uuid.h" +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; +class ILibraryAppletAccessor; +class IStorage; + +class IApplicationAccessor final : public ServiceFramework<IApplicationAccessor> { +public: + explicit IApplicationAccessor(Core::System& system_, std::shared_ptr<Applet> applet); + ~IApplicationAccessor() override; + +private: + Result Start(); + Result RequestExit(); + Result Terminate(); + Result GetResult(); + Result GetAppletStateChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result PushLaunchParameter(LaunchParameterKind kind, SharedPointer<IStorage> storage); + Result GetApplicationControlProperty(OutBuffer<BufferAttr_HipcMapAlias> out_control_property); + Result SetUsers(bool enable, InArray<Common::UUID, BufferAttr_HipcMapAlias> user_ids); + Result GetCurrentLibraryApplet(Out<SharedPointer<ILibraryAppletAccessor>> out_accessor); + Result RequestForApplicationToGetForeground(); + Result CheckRightsEnvironmentAvailable(Out<bool> out_is_available); + Result GetNsRightsEnvironmentHandle(Out<u64> out_handle); + Result ReportApplicationExitTimeout(); + + const std::shared_ptr<Applet> m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_creator.cpp b/src/core/hle/service/am/service/application_creator.cpp new file mode 100644 index 000000000..568bb0122 --- /dev/null +++ b/src/core/hle/service/am/service/application_creator.cpp @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/service/application_accessor.h" +#include "core/hle/service/am/service/application_creator.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IApplicationCreator::IApplicationCreator(Core::System& system_) + : ServiceFramework{system_, "IApplicationCreator"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IApplicationCreator::CreateApplication>, "CreateApplication"}, + {1, nullptr, "PopLaunchRequestedApplication"}, + {10, nullptr, "CreateSystemApplication"}, + {100, nullptr, "PopFloatingApplicationForDevelopment"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IApplicationCreator::~IApplicationCreator() = default; + +Result IApplicationCreator::CreateApplication( + Out<SharedPointer<IApplicationAccessor>> out_application_accessor, u64 application_id) { + LOG_ERROR(Service_NS, "called, application_id={:x}", application_id); + R_THROW(ResultUnknown); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_creator.h b/src/core/hle/service/am/service/application_creator.h new file mode 100644 index 000000000..9f939ebf6 --- /dev/null +++ b/src/core/hle/service/am/service/application_creator.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IApplicationAccessor; +struct Applet; + +class IApplicationCreator final : public ServiceFramework<IApplicationCreator> { +public: + explicit IApplicationCreator(Core::System& system_); + ~IApplicationCreator() override; + +private: + Result CreateApplication(Out<SharedPointer<IApplicationAccessor>>, u64 application_id); +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp new file mode 100644 index 000000000..cb53b07e0 --- /dev/null +++ b/src/core/hle/service/am/service/application_functions.cpp @@ -0,0 +1,485 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "common/uuid.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/savedata_factory.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/service/application_functions.h" +#include "core/hle/service/am/service/storage.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/save_data_controller.h" +#include "core/hle/service/glue/glue_manager.h" +#include "core/hle/service/ns/application_manager_interface.h" +#include "core/hle/service/ns/service_getter_interface.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::AM { + +IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet) + : ServiceFramework{system_, "IApplicationFunctions"}, m_applet{std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {1, D<&IApplicationFunctions::PopLaunchParameter>, "PopLaunchParameter"}, + {10, nullptr, "CreateApplicationAndPushAndRequestToStart"}, + {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, + {12, nullptr, "CreateApplicationAndRequestToStart"}, + {13, nullptr, "CreateApplicationAndRequestToStartForQuest"}, + {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"}, + {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"}, + {20, D<&IApplicationFunctions::EnsureSaveData>, "EnsureSaveData"}, + {21, D<&IApplicationFunctions::GetDesiredLanguage>, "GetDesiredLanguage"}, + {22, D<&IApplicationFunctions::SetTerminateResult>, "SetTerminateResult"}, + {23, D<&IApplicationFunctions::GetDisplayVersion>, "GetDisplayVersion"}, + {24, nullptr, "GetLaunchStorageInfoForDebug"}, + {25, D<&IApplicationFunctions::ExtendSaveData>, "ExtendSaveData"}, + {26, D<&IApplicationFunctions::GetSaveDataSize>, "GetSaveDataSize"}, + {27, D<&IApplicationFunctions::CreateCacheStorage>, "CreateCacheStorage"}, + {28, D<&IApplicationFunctions::GetSaveDataSizeMax>, "GetSaveDataSizeMax"}, + {29, D<&IApplicationFunctions::GetCacheStorageMax>, "GetCacheStorageMax"}, + {30, D<&IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed>, "BeginBlockingHomeButtonShortAndLongPressed"}, + {31, D<&IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed>, "EndBlockingHomeButtonShortAndLongPressed"}, + {32, D<&IApplicationFunctions::BeginBlockingHomeButton>, "BeginBlockingHomeButton"}, + {33, D<&IApplicationFunctions::EndBlockingHomeButton>, "EndBlockingHomeButton"}, + {34, nullptr, "SelectApplicationLicense"}, + {35, nullptr, "GetDeviceSaveDataSizeMax"}, + {36, nullptr, "GetLimitedApplicationLicense"}, + {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"}, + {40, D<&IApplicationFunctions::NotifyRunning>, "NotifyRunning"}, + {50, D<&IApplicationFunctions::GetPseudoDeviceId>, "GetPseudoDeviceId"}, + {60, nullptr, "SetMediaPlaybackStateForApplication"}, + {65, D<&IApplicationFunctions::IsGamePlayRecordingSupported>, "IsGamePlayRecordingSupported"}, + {66, D<&IApplicationFunctions::InitializeGamePlayRecording>, "InitializeGamePlayRecording"}, + {67, D<&IApplicationFunctions::SetGamePlayRecordingState>, "SetGamePlayRecordingState"}, + {68, nullptr, "RequestFlushGamePlayingMovieForDebug"}, + {70, nullptr, "RequestToShutdown"}, + {71, nullptr, "RequestToReboot"}, + {72, nullptr, "RequestToSleep"}, + {80, nullptr, "ExitAndRequestToShowThanksMessage"}, + {90, D<&IApplicationFunctions::EnableApplicationCrashReport>, "EnableApplicationCrashReport"}, + {100, D<&IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer>, "InitializeApplicationCopyrightFrameBuffer"}, + {101, D<&IApplicationFunctions::SetApplicationCopyrightImage>, "SetApplicationCopyrightImage"}, + {102, D<&IApplicationFunctions::SetApplicationCopyrightVisibility>, "SetApplicationCopyrightVisibility"}, + {110, D<&IApplicationFunctions::QueryApplicationPlayStatistics>, "QueryApplicationPlayStatistics"}, + {111, D<&IApplicationFunctions::QueryApplicationPlayStatisticsByUid>, "QueryApplicationPlayStatisticsByUid"}, + {120, D<&IApplicationFunctions::ExecuteProgram>, "ExecuteProgram"}, + {121, D<&IApplicationFunctions::ClearUserChannel>, "ClearUserChannel"}, + {122, D<&IApplicationFunctions::UnpopToUserChannel>, "UnpopToUserChannel"}, + {123, D<&IApplicationFunctions::GetPreviousProgramIndex>, "GetPreviousProgramIndex"}, + {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, + {130, D<&IApplicationFunctions::GetGpuErrorDetectedSystemEvent>, "GetGpuErrorDetectedSystemEvent"}, + {131, nullptr, "SetDelayTimeToAbortOnGpuError"}, + {140, D<&IApplicationFunctions::GetFriendInvitationStorageChannelEvent>, "GetFriendInvitationStorageChannelEvent"}, + {141, D<&IApplicationFunctions::TryPopFromFriendInvitationStorageChannel>, "TryPopFromFriendInvitationStorageChannel"}, + {150, D<&IApplicationFunctions::GetNotificationStorageChannelEvent>, "GetNotificationStorageChannelEvent"}, + {151, nullptr, "TryPopFromNotificationStorageChannel"}, + {160, D<&IApplicationFunctions::GetHealthWarningDisappearedSystemEvent>, "GetHealthWarningDisappearedSystemEvent"}, + {170, nullptr, "SetHdcpAuthenticationActivated"}, + {180, nullptr, "GetLaunchRequiredVersion"}, + {181, nullptr, "UpgradeLaunchRequiredVersion"}, + {190, nullptr, "SendServerMaintenanceOverlayNotification"}, + {200, nullptr, "GetLastApplicationExitReason"}, + {500, nullptr, "StartContinuousRecordingFlushForDebug"}, + {1000, nullptr, "CreateMovieMaker"}, + {1001, D<&IApplicationFunctions::PrepareForJit>, "PrepareForJit"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IApplicationFunctions::~IApplicationFunctions() = default; + +Result IApplicationFunctions::PopLaunchParameter(Out<SharedPointer<IStorage>> out_storage, + LaunchParameterKind launch_parameter_kind) { + LOG_INFO(Service_AM, "called, kind={}", launch_parameter_kind); + + std::scoped_lock lk{m_applet->lock}; + + auto& channel = launch_parameter_kind == LaunchParameterKind::UserChannel + ? m_applet->user_channel_launch_parameter + : m_applet->preselected_user_launch_parameter; + + if (channel.empty()) { + LOG_WARNING(Service_AM, "Attempted to pop parameter {} but none was found!", + launch_parameter_kind); + R_THROW(AM::ResultNoDataInChannel); + } + + auto data = channel.back(); + channel.pop_back(); + + *out_storage = std::make_shared<IStorage>(system, std::move(data)); + R_SUCCEED(); +} + +Result IApplicationFunctions::EnsureSaveData(Out<u64> out_size, Common::UUID user_id) { + LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString()); + + FileSys::SaveDataAttribute attribute{}; + attribute.title_id = m_applet->program_id; + attribute.user_id = user_id.AsU128(); + attribute.type = FileSys::SaveDataType::SaveData; + + FileSys::VirtualDir save_data{}; + R_TRY(system.GetFileSystemController().OpenSaveDataController()->CreateSaveData( + &save_data, FileSys::SaveDataSpaceId::NandUser, attribute)); + + *out_size = 0; + R_SUCCEED(); +} + +Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) { + // FIXME: all of this stuff belongs to ns + // TODO(bunnei): This should be configurable + LOG_DEBUG(Service_AM, "called"); + + // Get supported languages from NACP, if possible + // Default to 0 (all languages supported) + u32 supported_languages = 0; + + const auto res = [this] { + const FileSys::PatchManager pm{m_applet->program_id, system.GetFileSystemController(), + system.GetContentProvider()}; + auto metadata = pm.GetControlMetadata(); + if (metadata.first != nullptr) { + return metadata; + } + + const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(m_applet->program_id), + system.GetFileSystemController(), + system.GetContentProvider()}; + return pm_update.GetControlMetadata(); + }(); + + if (res.first != nullptr) { + supported_languages = res.first->GetSupportedLanguages(); + } + + // Call IApplicationManagerInterface implementation. + auto& service_manager = system.ServiceManager(); + auto ns_am2 = service_manager.GetService<NS::IServiceGetterInterface>("ns:am2"); + + std::shared_ptr<NS::IApplicationManagerInterface> app_man; + R_TRY(ns_am2->GetApplicationManagerInterface(&app_man)); + + // Get desired application language + NS::ApplicationLanguage desired_language{}; + R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages)); + + // Convert to settings language code. + R_TRY(app_man->ConvertApplicationLanguageToLanguageCode(out_language_code, desired_language)); + + LOG_DEBUG(Service_AM, "got desired_language={:016X}", *out_language_code); + R_SUCCEED(); +} + +Result IApplicationFunctions::SetTerminateResult(Result terminate_result) { + LOG_INFO(Service_AM, "(STUBBED) called, result={:#x} ({}-{})", terminate_result.GetInnerValue(), + static_cast<u32>(terminate_result.GetModule()) + 2000, + terminate_result.GetDescription()); + + std::scoped_lock lk{m_applet->lock}; + m_applet->terminate_result = terminate_result; + + R_SUCCEED(); +} + +Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_version) { + LOG_DEBUG(Service_AM, "called"); + + const auto res = [this] { + const FileSys::PatchManager pm{m_applet->program_id, system.GetFileSystemController(), + system.GetContentProvider()}; + auto metadata = pm.GetControlMetadata(); + if (metadata.first != nullptr) { + return metadata; + } + + const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(m_applet->program_id), + system.GetFileSystemController(), + system.GetContentProvider()}; + return pm_update.GetControlMetadata(); + }(); + + if (res.first != nullptr) { + const auto& version = res.first->GetVersionString(); + std::memcpy(out_display_version->string.data(), version.data(), + std::min(version.size(), out_display_version->string.size())); + } else { + static constexpr char default_version[]{"1.0.0"}; + std::memcpy(out_display_version->string.data(), default_version, sizeof(default_version)); + } + + out_display_version->string[out_display_version->string.size() - 1] = '\0'; + R_SUCCEED(); +} + +Result IApplicationFunctions::ExtendSaveData(Out<u64> out_required_size, FileSys::SaveDataType type, + Common::UUID user_id, u64 normal_size, + u64 journal_size) { + LOG_DEBUG(Service_AM, "called with type={} user_id={} normal={:#x} journal={:#x}", + static_cast<u8>(type), user_id.FormattedString(), normal_size, journal_size); + + system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize( + type, m_applet->program_id, user_id.AsU128(), {normal_size, journal_size}); + + // The following value is used to indicate the amount of space remaining on failure + // due to running out of space. Since we always succeed, this should be 0. + *out_required_size = 0; + + R_SUCCEED(); +} + +Result IApplicationFunctions::GetSaveDataSize(Out<u64> out_normal_size, Out<u64> out_journal_size, + FileSys::SaveDataType type, Common::UUID user_id) { + LOG_DEBUG(Service_AM, "called with type={} user_id={}", type, user_id.FormattedString()); + + const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize( + type, m_applet->program_id, user_id.AsU128()); + + *out_normal_size = size.normal; + *out_journal_size = size.journal; + R_SUCCEED(); +} + +Result IApplicationFunctions::CreateCacheStorage(Out<u32> out_target_media, + Out<u64> out_required_size, u16 index, + u64 normal_size, u64 journal_size) { + LOG_WARNING(Service_AM, "(STUBBED) called with index={} size={:#x} journal_size={:#x}", index, + normal_size, journal_size); + + *out_target_media = 1; // Nand + *out_required_size = 0; + + R_SUCCEED(); +} + +Result IApplicationFunctions::GetSaveDataSizeMax(Out<u64> out_max_normal_size, + Out<u64> out_max_journal_size) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + *out_max_normal_size = 0xFFFFFFF; + *out_max_journal_size = 0xFFFFFFF; + + R_SUCCEED(); +} + +Result IApplicationFunctions::GetCacheStorageMax(Out<u32> out_cache_storage_index_max, + Out<u64> out_max_journal_size) { + LOG_DEBUG(Service_AM, "called"); + + std::vector<u8> nacp; + R_TRY(system.GetARPManager().GetControlProperty(&nacp, m_applet->program_id)); + + auto raw_nacp = std::make_unique<FileSys::RawNACP>(); + std::memcpy(raw_nacp.get(), nacp.data(), std::min(sizeof(*raw_nacp), nacp.size())); + + *out_cache_storage_index_max = static_cast<u32>(raw_nacp->cache_storage_max_index); + *out_max_journal_size = static_cast<u64>(raw_nacp->cache_storage_data_and_journal_max_size); + + R_SUCCEED(); +} + +Result IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(s64 unused) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{m_applet->lock}; + m_applet->home_button_long_pressed_blocked = true; + m_applet->home_button_short_pressed_blocked = true; + + R_SUCCEED(); +} + +Result IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{m_applet->lock}; + m_applet->home_button_long_pressed_blocked = false; + m_applet->home_button_short_pressed_blocked = false; + + R_SUCCEED(); +} + +Result IApplicationFunctions::BeginBlockingHomeButton(s64 timeout_ns) { + LOG_WARNING(Service_AM, "(STUBBED) called, timeout_ns={}", timeout_ns); + + std::scoped_lock lk{m_applet->lock}; + m_applet->home_button_long_pressed_blocked = true; + m_applet->home_button_short_pressed_blocked = true; + m_applet->home_button_double_click_enabled = true; + + R_SUCCEED(); +} + +Result IApplicationFunctions::EndBlockingHomeButton() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{m_applet->lock}; + m_applet->home_button_long_pressed_blocked = false; + m_applet->home_button_short_pressed_blocked = false; + m_applet->home_button_double_click_enabled = false; + + R_SUCCEED(); +} + +Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_became_running = true; + R_SUCCEED(); +} + +Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_pseudo_device_id = {}; + R_SUCCEED(); +} + +Result IApplicationFunctions::IsGamePlayRecordingSupported( + Out<bool> out_is_game_play_recording_supported) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_is_game_play_recording_supported = m_applet->game_play_recording_supported; + R_SUCCEED(); +} + +Result IApplicationFunctions::InitializeGamePlayRecording( + u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IApplicationFunctions::SetGamePlayRecordingState( + GamePlayRecordingState game_play_recording_state) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{m_applet->lock}; + m_applet->game_play_recording_state = game_play_recording_state; + + R_SUCCEED(); +} + +Result IApplicationFunctions::EnableApplicationCrashReport(bool enabled) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{m_applet->lock}; + m_applet->application_crash_report_enabled = enabled; + + R_SUCCEED(); +} + +Result IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer( + s32 width, s32 height, u64 transfer_memory_size, + InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IApplicationFunctions::SetApplicationCopyrightImage( + s32 x, s32 y, s32 width, s32 height, WindowOriginMode window_origin_mode, + InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> image_data) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IApplicationFunctions::SetApplicationCopyrightVisibility(bool visible) { + LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", visible); + R_SUCCEED(); +} + +Result IApplicationFunctions::QueryApplicationPlayStatistics( + Out<s32> out_entries, + OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics, + InArray<u64, BufferAttr_HipcMapAlias> application_ids) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_entries = 0; + R_SUCCEED(); +} + +Result IApplicationFunctions::QueryApplicationPlayStatisticsByUid( + Out<s32> out_entries, + OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics, + Common::UUID user_id, InArray<u64, BufferAttr_HipcMapAlias> application_ids) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_entries = 0; + R_SUCCEED(); +} + +Result IApplicationFunctions::ExecuteProgram(ProgramSpecifyKind kind, u64 value) { + LOG_WARNING(Service_AM, "(STUBBED) called, kind={}, value={}", kind, value); + ASSERT(kind == ProgramSpecifyKind::ExecuteProgram || + kind == ProgramSpecifyKind::RestartProgram); + + // Copy user channel ownership into the system so that it will be preserved + system.GetUserChannel() = m_applet->user_channel_launch_parameter; + system.ExecuteProgram(value); + R_SUCCEED(); +} + +Result IApplicationFunctions::ClearUserChannel() { + LOG_DEBUG(Service_AM, "called"); + m_applet->user_channel_launch_parameter.clear(); + R_SUCCEED(); +} + +Result IApplicationFunctions::UnpopToUserChannel(SharedPointer<IStorage> storage) { + LOG_DEBUG(Service_AM, "called"); + m_applet->user_channel_launch_parameter.push_back(storage->GetData()); + R_SUCCEED(); +} + +Result IApplicationFunctions::GetPreviousProgramIndex(Out<s32> out_previous_program_index) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_previous_program_index = m_applet->previous_program_index; + R_SUCCEED(); +} + +Result IApplicationFunctions::GetGpuErrorDetectedSystemEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_event = m_applet->gpu_error_detected_event.GetHandle(); + R_SUCCEED(); +} + +Result IApplicationFunctions::GetFriendInvitationStorageChannelEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = m_applet->friend_invitation_storage_channel_event.GetHandle(); + R_SUCCEED(); +} + +Result IApplicationFunctions::TryPopFromFriendInvitationStorageChannel( + Out<SharedPointer<IStorage>> out_storage) { + LOG_INFO(Service_AM, "(STUBBED) called"); + R_THROW(AM::ResultNoDataInChannel); +} + +Result IApplicationFunctions::GetNotificationStorageChannelEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = m_applet->notification_storage_channel_event.GetHandle(); + R_SUCCEED(); +} + +Result IApplicationFunctions::GetHealthWarningDisappearedSystemEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = m_applet->health_warning_disappeared_system_event.GetHandle(); + R_SUCCEED(); +} + +Result IApplicationFunctions::PrepareForJit() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{m_applet->lock}; + m_applet->jit_service_launched = true; + + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_functions.h b/src/core/hle/service/am/service/application_functions.h new file mode 100644 index 000000000..10025a152 --- /dev/null +++ b/src/core/hle/service/am/service/application_functions.h @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/uuid.h" +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace FileSys { +enum class SaveDataType : u8; +} + +namespace Kernel { +class KReadableEvent; +} + +namespace Service::AM { + +struct Applet; +class IStorage; + +class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { +public: + explicit IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet); + ~IApplicationFunctions() override; + +private: + Result PopLaunchParameter(Out<SharedPointer<IStorage>> out_storage, + LaunchParameterKind launch_parameter_kind); + Result EnsureSaveData(Out<u64> out_size, Common::UUID user_id); + Result GetDesiredLanguage(Out<u64> out_language_code); + Result SetTerminateResult(Result terminate_result); + Result GetDisplayVersion(Out<DisplayVersion> out_display_version); + Result ExtendSaveData(Out<u64> out_required_size, FileSys::SaveDataType type, + Common::UUID user_id, u64 normal_size, u64 journal_size); + Result GetSaveDataSize(Out<u64> out_normal_size, Out<u64> out_journal_size, + FileSys::SaveDataType type, Common::UUID user_id); + Result CreateCacheStorage(Out<u32> out_target_media, Out<u64> out_required_size, u16 index, + u64 normal_size, u64 journal_size); + Result GetSaveDataSizeMax(Out<u64> out_max_normal_size, Out<u64> out_max_journal_size); + Result GetCacheStorageMax(Out<u32> out_cache_storage_index_max, Out<u64> out_max_journal_size); + Result BeginBlockingHomeButtonShortAndLongPressed(s64 unused); + Result EndBlockingHomeButtonShortAndLongPressed(); + Result BeginBlockingHomeButton(s64 timeout_ns); + Result EndBlockingHomeButton(); + Result NotifyRunning(Out<bool> out_became_running); + Result GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id); + Result IsGamePlayRecordingSupported(Out<bool> out_is_game_play_recording_supported); + Result InitializeGamePlayRecording( + u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle); + Result SetGamePlayRecordingState(GamePlayRecordingState game_play_recording_state); + Result EnableApplicationCrashReport(bool enabled); + Result InitializeApplicationCopyrightFrameBuffer( + s32 width, s32 height, u64 transfer_memory_size, + InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle); + Result SetApplicationCopyrightImage( + s32 x, s32 y, s32 width, s32 height, WindowOriginMode window_origin_mode, + InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> image_data); + Result SetApplicationCopyrightVisibility(bool visible); + Result QueryApplicationPlayStatistics( + Out<s32> out_entries, + OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics, + InArray<u64, BufferAttr_HipcMapAlias> application_ids); + Result QueryApplicationPlayStatisticsByUid( + Out<s32> out_entries, + OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics, + Common::UUID user_id, InArray<u64, BufferAttr_HipcMapAlias> application_ids); + Result ExecuteProgram(ProgramSpecifyKind kind, u64 value); + Result ClearUserChannel(); + Result UnpopToUserChannel(SharedPointer<IStorage> storage); + Result GetPreviousProgramIndex(Out<s32> out_previous_program_index); + Result GetGpuErrorDetectedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result GetFriendInvitationStorageChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result TryPopFromFriendInvitationStorageChannel(Out<SharedPointer<IStorage>> out_storage); + Result GetNotificationStorageChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result GetHealthWarningDisappearedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result PrepareForJit(); + + const std::shared_ptr<Applet> m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_proxy.cpp b/src/core/hle/service/am/service/application_proxy.cpp new file mode 100644 index 000000000..19d6a3b89 --- /dev/null +++ b/src/core/hle/service/am/service/application_proxy.cpp @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/service/applet_common_functions.h" +#include "core/hle/service/am/service/application_functions.h" +#include "core/hle/service/am/service/application_proxy.h" +#include "core/hle/service/am/service/audio_controller.h" +#include "core/hle/service/am/service/common_state_getter.h" +#include "core/hle/service/am/service/debug_functions.h" +#include "core/hle/service/am/service/display_controller.h" +#include "core/hle/service/am/service/library_applet_creator.h" +#include "core/hle/service/am/service/process_winding_controller.h" +#include "core/hle/service/am/service/self_controller.h" +#include "core/hle/service/am/service/window_controller.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet, + Kernel::KProcess* process) + : ServiceFramework{system_, "IApplicationProxy"}, m_process{process}, m_applet{ + std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IApplicationProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, + {1, D<&IApplicationProxy::GetSelfController>, "GetSelfController"}, + {2, D<&IApplicationProxy::GetWindowController>, "GetWindowController"}, + {3, D<&IApplicationProxy::GetAudioController>, "GetAudioController"}, + {4, D<&IApplicationProxy::GetDisplayController>, "GetDisplayController"}, + {10, D<&IApplicationProxy::GetProcessWindingController>, "GetProcessWindingController"}, + {11, D<&IApplicationProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"}, + {20, D<&IApplicationProxy::GetApplicationFunctions>, "GetApplicationFunctions"}, + {1000, D<&IApplicationProxy::GetDebugFunctions>, "GetDebugFunctions"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IApplicationProxy::~IApplicationProxy() = default; + +Result IApplicationProxy::GetAudioController( + Out<SharedPointer<IAudioController>> out_audio_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_audio_controller = std::make_shared<IAudioController>(system); + R_SUCCEED(); +} + +Result IApplicationProxy::GetDisplayController( + Out<SharedPointer<IDisplayController>> out_display_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_display_controller = std::make_shared<IDisplayController>(system, m_applet); + R_SUCCEED(); +} + +Result IApplicationProxy::GetProcessWindingController( + Out<SharedPointer<IProcessWindingController>> out_process_winding_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_process_winding_controller = std::make_shared<IProcessWindingController>(system, m_applet); + R_SUCCEED(); +} + +Result IApplicationProxy::GetDebugFunctions( + Out<SharedPointer<IDebugFunctions>> out_debug_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_debug_functions = std::make_shared<IDebugFunctions>(system); + R_SUCCEED(); +} + +Result IApplicationProxy::GetWindowController( + Out<SharedPointer<IWindowController>> out_window_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_window_controller = std::make_shared<IWindowController>(system, m_applet); + R_SUCCEED(); +} + +Result IApplicationProxy::GetSelfController( + Out<SharedPointer<ISelfController>> out_self_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process); + R_SUCCEED(); +} + +Result IApplicationProxy::GetCommonStateGetter( + Out<SharedPointer<ICommonStateGetter>> out_common_state_getter) { + LOG_DEBUG(Service_AM, "called"); + *out_common_state_getter = std::make_shared<ICommonStateGetter>(system, m_applet); + R_SUCCEED(); +} + +Result IApplicationProxy::GetLibraryAppletCreator( + Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator) { + LOG_DEBUG(Service_AM, "called"); + *out_library_applet_creator = std::make_shared<ILibraryAppletCreator>(system, m_applet); + R_SUCCEED(); +} + +Result IApplicationProxy::GetApplicationFunctions( + Out<SharedPointer<IApplicationFunctions>> out_application_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_application_functions = std::make_shared<IApplicationFunctions>(system, m_applet); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_proxy.h b/src/core/hle/service/am/service/application_proxy.h new file mode 100644 index 000000000..6da350df7 --- /dev/null +++ b/src/core/hle/service/am/service/application_proxy.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; +class IAudioController; +class IApplicationFunctions; +class ICommonStateGetter; +class IDebugFunctions; +class IDisplayController; +class ILibraryAppletCreator; +class IProcessWindingController; +class ISelfController; +class IWindowController; + +class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { +public: + explicit IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet, + Kernel::KProcess* process); + ~IApplicationProxy(); + +private: + Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller); + Result GetDisplayController(Out<SharedPointer<IDisplayController>> out_display_controller); + Result GetProcessWindingController( + Out<SharedPointer<IProcessWindingController>> out_process_winding_controller); + Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions); + Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller); + Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller); + Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter); + Result GetLibraryAppletCreator( + Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator); + Result GetApplicationFunctions( + Out<SharedPointer<IApplicationFunctions>> out_application_functions); + +private: + Kernel::KProcess* const m_process; + const std::shared_ptr<Applet> m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_proxy_service.cpp b/src/core/hle/service/am/service/application_proxy_service.cpp new file mode 100644 index 000000000..fd66e77b9 --- /dev/null +++ b/src/core/hle/service/am/service/application_proxy_service.cpp @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/service/application_proxy.h" +#include "core/hle/service/am/service/application_proxy_service.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IApplicationProxyService::IApplicationProxyService(Core::System& system_) + : ServiceFramework{system_, "appletOE"} { + static const FunctionInfo functions[] = { + {0, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy"}, + }; + RegisterHandlers(functions); +} + +IApplicationProxyService::~IApplicationProxyService() = default; + +Result IApplicationProxyService::OpenApplicationProxy( + Out<SharedPointer<IApplicationProxy>> out_application_proxy, ClientProcessId pid, + InCopyHandle<Kernel::KProcess> process_handle) { + LOG_DEBUG(Service_AM, "called"); + + if (const auto applet = this->GetAppletFromProcessId(pid)) { + *out_application_proxy = + std::make_shared<IApplicationProxy>(system, applet, process_handle.Get()); + R_SUCCEED(); + } else { + UNIMPLEMENTED(); + R_THROW(ResultUnknown); + } +} + +std::shared_ptr<Applet> IApplicationProxyService::GetAppletFromProcessId(ProcessId process_id) { + return system.GetAppletManager().GetByAppletResourceUserId(process_id.pid); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_proxy_service.h b/src/core/hle/service/am/service/application_proxy_service.h new file mode 100644 index 000000000..8efafa31a --- /dev/null +++ b/src/core/hle/service/am/service/application_proxy_service.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service { + +namespace AM { + +struct Applet; +class IApplicationProxy; + +class IApplicationProxyService final : public ServiceFramework<IApplicationProxyService> { +public: + explicit IApplicationProxyService(Core::System& system_); + ~IApplicationProxyService() override; + +private: + Result OpenApplicationProxy(Out<SharedPointer<IApplicationProxy>> out_application_proxy, + ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle); + +private: + std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid); +}; + +} // namespace AM +} // namespace Service diff --git a/src/core/hle/service/am/service/audio_controller.cpp b/src/core/hle/service/am/service/audio_controller.cpp new file mode 100644 index 000000000..ad731c7bd --- /dev/null +++ b/src/core/hle/service/am/service/audio_controller.cpp @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/service/audio_controller.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IAudioController::IAudioController(Core::System& system_) + : ServiceFramework{system_, "IAudioController"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IAudioController::SetExpectedMasterVolume>, "SetExpectedMasterVolume"}, + {1, D<&IAudioController::GetMainAppletExpectedMasterVolume>, "GetMainAppletExpectedMasterVolume"}, + {2, D<&IAudioController::GetLibraryAppletExpectedMasterVolume>, "GetLibraryAppletExpectedMasterVolume"}, + {3, D<&IAudioController::ChangeMainAppletMasterVolume>, "ChangeMainAppletMasterVolume"}, + {4, D<&IAudioController::SetTransparentVolumeRate>, "SetTransparentVolumeRate"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IAudioController::~IAudioController() = default; + +Result IAudioController::SetExpectedMasterVolume(f32 main_applet_volume, + f32 library_applet_volume) { + LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}", + main_applet_volume, library_applet_volume); + + // Ensure the volume values remain within the 0-100% range + m_main_applet_volume = std::clamp(main_applet_volume, MinAllowedVolume, MaxAllowedVolume); + m_library_applet_volume = std::clamp(library_applet_volume, MinAllowedVolume, MaxAllowedVolume); + + R_SUCCEED(); +} + +Result IAudioController::GetMainAppletExpectedMasterVolume(Out<f32> out_main_applet_volume) { + LOG_DEBUG(Service_AM, "called. main_applet_volume={}", m_main_applet_volume); + *out_main_applet_volume = m_main_applet_volume; + R_SUCCEED(); +} + +Result IAudioController::GetLibraryAppletExpectedMasterVolume(Out<f32> out_library_applet_volume) { + LOG_DEBUG(Service_AM, "called. library_applet_volume={}", m_library_applet_volume); + *out_library_applet_volume = m_library_applet_volume; + R_SUCCEED(); +} + +Result IAudioController::ChangeMainAppletMasterVolume(f32 volume, s64 fade_time_ns) { + LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", volume, fade_time_ns); + + m_main_applet_volume = std::clamp(volume, MinAllowedVolume, MaxAllowedVolume); + m_fade_time_ns = std::chrono::nanoseconds{fade_time_ns}; + + R_SUCCEED(); +} + +Result IAudioController::SetTransparentVolumeRate(f32 transparent_volume_rate) { + LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate); + + // Clamp volume range to 0-100%. + m_transparent_volume_rate = + std::clamp(transparent_volume_rate, MinAllowedVolume, MaxAllowedVolume); + + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/audio_controller.h b/src/core/hle/service/am/service/audio_controller.h new file mode 100644 index 000000000..4b0f3f9ae --- /dev/null +++ b/src/core/hle/service/am/service/audio_controller.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IAudioController final : public ServiceFramework<IAudioController> { +public: + explicit IAudioController(Core::System& system_); + ~IAudioController() override; + +private: + Result SetExpectedMasterVolume(f32 main_applet_volume, f32 library_applet_volume); + Result GetMainAppletExpectedMasterVolume(Out<f32> out_main_applet_volume); + Result GetLibraryAppletExpectedMasterVolume(Out<f32> out_library_applet_volume); + Result ChangeMainAppletMasterVolume(f32 volume, s64 fade_time_ns); + Result SetTransparentVolumeRate(f32 transparent_volume_rate); + + static constexpr float MinAllowedVolume = 0.0f; + static constexpr float MaxAllowedVolume = 1.0f; + + float m_main_applet_volume{0.25f}; + float m_library_applet_volume{MaxAllowedVolume}; + float m_transparent_volume_rate{MinAllowedVolume}; + + // Volume transition fade time in nanoseconds. + // e.g. If the main applet volume was 0% and was changed to 50% + // with a fade of 50ns, then over the course of 50ns, + // the volume will gradually fade up to 50% + std::chrono::nanoseconds m_fade_time_ns{0}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/common_state_getter.cpp b/src/core/hle/service/am/service/common_state_getter.cpp new file mode 100644 index 000000000..548498e83 --- /dev/null +++ b/src/core/hle/service/am/service/common_state_getter.cpp @@ -0,0 +1,278 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/service/common_state_getter.h" +#include "core/hle/service/am/service/lock_accessor.h" +#include "core/hle/service/apm/apm_interface.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/pm/pm.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/vi/vi.h" +#include "core/hle/service/vi/vi_types.h" + +namespace Service::AM { + +ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet) + : ServiceFramework{system_, "ICommonStateGetter"}, m_applet{std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&ICommonStateGetter::GetEventHandle>, "GetEventHandle"}, + {1, D<&ICommonStateGetter::ReceiveMessage>, "ReceiveMessage"}, + {2, nullptr, "GetThisAppletKind"}, + {3, nullptr, "AllowToEnterSleep"}, + {4, nullptr, "DisallowToEnterSleep"}, + {5, D<&ICommonStateGetter::GetOperationMode>, "GetOperationMode"}, + {6, D<&ICommonStateGetter::GetPerformanceMode>, "GetPerformanceMode"}, + {7, nullptr, "GetCradleStatus"}, + {8, D<&ICommonStateGetter::GetBootMode>, "GetBootMode"}, + {9, D<&ICommonStateGetter::GetCurrentFocusState>, "GetCurrentFocusState"}, + {10, D<&ICommonStateGetter::RequestToAcquireSleepLock>, "RequestToAcquireSleepLock"}, + {11, nullptr, "ReleaseSleepLock"}, + {12, nullptr, "ReleaseSleepLockTransiently"}, + {13, D<&ICommonStateGetter::GetAcquiredSleepLockEvent>, "GetAcquiredSleepLockEvent"}, + {14, nullptr, "GetWakeupCount"}, + {20, nullptr, "PushToGeneralChannel"}, + {30, nullptr, "GetHomeButtonReaderLockAccessor"}, + {31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"}, + {32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"}, + {40, nullptr, "GetCradleFwVersion"}, + {50, D<&ICommonStateGetter::IsVrModeEnabled>, "IsVrModeEnabled"}, + {51, D<&ICommonStateGetter::SetVrModeEnabled>, "SetVrModeEnabled"}, + {52, D<&ICommonStateGetter::SetLcdBacklighOffEnabled>, "SetLcdBacklighOffEnabled"}, + {53, D<&ICommonStateGetter::BeginVrModeEx>, "BeginVrModeEx"}, + {54, D<&ICommonStateGetter::EndVrModeEx>, "EndVrModeEx"}, + {55, D<&ICommonStateGetter::IsInControllerFirmwareUpdateSection>, "IsInControllerFirmwareUpdateSection"}, + {59, nullptr, "SetVrPositionForDebug"}, + {60, D<&ICommonStateGetter::GetDefaultDisplayResolution>, "GetDefaultDisplayResolution"}, + {61, D<&ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent>, "GetDefaultDisplayResolutionChangeEvent"}, + {62, nullptr, "GetHdcpAuthenticationState"}, + {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, + {64, nullptr, "SetTvPowerStateMatchingMode"}, + {65, nullptr, "GetApplicationIdByContentActionName"}, + {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, + {67, nullptr, "CancelCpuBoostMode"}, + {68, D<&ICommonStateGetter::GetBuiltInDisplayType>, "GetBuiltInDisplayType"}, + {80, D<&ICommonStateGetter::PerformSystemButtonPressingIfInFocus>, "PerformSystemButtonPressingIfInFocus"}, + {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, + {91, nullptr, "GetCurrentPerformanceConfiguration"}, + {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"}, + {110, nullptr, "OpenMyGpuErrorHandler"}, + {120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"}, + {200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"}, + {300, D<&ICommonStateGetter::GetSettingsPlatformRegion>, "GetSettingsPlatformRegion"}, + {400, nullptr, "ActivateMigrationService"}, + {401, nullptr, "DeactivateMigrationService"}, + {500, nullptr, "DisableSleepTillShutdown"}, + {501, nullptr, "SuppressDisablingSleepTemporarily"}, + {502, nullptr, "IsSleepEnabled"}, + {503, nullptr, "IsDisablingSleepSuppressed"}, + {900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ICommonStateGetter::~ICommonStateGetter() = default; + +Result ICommonStateGetter::GetEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = &m_applet->message_queue.GetMessageReceiveEvent(); + R_SUCCEED(); +} + +Result ICommonStateGetter::ReceiveMessage(Out<AppletMessage> out_applet_message) { + LOG_DEBUG(Service_AM, "called"); + + *out_applet_message = m_applet->message_queue.PopMessage(); + if (*out_applet_message == AppletMessage::None) { + LOG_ERROR(Service_AM, "Tried to pop message but none was available!"); + R_THROW(AM::ResultNoMessages); + } + + R_SUCCEED(); +} + +Result ICommonStateGetter::GetCurrentFocusState(Out<FocusState> out_focus_state) { + LOG_DEBUG(Service_AM, "called"); + + std::scoped_lock lk{m_applet->lock}; + *out_focus_state = m_applet->focus_state; + + R_SUCCEED(); +} + +Result ICommonStateGetter::RequestToAcquireSleepLock() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + // Sleep lock is acquired immediately. + m_applet->sleep_lock_event.Signal(); + R_SUCCEED(); +} + +Result ICommonStateGetter::GetAcquiredSleepLockEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_AM, "called"); + *out_event = m_applet->sleep_lock_event.GetHandle(); + R_SUCCEED(); +} + +Result ICommonStateGetter::GetReaderLockAccessorEx( + Out<SharedPointer<ILockAccessor>> out_lock_accessor, u32 button_type) { + LOG_INFO(Service_AM, "called, button_type={}", button_type); + *out_lock_accessor = std::make_shared<ILockAccessor>(system); + R_SUCCEED(); +} + +Result ICommonStateGetter::GetWriterLockAccessorEx( + Out<SharedPointer<ILockAccessor>> out_lock_accessor, u32 button_type) { + LOG_INFO(Service_AM, "called, button_type={}", button_type); + *out_lock_accessor = std::make_shared<ILockAccessor>(system); + R_SUCCEED(); +} + +Result ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = &m_applet->message_queue.GetOperationModeChangedEvent(); + R_SUCCEED(); +} + +Result ICommonStateGetter::GetOperationMode(Out<OperationMode> out_operation_mode) { + const bool use_docked_mode{Settings::IsDockedMode()}; + LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); + *out_operation_mode = use_docked_mode ? OperationMode::Docked : OperationMode::Handheld; + R_SUCCEED(); +} + +Result ICommonStateGetter::GetPerformanceMode(Out<APM::PerformanceMode> out_performance_mode) { + LOG_DEBUG(Service_AM, "called"); + *out_performance_mode = system.GetAPMController().GetCurrentPerformanceMode(); + R_SUCCEED(); +} + +Result ICommonStateGetter::GetBootMode(Out<PM::SystemBootMode> out_boot_mode) { + LOG_DEBUG(Service_AM, "called"); + *out_boot_mode = Service::PM::SystemBootMode::Normal; + R_SUCCEED(); +} + +Result ICommonStateGetter::IsVrModeEnabled(Out<bool> out_is_vr_mode_enabled) { + LOG_DEBUG(Service_AM, "called"); + + std::scoped_lock lk{m_applet->lock}; + *out_is_vr_mode_enabled = m_applet->vr_mode_enabled; + R_SUCCEED(); +} + +Result ICommonStateGetter::SetVrModeEnabled(bool is_vr_mode_enabled) { + std::scoped_lock lk{m_applet->lock}; + m_applet->vr_mode_enabled = is_vr_mode_enabled; + LOG_WARNING(Service_AM, "VR Mode is {}", m_applet->vr_mode_enabled ? "on" : "off"); + R_SUCCEED(); +} + +Result ICommonStateGetter::SetLcdBacklighOffEnabled(bool is_lcd_backlight_off_enabled) { + LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}", + is_lcd_backlight_off_enabled); + R_SUCCEED(); +} + +Result ICommonStateGetter::BeginVrModeEx() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + std::scoped_lock lk{m_applet->lock}; + m_applet->vr_mode_enabled = true; + R_SUCCEED(); +} + +Result ICommonStateGetter::EndVrModeEx() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + std::scoped_lock lk{m_applet->lock}; + m_applet->vr_mode_enabled = false; + R_SUCCEED(); +} + +Result ICommonStateGetter::IsInControllerFirmwareUpdateSection( + Out<bool> out_is_in_controller_firmware_update_section) { + LOG_INFO(Service_AM, "called"); + *out_is_in_controller_firmware_update_section = false; + R_SUCCEED(); +} + +Result ICommonStateGetter::GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height) { + LOG_DEBUG(Service_AM, "called"); + + if (Settings::IsDockedMode()) { + *out_width = static_cast<u32>(Service::VI::DisplayResolution::DockedWidth); + *out_height = static_cast<u32>(Service::VI::DisplayResolution::DockedHeight); + } else { + *out_width = static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth); + *out_height = static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight); + } + + R_SUCCEED(); +} + +void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS"); + + const auto& sm = system.ServiceManager(); + const auto apm_sys = sm.GetService<APM::APM_Sys>("apm:sys"); + ASSERT(apm_sys != nullptr); + + apm_sys->SetCpuBoostMode(ctx); +} + +Result ICommonStateGetter::GetBuiltInDisplayType(Out<s32> out_display_type) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_display_type = 0; + R_SUCCEED(); +} + +Result ICommonStateGetter::PerformSystemButtonPressingIfInFocus(SystemButtonType type) { + LOG_WARNING(Service_AM, "(STUBBED) called, type={}", type); + R_SUCCEED(); +} + +Result ICommonStateGetter::GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_operation_mode_system_info = 0; + R_SUCCEED(); +} + +Result ICommonStateGetter::GetAppletLaunchedHistory( + Out<s32> out_count, OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids) { + LOG_INFO(Service_AM, "called"); + + std::shared_ptr<Applet> current_applet = m_applet; + + for (*out_count = 0; + *out_count < static_cast<s32>(out_applet_ids.size()) && current_applet != nullptr; + /* ... */) { + out_applet_ids[(*out_count)++] = current_applet->applet_id; + current_applet = current_applet->caller_applet.lock(); + } + + R_SUCCEED(); +} + +Result ICommonStateGetter::GetSettingsPlatformRegion( + Out<SysPlatformRegion> out_settings_platform_region) { + LOG_INFO(Service_AM, "called"); + *out_settings_platform_region = SysPlatformRegion::Global; + R_SUCCEED(); +} + +Result ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{m_applet->lock}; + m_applet->request_exit_to_library_applet_at_execute_next_program_enabled = true; + + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/common_state_getter.h b/src/core/hle/service/am/service/common_state_getter.h new file mode 100644 index 000000000..5a8dca3d6 --- /dev/null +++ b/src/core/hle/service/am/service/common_state_getter.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/apm/apm_controller.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/pm/pm.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KReadableEvent; +} + +namespace Service::AM { + +struct Applet; +class ILockAccessor; + +class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { +public: + explicit ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_); + ~ICommonStateGetter() override; + +private: + Result GetEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result ReceiveMessage(Out<AppletMessage> out_applet_message); + Result GetCurrentFocusState(Out<FocusState> out_focus_state); + Result RequestToAcquireSleepLock(); + Result GetAcquiredSleepLockEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result GetReaderLockAccessorEx(Out<SharedPointer<ILockAccessor>> out_lock_accessor, + u32 button_type); + Result GetWriterLockAccessorEx(Out<SharedPointer<ILockAccessor>> out_lock_accessor, + u32 button_type); + Result GetDefaultDisplayResolutionChangeEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result GetOperationMode(Out<OperationMode> out_operation_mode); + Result GetPerformanceMode(Out<APM::PerformanceMode> out_performance_mode); + Result GetBootMode(Out<PM::SystemBootMode> out_boot_mode); + Result IsVrModeEnabled(Out<bool> out_is_vr_mode_enabled); + Result SetVrModeEnabled(bool is_vr_mode_enabled); + Result SetLcdBacklighOffEnabled(bool is_lcd_backlight_off_enabled); + Result BeginVrModeEx(); + Result EndVrModeEx(); + Result IsInControllerFirmwareUpdateSection( + Out<bool> out_is_in_controller_firmware_update_section); + Result GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height); + Result GetBuiltInDisplayType(Out<s32> out_display_type); + Result PerformSystemButtonPressingIfInFocus(SystemButtonType type); + Result GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info); + Result GetAppletLaunchedHistory(Out<s32> out_count, + OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids); + Result GetSettingsPlatformRegion(Out<SysPlatformRegion> out_settings_platform_region); + Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(); + + void SetCpuBoostMode(HLERequestContext& ctx); + + const std::shared_ptr<Applet> m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/cradle_firmware_updater.cpp b/src/core/hle/service/am/service/cradle_firmware_updater.cpp new file mode 100644 index 000000000..0a8af0858 --- /dev/null +++ b/src/core/hle/service/am/service/cradle_firmware_updater.cpp @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/service/cradle_firmware_updater.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +ICradleFirmwareUpdater::ICradleFirmwareUpdater(Core::System& system_) + : ServiceFramework{system_, "ICradleFirmwareUpdater"}, + m_context{system, "ICradleFirmwareUpdater"}, m_cradle_device_info_event{m_context} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&ICradleFirmwareUpdater::StartUpdate>, "StartUpdate"}, + {1, D<&ICradleFirmwareUpdater::FinishUpdate>, "FinishUpdate"}, + {2, D<&ICradleFirmwareUpdater::GetCradleDeviceInfo>, "GetCradleDeviceInfo"}, + {3, D<&ICradleFirmwareUpdater::GetCradleDeviceInfoChangeEvent>, "GetCradleDeviceInfoChangeEvent"}, + {4, nullptr, "GetUpdateProgressInfo"}, + {5, nullptr, "GetLastInternalResult"}, + + }; + // clang-format on + + RegisterHandlers(functions); +} + +ICradleFirmwareUpdater::~ICradleFirmwareUpdater() = default; + +Result ICradleFirmwareUpdater::StartUpdate() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result ICradleFirmwareUpdater::FinishUpdate() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result ICradleFirmwareUpdater::GetCradleDeviceInfo(Out<CradleDeviceInfo> out_cradle_device_info) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_cradle_device_info = {}; + R_SUCCEED(); +} + +Result ICradleFirmwareUpdater::GetCradleDeviceInfoChangeEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_event = m_cradle_device_info_event.GetHandle(); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/cradle_firmware_updater.h b/src/core/hle/service/am/service/cradle_firmware_updater.h new file mode 100644 index 000000000..3e803f0ae --- /dev/null +++ b/src/core/hle/service/am/service/cradle_firmware_updater.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/os/event.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct CradleDeviceInfo { + bool unknown0; + bool unknown1; + bool unknown2; + u64 unknown3; +}; +static_assert(sizeof(CradleDeviceInfo) == 0x10, "CradleDeviceInfo has incorrect size"); + +class ICradleFirmwareUpdater final : public ServiceFramework<ICradleFirmwareUpdater> { +public: + explicit ICradleFirmwareUpdater(Core::System& system_); + ~ICradleFirmwareUpdater() override; + +private: + Result StartUpdate(); + Result FinishUpdate(); + Result GetCradleDeviceInfo(Out<CradleDeviceInfo> out_cradle_device_info); + Result GetCradleDeviceInfoChangeEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + +private: + KernelHelpers::ServiceContext m_context; + Event m_cradle_device_info_event; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/debug_functions.cpp b/src/core/hle/service/am/service/debug_functions.cpp new file mode 100644 index 000000000..fcac4776d --- /dev/null +++ b/src/core/hle/service/am/service/debug_functions.cpp @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/service/debug_functions.h" + +namespace Service::AM { + +IDebugFunctions::IDebugFunctions(Core::System& system_) + : ServiceFramework{system_, "IDebugFunctions"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "NotifyMessageToHomeMenuForDebug"}, + {1, nullptr, "OpenMainApplication"}, + {10, nullptr, "PerformSystemButtonPressing"}, + {20, nullptr, "InvalidateTransitionLayer"}, + {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, + {31, nullptr, "RequestLaunchApplicationByApplicationLaunchInfoForDebug"}, + {40, nullptr, "GetAppletResourceUsageInfo"}, + {50, nullptr, "AddSystemProgramIdAndAppletIdForDebug"}, + {51, nullptr, "AddOperationConfirmedLibraryAppletIdForDebug"}, + {100, nullptr, "SetCpuBoostModeForApplet"}, + {101, nullptr, "CancelCpuBoostModeForApplet"}, + {110, nullptr, "PushToAppletBoundChannelForDebug"}, + {111, nullptr, "TryPopFromAppletBoundChannelForDebug"}, + {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"}, + {121, nullptr, "AlarmSettingNotificationDisableAppEventReserve"}, + {122, nullptr, "AlarmSettingNotificationPushAppEventNotify"}, + {130, nullptr, "FriendInvitationSetApplicationParameter"}, + {131, nullptr, "FriendInvitationClearApplicationParameter"}, + {132, nullptr, "FriendInvitationPushApplicationParameter"}, + {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"}, + {200, nullptr, "CreateFloatingLibraryAppletAccepterForDebug"}, + {300, nullptr, "TerminateAllRunningApplicationsForDebug"}, + {900, nullptr, "GetGrcProcessLaunchedSystemEvent"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IDebugFunctions::~IDebugFunctions() = default; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/debug_functions.h b/src/core/hle/service/am/service/debug_functions.h index d55968743..d55968743 100644 --- a/src/core/hle/service/am/debug_functions.h +++ b/src/core/hle/service/am/service/debug_functions.h diff --git a/src/core/hle/service/am/service/display_controller.cpp b/src/core/hle/service/am/service/display_controller.cpp new file mode 100644 index 000000000..ed71f9093 --- /dev/null +++ b/src/core/hle/service/am/service/display_controller.cpp @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/result.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/service/display_controller.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IDisplayController::IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_) + : ServiceFramework{system_, "IDisplayController"}, applet(std::move(applet_)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetLastForegroundCaptureImage"}, + {1, nullptr, "UpdateLastForegroundCaptureImage"}, + {2, nullptr, "GetLastApplicationCaptureImage"}, + {3, nullptr, "GetCallerAppletCaptureImage"}, + {4, nullptr, "UpdateCallerAppletCaptureImage"}, + {5, nullptr, "GetLastForegroundCaptureImageEx"}, + {6, nullptr, "GetLastApplicationCaptureImageEx"}, + {7, D<&IDisplayController::GetCallerAppletCaptureImageEx>, "GetCallerAppletCaptureImageEx"}, + {8, D<&IDisplayController::TakeScreenShotOfOwnLayer>, "TakeScreenShotOfOwnLayer"}, + {9, nullptr, "CopyBetweenCaptureBuffers"}, + {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, + {11, nullptr, "ReleaseLastApplicationCaptureBuffer"}, + {12, nullptr, "AcquireLastForegroundCaptureBuffer"}, + {13, nullptr, "ReleaseLastForegroundCaptureBuffer"}, + {14, nullptr, "AcquireCallerAppletCaptureBuffer"}, + {15, nullptr, "ReleaseCallerAppletCaptureBuffer"}, + {16, nullptr, "AcquireLastApplicationCaptureBufferEx"}, + {17, nullptr, "AcquireLastForegroundCaptureBufferEx"}, + {18, nullptr, "AcquireCallerAppletCaptureBufferEx"}, + {20, D<&IDisplayController::ClearCaptureBuffer>, "ClearCaptureBuffer"}, + {21, nullptr, "ClearAppletTransitionBuffer"}, + {22, D<&IDisplayController::AcquireLastApplicationCaptureSharedBuffer>, "AcquireLastApplicationCaptureSharedBuffer"}, + {23, D<&IDisplayController::ReleaseLastApplicationCaptureSharedBuffer>, "ReleaseLastApplicationCaptureSharedBuffer"}, + {24, D<&IDisplayController::AcquireLastForegroundCaptureSharedBuffer>, "AcquireLastForegroundCaptureSharedBuffer"}, + {25, D<&IDisplayController::ReleaseLastForegroundCaptureSharedBuffer>, "ReleaseLastForegroundCaptureSharedBuffer"}, + {26, D<&IDisplayController::AcquireCallerAppletCaptureSharedBuffer>, "AcquireCallerAppletCaptureSharedBuffer"}, + {27, D<&IDisplayController::ReleaseCallerAppletCaptureSharedBuffer>, "ReleaseCallerAppletCaptureSharedBuffer"}, + {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IDisplayController::~IDisplayController() = default; + +Result IDisplayController::GetCallerAppletCaptureImageEx( + Out<bool> out_was_written, OutBuffer<BufferAttr_HipcMapAlias> out_image_data) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_was_written = true; + R_SUCCEED(); +} + +Result IDisplayController::TakeScreenShotOfOwnLayer(bool unknown0, s32 fbshare_layer_index) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color) { + LOG_WARNING(Service_AM, "(STUBBED) called, unknown0={} fbshare_layer_index={} color={:#x}", + unknown0, fbshare_layer_index, color); + R_SUCCEED(); +} + +Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer( + Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written, + out_fbshare_layer_index)); +} + +Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer( + Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written, + out_fbshare_layer_index)); +} + +Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer( + Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written, + out_fbshare_layer_index)); +} + +Result IDisplayController::ReleaseLastApplicationCaptureSharedBuffer() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/display_controller.h b/src/core/hle/service/am/service/display_controller.h new file mode 100644 index 000000000..406fae21a --- /dev/null +++ b/src/core/hle/service/am/service/display_controller.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class IDisplayController final : public ServiceFramework<IDisplayController> { +public: + explicit IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_); + ~IDisplayController() override; + +private: + Result GetCallerAppletCaptureImageEx(Out<bool> out_was_written, + OutBuffer<BufferAttr_HipcMapAlias> out_image_data); + Result TakeScreenShotOfOwnLayer(bool unknown0, s32 fbshare_layer_index); + Result ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color); + Result AcquireLastForegroundCaptureSharedBuffer(Out<bool> out_was_written, + Out<s32> out_fbshare_layer_index); + Result ReleaseLastForegroundCaptureSharedBuffer(); + Result AcquireCallerAppletCaptureSharedBuffer(Out<bool> out_was_written, + Out<s32> out_fbshare_layer_index); + Result ReleaseCallerAppletCaptureSharedBuffer(); + Result AcquireLastApplicationCaptureSharedBuffer(Out<bool> out_was_written, + Out<s32> out_fbshare_layer_index); + Result ReleaseLastApplicationCaptureSharedBuffer(); + + const std::shared_ptr<Applet> applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/global_state_controller.cpp b/src/core/hle/service/am/service/global_state_controller.cpp new file mode 100644 index 000000000..dba5d3613 --- /dev/null +++ b/src/core/hle/service/am/service/global_state_controller.cpp @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/service/cradle_firmware_updater.h" +#include "core/hle/service/am/service/global_state_controller.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IGlobalStateController::IGlobalStateController(Core::System& system_) + : ServiceFramework{system_, "IGlobalStateController"}, + m_context{system_, "IGlobalStateController"}, m_hdcp_authentication_failed_event{m_context} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestToEnterSleep"}, + {1, nullptr, "EnterSleep"}, + {2, nullptr, "StartSleepSequence"}, + {3, nullptr, "StartShutdownSequence"}, + {4, nullptr, "StartRebootSequence"}, + {9, nullptr, "IsAutoPowerDownRequested"}, + {10, D<&IGlobalStateController::LoadAndApplyIdlePolicySettings>, "LoadAndApplyIdlePolicySettings"}, + {11, nullptr, "NotifyCecSettingsChanged"}, + {12, nullptr, "SetDefaultHomeButtonLongPressTime"}, + {13, nullptr, "UpdateDefaultDisplayResolution"}, + {14, D<&IGlobalStateController::ShouldSleepOnBoot>, "ShouldSleepOnBoot"}, + {15, D<&IGlobalStateController::GetHdcpAuthenticationFailedEvent>, "GetHdcpAuthenticationFailedEvent"}, + {30, D<&IGlobalStateController::OpenCradleFirmwareUpdater>, "OpenCradleFirmwareUpdater"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IGlobalStateController::~IGlobalStateController() = default; + +Result IGlobalStateController::LoadAndApplyIdlePolicySettings() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IGlobalStateController::ShouldSleepOnBoot(Out<bool> out_should_sleep_on_boot) { + LOG_INFO(Service_AM, "called"); + *out_should_sleep_on_boot = false; + R_SUCCEED(); +} + +Result IGlobalStateController::GetHdcpAuthenticationFailedEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_INFO(Service_AM, "called"); + *out_event = m_hdcp_authentication_failed_event.GetHandle(); + R_SUCCEED(); +} + +Result IGlobalStateController::OpenCradleFirmwareUpdater( + Out<SharedPointer<ICradleFirmwareUpdater>> out_cradle_firmware_updater) { + LOG_INFO(Service_AM, "called"); + *out_cradle_firmware_updater = std::make_shared<ICradleFirmwareUpdater>(system); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/global_state_controller.h b/src/core/hle/service/am/service/global_state_controller.h new file mode 100644 index 000000000..67c753513 --- /dev/null +++ b/src/core/hle/service/am/service/global_state_controller.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/os/event.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +class ICradleFirmwareUpdater; + +class IGlobalStateController final : public ServiceFramework<IGlobalStateController> { +public: + explicit IGlobalStateController(Core::System& system_); + ~IGlobalStateController() override; + +private: + Result LoadAndApplyIdlePolicySettings(); + Result ShouldSleepOnBoot(Out<bool> out_should_sleep_on_boot); + Result GetHdcpAuthenticationFailedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result OpenCradleFirmwareUpdater( + Out<SharedPointer<ICradleFirmwareUpdater>> out_cradle_firmware_updater); + + KernelHelpers::ServiceContext m_context; + Event m_hdcp_authentication_failed_event; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/home_menu_functions.cpp b/src/core/hle/service/am/service/home_menu_functions.cpp new file mode 100644 index 000000000..0c4d24b58 --- /dev/null +++ b/src/core/hle/service/am/service/home_menu_functions.cpp @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/result.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/service/home_menu_functions.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Applet> applet) + : ServiceFramework{system_, "IHomeMenuFunctions"}, m_applet{std::move(applet)}, + m_context{system, "IHomeMenuFunctions"}, m_pop_from_general_channel_event{m_context} { + // clang-format off + static const FunctionInfo functions[] = { + {10, D<&IHomeMenuFunctions::RequestToGetForeground>, "RequestToGetForeground"}, + {11, D<&IHomeMenuFunctions::LockForeground>, "LockForeground"}, + {12, D<&IHomeMenuFunctions::UnlockForeground>, "UnlockForeground"}, + {20, nullptr, "PopFromGeneralChannel"}, + {21, D<&IHomeMenuFunctions::GetPopFromGeneralChannelEvent>, "GetPopFromGeneralChannelEvent"}, + {30, nullptr, "GetHomeButtonWriterLockAccessor"}, + {31, nullptr, "GetWriterLockAccessorEx"}, + {40, nullptr, "IsSleepEnabled"}, + {41, D<&IHomeMenuFunctions::IsRebootEnabled>, "IsRebootEnabled"}, + {50, nullptr, "LaunchSystemApplet"}, + {51, nullptr, "LaunchStarter"}, + {100, nullptr, "PopRequestLaunchApplicationForDebug"}, + {110, D<&IHomeMenuFunctions::IsForceTerminateApplicationDisabledForDebug>, "IsForceTerminateApplicationDisabledForDebug"}, + {200, nullptr, "LaunchDevMenu"}, + {1000, nullptr, "SetLastApplicationExitReason"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IHomeMenuFunctions::~IHomeMenuFunctions() = default; + +Result IHomeMenuFunctions::RequestToGetForeground() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IHomeMenuFunctions::LockForeground() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IHomeMenuFunctions::UnlockForeground() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IHomeMenuFunctions::GetPopFromGeneralChannelEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_INFO(Service_AM, "called"); + *out_event = m_pop_from_general_channel_event.GetHandle(); + R_SUCCEED(); +} + +Result IHomeMenuFunctions::IsRebootEnabled(Out<bool> out_is_reboot_enbaled) { + LOG_INFO(Service_AM, "called"); + *out_is_reboot_enbaled = true; + R_SUCCEED(); +} + +Result IHomeMenuFunctions::IsForceTerminateApplicationDisabledForDebug( + Out<bool> out_is_force_terminate_application_disabled_for_debug) { + LOG_INFO(Service_AM, "called"); + *out_is_force_terminate_application_disabled_for_debug = false; + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/home_menu_functions.h b/src/core/hle/service/am/service/home_menu_functions.h new file mode 100644 index 000000000..caf6fbaab --- /dev/null +++ b/src/core/hle/service/am/service/home_menu_functions.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/os/event.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { +public: + explicit IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Applet> applet); + ~IHomeMenuFunctions() override; + +private: + Result RequestToGetForeground(); + Result LockForeground(); + Result UnlockForeground(); + Result GetPopFromGeneralChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result IsRebootEnabled(Out<bool> out_is_reboot_enbaled); + Result IsForceTerminateApplicationDisabledForDebug( + Out<bool> out_is_force_terminate_application_disabled_for_debug); + + const std::shared_ptr<Applet> m_applet; + KernelHelpers::ServiceContext m_context; + Event m_pop_from_general_channel_event; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/library_applet_accessor.cpp b/src/core/hle/service/am/service/library_applet_accessor.cpp new file mode 100644 index 000000000..0c2426d4b --- /dev/null +++ b/src/core/hle/service/am/service/library_applet_accessor.cpp @@ -0,0 +1,157 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/service/library_applet_accessor.h" +#include "core/hle/service/am/service/storage.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_, + std::shared_ptr<AppletDataBroker> broker, + std::shared_ptr<Applet> applet) + : ServiceFramework{system_, "ILibraryAppletAccessor"}, m_broker{std::move(broker)}, + m_applet{std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&ILibraryAppletAccessor::GetAppletStateChangedEvent>, "GetAppletStateChangedEvent"}, + {1, D<&ILibraryAppletAccessor::IsCompleted>, "IsCompleted"}, + {10, D<&ILibraryAppletAccessor::Start>, "Start"}, + {20, D<&ILibraryAppletAccessor::RequestExit>, "RequestExit"}, + {25, D<&ILibraryAppletAccessor::Terminate>, "Terminate"}, + {30, D<&ILibraryAppletAccessor::GetResult>, "GetResult"}, + {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, + {60, D<&ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero>, "PresetLibraryAppletGpuTimeSliceZero"}, + {100, D<&ILibraryAppletAccessor::PushInData>, "PushInData"}, + {101, D<&ILibraryAppletAccessor::PopOutData>, "PopOutData"}, + {102, nullptr, "PushExtraStorage"}, + {103, D<&ILibraryAppletAccessor::PushInteractiveInData>, "PushInteractiveInData"}, + {104, D<&ILibraryAppletAccessor::PopInteractiveOutData>, "PopInteractiveOutData"}, + {105, D<&ILibraryAppletAccessor::GetPopOutDataEvent>, "GetPopOutDataEvent"}, + {106, D<&ILibraryAppletAccessor::GetPopInteractiveOutDataEvent>, "GetPopInteractiveOutDataEvent"}, + {110, nullptr, "NeedsToExitProcess"}, + {120, nullptr, "GetLibraryAppletInfo"}, + {150, nullptr, "RequestForAppletToGetForeground"}, + {160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ILibraryAppletAccessor::~ILibraryAppletAccessor() = default; + +Result ILibraryAppletAccessor::GetAppletStateChangedEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = m_broker->GetStateChangedEvent().GetHandle(); + R_SUCCEED(); +} + +Result ILibraryAppletAccessor::IsCompleted(Out<bool> out_is_completed) { + LOG_DEBUG(Service_AM, "called"); + *out_is_completed = m_broker->IsCompleted(); + R_SUCCEED(); +} + +Result ILibraryAppletAccessor::GetResult(Out<Result> out_result) { + LOG_DEBUG(Service_AM, "called"); + *out_result = m_applet->terminate_result; + R_SUCCEED(); +} + +Result ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero() { + LOG_INFO(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result ILibraryAppletAccessor::Start() { + LOG_DEBUG(Service_AM, "called"); + m_applet->process->Run(); + FrontendExecute(); + R_SUCCEED(); +} + +Result ILibraryAppletAccessor::RequestExit() { + LOG_DEBUG(Service_AM, "called"); + m_applet->message_queue.RequestExit(); + FrontendRequestExit(); + R_SUCCEED(); +} + +Result ILibraryAppletAccessor::Terminate() { + LOG_DEBUG(Service_AM, "called"); + m_applet->process->Terminate(); + FrontendRequestExit(); + R_SUCCEED(); +} + +Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) { + LOG_DEBUG(Service_AM, "called"); + m_broker->GetInData().Push(storage); + R_SUCCEED(); +} + +Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) { + LOG_DEBUG(Service_AM, "called"); + R_RETURN(m_broker->GetOutData().Pop(out_storage.Get())); +} + +Result ILibraryAppletAccessor::PushInteractiveInData(SharedPointer<IStorage> storage) { + LOG_DEBUG(Service_AM, "called"); + m_broker->GetInteractiveInData().Push(storage); + FrontendExecuteInteractive(); + R_SUCCEED(); +} + +Result ILibraryAppletAccessor::PopInteractiveOutData(Out<SharedPointer<IStorage>> out_storage) { + LOG_DEBUG(Service_AM, "called"); + R_RETURN(m_broker->GetInteractiveOutData().Pop(out_storage.Get())); +} + +Result ILibraryAppletAccessor::GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = m_broker->GetOutData().GetEvent(); + R_SUCCEED(); +} + +Result ILibraryAppletAccessor::GetPopInteractiveOutDataEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_DEBUG(Service_AM, "called"); + *out_event = m_broker->GetInteractiveOutData().GetEvent(); + R_SUCCEED(); +} + +Result ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(Out<u64> out_handle) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is + // actually used anywhere + *out_handle = 0xdeadbeef; + R_SUCCEED(); +} + +void ILibraryAppletAccessor::FrontendExecute() { + if (m_applet->frontend) { + m_applet->frontend->Initialize(); + m_applet->frontend->Execute(); + } +} + +void ILibraryAppletAccessor::FrontendExecuteInteractive() { + if (m_applet->frontend) { + m_applet->frontend->ExecuteInteractive(); + m_applet->frontend->Execute(); + } +} + +void ILibraryAppletAccessor::FrontendRequestExit() { + if (m_applet->frontend) { + m_applet->frontend->RequestExit(); + } +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/library_applet_accessor.h b/src/core/hle/service/am/service/library_applet_accessor.h new file mode 100644 index 000000000..97d3b6c8a --- /dev/null +++ b/src/core/hle/service/am/service/library_applet_accessor.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +class AppletDataBroker; +struct Applet; +class IStorage; + +class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { +public: + explicit ILibraryAppletAccessor(Core::System& system_, std::shared_ptr<AppletDataBroker> broker, + std::shared_ptr<Applet> applet); + ~ILibraryAppletAccessor(); + +private: + Result GetAppletStateChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result IsCompleted(Out<bool> out_is_completed); + Result GetResult(Out<Result> out_result); + Result PresetLibraryAppletGpuTimeSliceZero(); + Result Start(); + Result RequestExit(); + Result Terminate(); + Result PushInData(SharedPointer<IStorage> storage); + Result PopOutData(Out<SharedPointer<IStorage>> out_storage); + Result PushInteractiveInData(SharedPointer<IStorage> storage); + Result PopInteractiveOutData(Out<SharedPointer<IStorage>> out_storage); + Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result GetIndirectLayerConsumerHandle(Out<u64> out_handle); + + void FrontendExecute(); + void FrontendExecuteInteractive(); + void FrontendRequestExit(); + + const std::shared_ptr<AppletDataBroker> m_broker; + const std::shared_ptr<Applet> m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/library_applet_creator.cpp b/src/core/hle/service/am/service/library_applet_creator.cpp new file mode 100644 index 000000000..c97358d81 --- /dev/null +++ b/src/core/hle/service/am/service/library_applet_creator.cpp @@ -0,0 +1,268 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/library_applet_storage.h" +#include "core/hle/service/am/service/library_applet_accessor.h" +#include "core/hle/service/am/service/library_applet_creator.h" +#include "core/hle/service/am/service/storage.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::AM { + +namespace { + +bool ShouldCreateGuestApplet(AppletId applet_id) { +#define X(Name, name) \ + if (applet_id == AppletId::Name && \ + Settings::values.name##_applet_mode.GetValue() != Settings::AppletMode::LLE) { \ + return false; \ + } + + X(Cabinet, cabinet) + X(Controller, controller) + X(DataErase, data_erase) + X(Error, error) + X(NetConnect, net_connect) + X(ProfileSelect, player_select) + X(SoftwareKeyboard, swkbd) + X(MiiEdit, mii_edit) + X(Web, web) + X(Shop, shop) + X(PhotoViewer, photo_viewer) + X(OfflineWeb, offline_web) + X(LoginShare, login_share) + X(WebAuth, wifi_web_auth) + X(MyPage, my_page) + +#undef X + + return true; +} + +AppletProgramId AppletIdToProgramId(AppletId applet_id) { + switch (applet_id) { + case AppletId::OverlayDisplay: + return AppletProgramId::OverlayDisplay; + case AppletId::QLaunch: + return AppletProgramId::QLaunch; + case AppletId::Starter: + return AppletProgramId::Starter; + case AppletId::Auth: + return AppletProgramId::Auth; + case AppletId::Cabinet: + return AppletProgramId::Cabinet; + case AppletId::Controller: + return AppletProgramId::Controller; + case AppletId::DataErase: + return AppletProgramId::DataErase; + case AppletId::Error: + return AppletProgramId::Error; + case AppletId::NetConnect: + return AppletProgramId::NetConnect; + case AppletId::ProfileSelect: + return AppletProgramId::ProfileSelect; + case AppletId::SoftwareKeyboard: + return AppletProgramId::SoftwareKeyboard; + case AppletId::MiiEdit: + return AppletProgramId::MiiEdit; + case AppletId::Web: + return AppletProgramId::Web; + case AppletId::Shop: + return AppletProgramId::Shop; + case AppletId::PhotoViewer: + return AppletProgramId::PhotoViewer; + case AppletId::Settings: + return AppletProgramId::Settings; + case AppletId::OfflineWeb: + return AppletProgramId::OfflineWeb; + case AppletId::LoginShare: + return AppletProgramId::LoginShare; + case AppletId::WebAuth: + return AppletProgramId::WebAuth; + case AppletId::MyPage: + return AppletProgramId::MyPage; + default: + return static_cast<AppletProgramId>(0); + } +} + +std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system, + std::shared_ptr<Applet> caller_applet, + AppletId applet_id, + LibraryAppletMode mode) { + const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); + if (program_id == 0) { + // Unknown applet + return {}; + } + + // TODO: enable other versions of applets + enum : u8 { + Firmware1400 = 14, + Firmware1500 = 15, + Firmware1600 = 16, + Firmware1700 = 17, + }; + + auto process = std::make_unique<Process>(system); + if (!process->Initialize(program_id, Firmware1400, Firmware1700)) { + // Couldn't initialize the guest process + return {}; + } + + const auto applet = std::make_shared<Applet>(system, std::move(process)); + applet->program_id = program_id; + applet->applet_id = applet_id; + applet->type = AppletType::LibraryApplet; + applet->library_applet_mode = mode; + + // Set focus state + switch (mode) { + case LibraryAppletMode::AllForeground: + case LibraryAppletMode::NoUi: + case LibraryAppletMode::PartialForeground: + case LibraryAppletMode::PartialForegroundIndirectDisplay: + applet->hid_registration.EnableAppletToGetInput(true); + applet->focus_state = FocusState::InFocus; + applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground); + break; + case LibraryAppletMode::AllForegroundInitiallyHidden: + applet->hid_registration.EnableAppletToGetInput(false); + applet->focus_state = FocusState::NotInFocus; + applet->display_layer_manager.SetWindowVisibility(false); + applet->message_queue.PushMessage(AppletMessage::ChangeIntoBackground); + break; + } + + auto broker = std::make_shared<AppletDataBroker>(system); + applet->caller_applet = caller_applet; + applet->caller_applet_broker = broker; + + system.GetAppletManager().InsertApplet(applet); + + return std::make_shared<ILibraryAppletAccessor>(system, broker, applet); +} + +std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system, + std::shared_ptr<Applet> caller_applet, + AppletId applet_id, + LibraryAppletMode mode) { + const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); + + auto process = std::make_unique<Process>(system); + auto applet = std::make_shared<Applet>(system, std::move(process)); + applet->program_id = program_id; + applet->applet_id = applet_id; + applet->type = AppletType::LibraryApplet; + applet->library_applet_mode = mode; + + auto storage = std::make_shared<AppletDataBroker>(system); + applet->caller_applet = caller_applet; + applet->caller_applet_broker = storage; + applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode); + + return std::make_shared<ILibraryAppletAccessor>(system, storage, applet); +} + +} // namespace + +ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet) + : ServiceFramework{system_, "ILibraryAppletCreator"}, m_applet{std::move(applet)} { + static const FunctionInfo functions[] = { + {0, D<&ILibraryAppletCreator::CreateLibraryApplet>, "CreateLibraryApplet"}, + {1, nullptr, "TerminateAllLibraryApplets"}, + {2, nullptr, "AreAnyLibraryAppletsLeft"}, + {10, D<&ILibraryAppletCreator::CreateStorage>, "CreateStorage"}, + {11, D<&ILibraryAppletCreator::CreateTransferMemoryStorage>, "CreateTransferMemoryStorage"}, + {12, D<&ILibraryAppletCreator::CreateHandleStorage>, "CreateHandleStorage"}, + }; + RegisterHandlers(functions); +} + +ILibraryAppletCreator::~ILibraryAppletCreator() = default; + +Result ILibraryAppletCreator::CreateLibraryApplet( + Out<SharedPointer<ILibraryAppletAccessor>> out_library_applet_accessor, AppletId applet_id, + LibraryAppletMode library_applet_mode) { + LOG_DEBUG(Service_AM, "called with applet_id={} applet_mode={}", applet_id, + library_applet_mode); + + std::shared_ptr<ILibraryAppletAccessor> library_applet; + if (ShouldCreateGuestApplet(applet_id)) { + library_applet = CreateGuestApplet(system, m_applet, applet_id, library_applet_mode); + } + if (!library_applet) { + library_applet = CreateFrontendApplet(system, m_applet, applet_id, library_applet_mode); + } + if (!library_applet) { + LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); + R_THROW(ResultUnknown); + } + + // Applet is created, can now be launched. + m_applet->library_applet_launchable_event.Signal(); + *out_library_applet_accessor = library_applet; + R_SUCCEED(); +} + +Result ILibraryAppletCreator::CreateStorage(Out<SharedPointer<IStorage>> out_storage, s64 size) { + LOG_DEBUG(Service_AM, "called, size={}", size); + + if (size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + R_THROW(ResultUnknown); + } + + *out_storage = std::make_shared<IStorage>(system, AM::CreateStorage(std::vector<u8>(size))); + R_SUCCEED(); +} + +Result ILibraryAppletCreator::CreateTransferMemoryStorage( + Out<SharedPointer<IStorage>> out_storage, bool is_writable, s64 size, + InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle) { + LOG_DEBUG(Service_AM, "called, is_writable={} size={}", is_writable, size); + + if (size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + R_THROW(ResultUnknown); + } + + if (!transfer_memory_handle) { + LOG_ERROR(Service_AM, "transfer_memory_handle is null"); + R_THROW(ResultUnknown); + } + + *out_storage = std::make_shared<IStorage>( + system, AM::CreateTransferMemoryStorage(transfer_memory_handle->GetOwner()->GetMemory(), + transfer_memory_handle.Get(), is_writable, size)); + R_SUCCEED(); +} + +Result ILibraryAppletCreator::CreateHandleStorage( + Out<SharedPointer<IStorage>> out_storage, s64 size, + InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle) { + LOG_DEBUG(Service_AM, "called, size={}", size); + + if (size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + R_THROW(ResultUnknown); + } + + if (!transfer_memory_handle) { + LOG_ERROR(Service_AM, "transfer_memory_handle is null"); + R_THROW(ResultUnknown); + } + + *out_storage = std::make_shared<IStorage>( + system, AM::CreateHandleStorage(transfer_memory_handle->GetOwner()->GetMemory(), + transfer_memory_handle.Get(), size)); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/library_applet_creator.h b/src/core/hle/service/am/service/library_applet_creator.h new file mode 100644 index 000000000..fe6d40eb3 --- /dev/null +++ b/src/core/hle/service/am/service/library_applet_creator.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; +class ILibraryAppletAccessor; +class IStorage; + +class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { +public: + explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet); + ~ILibraryAppletCreator() override; + +private: + Result CreateLibraryApplet( + Out<SharedPointer<ILibraryAppletAccessor>> out_library_applet_accessor, AppletId applet_id, + LibraryAppletMode library_applet_mode); + Result CreateStorage(Out<SharedPointer<IStorage>> out_storage, s64 size); + Result CreateTransferMemoryStorage( + Out<SharedPointer<IStorage>> out_storage, bool is_writable, s64 size, + InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle); + Result CreateHandleStorage(Out<SharedPointer<IStorage>> out_storage, s64 size, + InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle); + + const std::shared_ptr<Applet> m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/library_applet_proxy.cpp b/src/core/hle/service/am/service/library_applet_proxy.cpp new file mode 100644 index 000000000..58e709347 --- /dev/null +++ b/src/core/hle/service/am/service/library_applet_proxy.cpp @@ -0,0 +1,132 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/service/applet_common_functions.h" +#include "core/hle/service/am/service/audio_controller.h" +#include "core/hle/service/am/service/common_state_getter.h" +#include "core/hle/service/am/service/debug_functions.h" +#include "core/hle/service/am/service/display_controller.h" +#include "core/hle/service/am/service/global_state_controller.h" +#include "core/hle/service/am/service/home_menu_functions.h" +#include "core/hle/service/am/service/library_applet_creator.h" +#include "core/hle/service/am/service/library_applet_proxy.h" +#include "core/hle/service/am/service/library_applet_self_accessor.h" +#include "core/hle/service/am/service/process_winding_controller.h" +#include "core/hle/service/am/service/self_controller.h" +#include "core/hle/service/am/service/window_controller.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +ILibraryAppletProxy::ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet, + Kernel::KProcess* process) + : ServiceFramework{system_, "ILibraryAppletProxy"}, m_process{process}, m_applet{ + std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&ILibraryAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, + {1, D<&ILibraryAppletProxy::GetSelfController>, "GetSelfController"}, + {2, D<&ILibraryAppletProxy::GetWindowController>, "GetWindowController"}, + {3, D<&ILibraryAppletProxy::GetAudioController>, "GetAudioController"}, + {4, D<&ILibraryAppletProxy::GetDisplayController>, "GetDisplayController"}, + {10, D<&ILibraryAppletProxy::GetProcessWindingController>, "GetProcessWindingController"}, + {11, D<&ILibraryAppletProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"}, + {20, D<&ILibraryAppletProxy::OpenLibraryAppletSelfAccessor>, "OpenLibraryAppletSelfAccessor"}, + {21, D<&ILibraryAppletProxy::GetAppletCommonFunctions>, "GetAppletCommonFunctions"}, + {22, D<&ILibraryAppletProxy::GetHomeMenuFunctions>, "GetHomeMenuFunctions"}, + {23, D<&ILibraryAppletProxy::GetGlobalStateController>, "GetGlobalStateController"}, + {1000, D<&ILibraryAppletProxy::GetDebugFunctions>, "GetDebugFunctions"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ILibraryAppletProxy::~ILibraryAppletProxy() = default; + +Result ILibraryAppletProxy::GetAudioController( + Out<SharedPointer<IAudioController>> out_audio_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_audio_controller = std::make_shared<IAudioController>(system); + R_SUCCEED(); +} + +Result ILibraryAppletProxy::GetDisplayController( + Out<SharedPointer<IDisplayController>> out_display_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_display_controller = std::make_shared<IDisplayController>(system, m_applet); + R_SUCCEED(); +} + +Result ILibraryAppletProxy::GetProcessWindingController( + Out<SharedPointer<IProcessWindingController>> out_process_winding_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_process_winding_controller = std::make_shared<IProcessWindingController>(system, m_applet); + R_SUCCEED(); +} + +Result ILibraryAppletProxy::GetDebugFunctions( + Out<SharedPointer<IDebugFunctions>> out_debug_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_debug_functions = std::make_shared<IDebugFunctions>(system); + R_SUCCEED(); +} + +Result ILibraryAppletProxy::GetWindowController( + Out<SharedPointer<IWindowController>> out_window_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_window_controller = std::make_shared<IWindowController>(system, m_applet); + R_SUCCEED(); +} + +Result ILibraryAppletProxy::GetSelfController( + Out<SharedPointer<ISelfController>> out_self_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process); + R_SUCCEED(); +} + +Result ILibraryAppletProxy::GetCommonStateGetter( + Out<SharedPointer<ICommonStateGetter>> out_common_state_getter) { + LOG_DEBUG(Service_AM, "called"); + *out_common_state_getter = std::make_shared<ICommonStateGetter>(system, m_applet); + R_SUCCEED(); +} + +Result ILibraryAppletProxy::GetLibraryAppletCreator( + Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator) { + LOG_DEBUG(Service_AM, "called"); + *out_library_applet_creator = std::make_shared<ILibraryAppletCreator>(system, m_applet); + R_SUCCEED(); +} + +Result ILibraryAppletProxy::OpenLibraryAppletSelfAccessor( + Out<SharedPointer<ILibraryAppletSelfAccessor>> out_library_applet_self_accessor) { + LOG_DEBUG(Service_AM, "called"); + *out_library_applet_self_accessor = + std::make_shared<ILibraryAppletSelfAccessor>(system, m_applet); + R_SUCCEED(); +} + +Result ILibraryAppletProxy::GetAppletCommonFunctions( + Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_applet_common_functions = std::make_shared<IAppletCommonFunctions>(system, m_applet); + R_SUCCEED(); +} + +Result ILibraryAppletProxy::GetHomeMenuFunctions( + Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_home_menu_functions = std::make_shared<IHomeMenuFunctions>(system, m_applet); + R_SUCCEED(); +} + +Result ILibraryAppletProxy::GetGlobalStateController( + Out<SharedPointer<IGlobalStateController>> out_global_state_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_global_state_controller = std::make_shared<IGlobalStateController>(system); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/library_applet_proxy.h b/src/core/hle/service/am/service/library_applet_proxy.h new file mode 100644 index 000000000..7d0714b85 --- /dev/null +++ b/src/core/hle/service/am/service/library_applet_proxy.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; +class IAppletCommonFunctions; +class IAudioController; +class ICommonStateGetter; +class IDebugFunctions; +class IDisplayController; +class IHomeMenuFunctions; +class IGlobalStateController; +class ILibraryAppletCreator; +class ILibraryAppletSelfAccessor; +class IProcessWindingController; +class ISelfController; +class IWindowController; + +class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { +public: + explicit ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet, + Kernel::KProcess* process); + ~ILibraryAppletProxy(); + +private: + Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller); + Result GetDisplayController(Out<SharedPointer<IDisplayController>> out_display_controller); + Result GetProcessWindingController( + Out<SharedPointer<IProcessWindingController>> out_process_winding_controller); + Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions); + Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller); + Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller); + Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter); + Result GetLibraryAppletCreator( + Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator); + Result OpenLibraryAppletSelfAccessor( + Out<SharedPointer<ILibraryAppletSelfAccessor>> out_library_applet_self_accessor); + Result GetAppletCommonFunctions( + Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions); + Result GetHomeMenuFunctions(Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions); + Result GetGlobalStateController( + Out<SharedPointer<IGlobalStateController>> out_global_state_controller); + + Kernel::KProcess* const m_process; + const std::shared_ptr<Applet> m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.cpp b/src/core/hle/service/am/service/library_applet_self_accessor.cpp new file mode 100644 index 000000000..330eb26f0 --- /dev/null +++ b/src/core/hle/service/am/service/library_applet_self_accessor.cpp @@ -0,0 +1,325 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core_timing.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/service/library_applet_self_accessor.h" +#include "core/hle/service/am/service/storage.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/glue/glue_manager.h" +#include "core/hle/service/ns/application_manager_interface.h" +#include "core/hle/service/ns/service_getter_interface.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::AM { + +namespace { + +AppletIdentityInfo GetCallerIdentity(Applet& applet) { + if (const auto caller_applet = applet.caller_applet.lock(); caller_applet) { + // TODO: is this actually the application ID? + return { + .applet_id = caller_applet->applet_id, + .application_id = caller_applet->program_id, + }; + } else { + return { + .applet_id = AppletId::QLaunch, + .application_id = 0x0100000000001000ull, + }; + } +} + +} // namespace + +ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_, + std::shared_ptr<Applet> applet) + : ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, m_applet{std::move(applet)}, + m_broker{m_applet->caller_applet_broker} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&ILibraryAppletSelfAccessor::PopInData>, "PopInData"}, + {1, D<&ILibraryAppletSelfAccessor::PushOutData>, "PushOutData"}, + {2, D<&ILibraryAppletSelfAccessor::PopInteractiveInData>, "PopInteractiveInData"}, + {3, D<&ILibraryAppletSelfAccessor::PushInteractiveOutData>, "PushInteractiveOutData"}, + {5, D<&ILibraryAppletSelfAccessor::GetPopInDataEvent>, "GetPopInDataEvent"}, + {6, D<&ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent>, "GetPopInteractiveInDataEvent"}, + {10, D<&ILibraryAppletSelfAccessor::ExitProcessAndReturn>, "ExitProcessAndReturn"}, + {11, D<&ILibraryAppletSelfAccessor::GetLibraryAppletInfo>, "GetLibraryAppletInfo"}, + {12, D<&ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo>, "GetMainAppletIdentityInfo"}, + {13, D<&ILibraryAppletSelfAccessor::CanUseApplicationCore>, "CanUseApplicationCore"}, + {14, D<&ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo>, "GetCallerAppletIdentityInfo"}, + {15, D<&ILibraryAppletSelfAccessor::GetMainAppletApplicationControlProperty>, "GetMainAppletApplicationControlProperty"}, + {16, D<&ILibraryAppletSelfAccessor::GetMainAppletStorageId>, "GetMainAppletStorageId"}, + {17, D<&ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfoStack>, "GetCallerAppletIdentityInfoStack"}, + {18, nullptr, "GetNextReturnDestinationAppletIdentityInfo"}, + {19, D<&ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout>, "GetDesirableKeyboardLayout"}, + {20, nullptr, "PopExtraStorage"}, + {25, nullptr, "GetPopExtraStorageEvent"}, + {30, nullptr, "UnpopInData"}, + {31, nullptr, "UnpopExtraStorage"}, + {40, nullptr, "GetIndirectLayerProducerHandle"}, + {50, D<&ILibraryAppletSelfAccessor::ReportVisibleError>, "ReportVisibleError"}, + {51, D<&ILibraryAppletSelfAccessor::ReportVisibleErrorWithErrorContext>, "ReportVisibleErrorWithErrorContext"}, + {60, D<&ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage>, "GetMainAppletApplicationDesiredLanguage"}, + {70, D<&ILibraryAppletSelfAccessor::GetCurrentApplicationId>, "GetCurrentApplicationId"}, + {80, nullptr, "RequestExitToSelf"}, + {90, nullptr, "CreateApplicationAndPushAndRequestToLaunch"}, + {100, nullptr, "CreateGameMovieTrimmer"}, + {101, nullptr, "ReserveResourceForMovieOperation"}, + {102, nullptr, "UnreserveResourceForMovieOperation"}, + {110, D<&ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers>, "GetMainAppletAvailableUsers"}, + {120, nullptr, "GetLaunchStorageInfoForDebug"}, + {130, nullptr, "GetGpuErrorDetectedSystemEvent"}, + {140, nullptr, "SetApplicationMemoryReservation"}, + {150, D<&ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually>, "ShouldSetGpuTimeSliceManually"}, + {160, D<&ILibraryAppletSelfAccessor::Cmd160>, "Cmd160"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default; + +Result ILibraryAppletSelfAccessor::PopInData(Out<SharedPointer<IStorage>> out_storage) { + LOG_INFO(Service_AM, "called"); + R_RETURN(m_broker->GetInData().Pop(out_storage)); +} + +Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer<IStorage> storage) { + LOG_INFO(Service_AM, "called"); + m_broker->GetOutData().Push(storage); + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::PopInteractiveInData(Out<SharedPointer<IStorage>> out_storage) { + LOG_INFO(Service_AM, "called"); + R_RETURN(m_broker->GetInteractiveInData().Pop(out_storage)); +} + +Result ILibraryAppletSelfAccessor::PushInteractiveOutData(SharedPointer<IStorage> storage) { + LOG_INFO(Service_AM, "called"); + m_broker->GetInteractiveOutData().Push(storage); + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::GetPopInDataEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_INFO(Service_AM, "called"); + *out_event = m_broker->GetInData().GetEvent(); + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_INFO(Service_AM, "called"); + *out_event = m_broker->GetInteractiveInData().GetEvent(); + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::GetLibraryAppletInfo( + Out<LibraryAppletInfo> out_library_applet_info) { + LOG_INFO(Service_AM, "called"); + *out_library_applet_info = { + .applet_id = m_applet->applet_id, + .library_applet_mode = m_applet->library_applet_mode, + }; + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo( + Out<AppletIdentityInfo> out_identity_info) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_identity_info = { + .applet_id = AppletId::QLaunch, + .application_id = 0x0100000000001000ull, + }; + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::CanUseApplicationCore(Out<bool> out_can_use_application_core) { + // TODO: This appears to read the NPDM from state and check the core mask of the applet. + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_can_use_application_core = false; + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::GetMainAppletApplicationControlProperty( + OutLargeData<std::array<u8, 0x4000>, BufferAttr_HipcMapAlias> out_nacp) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + // TODO: this should be the main applet, not the caller applet + const auto application = GetCallerIdentity(*m_applet); + std::vector<u8> nacp; + const auto result = + system.GetARPManager().GetControlProperty(&nacp, application.application_id); + + if (R_SUCCEEDED(result)) { + std::memcpy(out_nacp->data(), nacp.data(), std::min(nacp.size(), out_nacp->size())); + } + + R_RETURN(result); +} + +Result ILibraryAppletSelfAccessor::GetMainAppletStorageId(Out<FileSys::StorageId> out_storage_id) { + LOG_INFO(Service_AM, "(STUBBED) called"); + *out_storage_id = FileSys::StorageId::NandUser; + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::ExitProcessAndReturn() { + LOG_INFO(Service_AM, "called"); + system.GetAppletManager().TerminateAndRemoveApplet(m_applet->aruid); + m_broker->SignalCompletion(); + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo( + Out<AppletIdentityInfo> out_identity_info) { + LOG_INFO(Service_AM, "called"); + *out_identity_info = GetCallerIdentity(*m_applet); + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfoStack( + Out<s32> out_count, OutArray<AppletIdentityInfo, BufferAttr_HipcMapAlias> out_identity_info) { + LOG_INFO(Service_AM, "called"); + + std::shared_ptr<Applet> applet = m_applet; + *out_count = 0; + + do { + if (*out_count >= static_cast<s32>(out_identity_info.size())) { + break; + } + out_identity_info[(*out_count)++] = GetCallerIdentity(*applet); + } while ((applet = applet->caller_applet.lock())); + + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(Out<u32> out_desirable_layout) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_desirable_layout = 0; + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::ReportVisibleError(ErrorCode error_code) { + LOG_WARNING(Service_AM, "(STUBBED) called, error {}-{}", error_code.category, + error_code.number); + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::ReportVisibleErrorWithErrorContext( + ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context) { + LOG_WARNING(Service_AM, "(STUBBED) called, error {}-{}", error_code.category, + error_code.number); + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage( + Out<u64> out_desired_language) { + // FIXME: this is copied from IApplicationFunctions::GetDesiredLanguage + // FIXME: all of this stuff belongs to ns + auto identity = GetCallerIdentity(*m_applet); + + // TODO(bunnei): This should be configurable + LOG_DEBUG(Service_AM, "called"); + + // Get supported languages from NACP, if possible + // Default to 0 (all languages supported) + u32 supported_languages = 0; + + const auto res = [this, identity] { + const FileSys::PatchManager pm{identity.application_id, system.GetFileSystemController(), + system.GetContentProvider()}; + auto metadata = pm.GetControlMetadata(); + if (metadata.first != nullptr) { + return metadata; + } + + const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(identity.application_id), + system.GetFileSystemController(), + system.GetContentProvider()}; + return pm_update.GetControlMetadata(); + }(); + + if (res.first != nullptr) { + supported_languages = res.first->GetSupportedLanguages(); + } + + // Call IApplicationManagerInterface implementation. + auto& service_manager = system.ServiceManager(); + auto ns_am2 = service_manager.GetService<NS::IServiceGetterInterface>("ns:am2"); + + std::shared_ptr<NS::IApplicationManagerInterface> app_man; + R_TRY(ns_am2->GetApplicationManagerInterface(&app_man)); + + // Get desired application language + NS::ApplicationLanguage desired_language{}; + R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages)); + + // Convert to settings language code. + u64 language_code{}; + R_TRY(app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language)); + + LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code); + + *out_desired_language = language_code; + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::GetCurrentApplicationId(Out<u64> out_application_id) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + // TODO: this should be the main applet, not the caller applet + const auto main_applet = GetCallerIdentity(*m_applet); + *out_application_id = main_applet.application_id; + + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers( + Out<bool> out_can_select_any_user, Out<s32> out_users_count, + OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users) { + const Service::Account::ProfileManager manager{}; + + *out_can_select_any_user = false; + *out_users_count = -1; + + LOG_INFO(Service_AM, "called"); + + if (manager.GetUserCount() > 0) { + *out_can_select_any_user = true; + *out_users_count = static_cast<s32>(manager.GetUserCount()); + + const auto users = manager.GetAllUsers(); + for (size_t i = 0; i < users.size() && i < out_users.size(); i++) { + out_users[i] = users[i]; + } + } + + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually( + Out<bool> out_should_set_gpu_time_slice_manually) { + LOG_INFO(Service_AM, "(STUBBED) called"); + *out_should_set_gpu_time_slice_manually = false; + R_SUCCEED(); +} + +Result ILibraryAppletSelfAccessor::Cmd160(Out<u64> out_unknown0) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_unknown0 = 0; + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.h b/src/core/hle/service/am/service/library_applet_self_accessor.h new file mode 100644 index 000000000..3e60393c2 --- /dev/null +++ b/src/core/hle/service/am/service/library_applet_self_accessor.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/uuid.h" +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace FileSys { +enum class StorageId : u8; +} + +namespace Kernel { +class KReadableEvent; +} + +namespace Service::AM { + +class AppletDataBroker; +struct Applet; +class IStorage; + +struct LibraryAppletInfo { + AppletId applet_id; + LibraryAppletMode library_applet_mode; +}; +static_assert(sizeof(LibraryAppletInfo) == 0x8, "LibraryAppletInfo has incorrect size."); + +struct ErrorCode { + u32 category; + u32 number; +}; +static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size."); + +struct ErrorContext { + u8 type; + INSERT_PADDING_BYTES_NOINIT(0x7); + std::array<u8, 0x1f4> data; + Result result; +}; +static_assert(sizeof(ErrorContext) == 0x200, "ErrorContext has incorrect size."); + +class ILibraryAppletSelfAccessor final : public ServiceFramework<ILibraryAppletSelfAccessor> { +public: + explicit ILibraryAppletSelfAccessor(Core::System& system_, std::shared_ptr<Applet> applet); + ~ILibraryAppletSelfAccessor() override; + +private: + Result PopInData(Out<SharedPointer<IStorage>> out_storage); + Result PushOutData(SharedPointer<IStorage> storage); + Result PopInteractiveInData(Out<SharedPointer<IStorage>> out_storage); + Result PushInteractiveOutData(SharedPointer<IStorage> storage); + Result GetPopInDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result GetPopInteractiveInDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result GetLibraryAppletInfo(Out<LibraryAppletInfo> out_library_applet_info); + Result GetMainAppletIdentityInfo(Out<AppletIdentityInfo> out_identity_info); + Result CanUseApplicationCore(Out<bool> out_can_use_application_core); + Result GetMainAppletApplicationControlProperty( + OutLargeData<std::array<u8, 0x4000>, BufferAttr_HipcMapAlias> out_nacp); + Result GetMainAppletStorageId(Out<FileSys::StorageId> out_storage_id); + Result ExitProcessAndReturn(); + Result GetCallerAppletIdentityInfo(Out<AppletIdentityInfo> out_identity_info); + Result GetCallerAppletIdentityInfoStack( + Out<s32> out_count, + OutArray<AppletIdentityInfo, BufferAttr_HipcMapAlias> out_identity_info); + Result GetDesirableKeyboardLayout(Out<u32> out_desirable_layout); + Result ReportVisibleError(ErrorCode error_code); + Result ReportVisibleErrorWithErrorContext( + ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context); + Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language); + Result GetCurrentApplicationId(Out<u64> out_application_id); + Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count, + OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users); + Result ShouldSetGpuTimeSliceManually(Out<bool> out_should_set_gpu_time_slice_manually); + Result Cmd160(Out<u64> out_unknown0); + + const std::shared_ptr<Applet> m_applet; + const std::shared_ptr<AppletDataBroker> m_broker; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/lock_accessor.cpp b/src/core/hle/service/am/service/lock_accessor.cpp new file mode 100644 index 000000000..8e556fdd6 --- /dev/null +++ b/src/core/hle/service/am/service/lock_accessor.cpp @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/service/lock_accessor.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +ILockAccessor::ILockAccessor(Core::System& system_) + : ServiceFramework{system_, "ILockAccessor"}, m_context{system_, "ILockAccessor"}, + m_event{m_context} { + // clang-format off + static const FunctionInfo functions[] = { + {1, D<&ILockAccessor::TryLock>, "TryLock"}, + {2, D<&ILockAccessor::Unlock>, "Unlock"}, + {3, D<&ILockAccessor::GetEvent>, "GetEvent"}, + {4, D<&ILockAccessor::IsLocked>, "IsLocked"}, + }; + // clang-format on + + RegisterHandlers(functions); + + m_event.Signal(); +} + +ILockAccessor::~ILockAccessor() = default; + +Result ILockAccessor::TryLock(Out<bool> out_is_locked, + OutCopyHandle<Kernel::KReadableEvent> out_handle, + bool return_handle) { + LOG_INFO(Service_AM, "called, return_handle={}", return_handle); + + { + std::scoped_lock lk{m_mutex}; + if (m_is_locked) { + *out_is_locked = false; + } else { + m_is_locked = true; + *out_is_locked = true; + } + } + + if (return_handle) { + *out_handle = m_event.GetHandle(); + } + + R_SUCCEED(); +} + +Result ILockAccessor::Unlock() { + LOG_INFO(Service_AM, "called"); + + { + std::scoped_lock lk{m_mutex}; + m_is_locked = false; + } + + m_event.Signal(); + R_SUCCEED(); +} + +Result ILockAccessor::GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_handle) { + LOG_INFO(Service_AM, "called"); + *out_handle = m_event.GetHandle(); + R_SUCCEED(); +} + +Result ILockAccessor::IsLocked(Out<bool> out_is_locked) { + LOG_INFO(Service_AM, "called"); + std::scoped_lock lk{m_mutex}; + *out_is_locked = m_is_locked; + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/lock_accessor.h b/src/core/hle/service/am/service/lock_accessor.h new file mode 100644 index 000000000..9bfb5c050 --- /dev/null +++ b/src/core/hle/service/am/service/lock_accessor.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/os/event.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +class ILockAccessor final : public ServiceFramework<ILockAccessor> { +public: + explicit ILockAccessor(Core::System& system_); + ~ILockAccessor() override; + +private: + Result TryLock(Out<bool> out_is_locked, OutCopyHandle<Kernel::KReadableEvent> out_handle, + bool return_handle); + Result Unlock(); + Result GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_handle); + Result IsLocked(Out<bool> out_is_locked); + +private: + KernelHelpers::ServiceContext m_context; + Event m_event; + std::mutex m_mutex{}; + bool m_is_locked{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/process_winding_controller.cpp b/src/core/hle/service/am/service/process_winding_controller.cpp new file mode 100644 index 000000000..10df830d7 --- /dev/null +++ b/src/core/hle/service/am/service/process_winding_controller.cpp @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/service/library_applet_accessor.h" +#include "core/hle/service/am/service/process_winding_controller.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IProcessWindingController::IProcessWindingController(Core::System& system_, + std::shared_ptr<Applet> applet) + : ServiceFramework{system_, "IProcessWindingController"}, m_applet{std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IProcessWindingController::GetLaunchReason>, "GetLaunchReason"}, + {11, D<&IProcessWindingController::OpenCallingLibraryApplet>, "OpenCallingLibraryApplet"}, + {21, nullptr, "PushContext"}, + {22, nullptr, "PopContext"}, + {23, nullptr, "CancelWindingReservation"}, + {30, nullptr, "WindAndDoReserved"}, + {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, + {41, nullptr, "ReserveToStartAndWait"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IProcessWindingController::~IProcessWindingController() = default; + +Result IProcessWindingController::GetLaunchReason( + Out<AppletProcessLaunchReason> out_launch_reason) { + LOG_INFO(Service_AM, "called"); + *out_launch_reason = m_applet->launch_reason; + R_SUCCEED(); +} + +Result IProcessWindingController::OpenCallingLibraryApplet( + Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet) { + LOG_INFO(Service_AM, "called"); + + const auto caller_applet = m_applet->caller_applet.lock(); + if (caller_applet == nullptr) { + LOG_ERROR(Service_AM, "No caller applet available"); + R_THROW(ResultUnknown); + } + + *out_calling_library_applet = std::make_shared<ILibraryAppletAccessor>( + system, m_applet->caller_applet_broker, caller_applet); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/process_winding_controller.h b/src/core/hle/service/am/service/process_winding_controller.h new file mode 100644 index 000000000..4408af1f1 --- /dev/null +++ b/src/core/hle/service/am/service/process_winding_controller.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; +class ILibraryAppletAccessor; + +class IProcessWindingController final : public ServiceFramework<IProcessWindingController> { +public: + explicit IProcessWindingController(Core::System& system_, std::shared_ptr<Applet> applet_); + ~IProcessWindingController() override; + +private: + Result GetLaunchReason(Out<AppletProcessLaunchReason> out_launch_reason); + Result OpenCallingLibraryApplet( + Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet); + + const std::shared_ptr<Applet> m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/self_controller.cpp b/src/core/hle/service/am/service/self_controller.cpp new file mode 100644 index 000000000..06314407c --- /dev/null +++ b/src/core/hle/service/am/service/self_controller.cpp @@ -0,0 +1,394 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/service/self_controller.h" +#include "core/hle/service/caps/caps_su.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/vi/vi_results.h" + +namespace Service::AM { + +ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> applet, + Kernel::KProcess* process) + : ServiceFramework{system_, "ISelfController"}, m_process{process}, m_applet{ + std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&ISelfController::Exit>, "Exit"}, + {1, D<&ISelfController::LockExit>, "LockExit"}, + {2, D<&ISelfController::UnlockExit>, "UnlockExit"}, + {3, D<&ISelfController::EnterFatalSection>, "EnterFatalSection"}, + {4, D<&ISelfController::LeaveFatalSection>, "LeaveFatalSection"}, + {9, D<&ISelfController::GetLibraryAppletLaunchableEvent>, "GetLibraryAppletLaunchableEvent"}, + {10, D<&ISelfController::SetScreenShotPermission>, "SetScreenShotPermission"}, + {11, D<&ISelfController::SetOperationModeChangedNotification>, "SetOperationModeChangedNotification"}, + {12, D<&ISelfController::SetPerformanceModeChangedNotification>, "SetPerformanceModeChangedNotification"}, + {13, D<&ISelfController::SetFocusHandlingMode>, "SetFocusHandlingMode"}, + {14, D<&ISelfController::SetRestartMessageEnabled>, "SetRestartMessageEnabled"}, + {15, D<&ISelfController::SetScreenShotAppletIdentityInfo>, "SetScreenShotAppletIdentityInfo"}, + {16, D<&ISelfController::SetOutOfFocusSuspendingEnabled>, "SetOutOfFocusSuspendingEnabled"}, + {17, nullptr, "SetControllerFirmwareUpdateSection"}, + {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, + {19, D<&ISelfController::SetAlbumImageOrientation>, "SetAlbumImageOrientation"}, + {20, nullptr, "SetDesirableKeyboardLayout"}, + {21, nullptr, "GetScreenShotProgramId"}, + {40, D<&ISelfController::CreateManagedDisplayLayer>, "CreateManagedDisplayLayer"}, + {41, D<&ISelfController::IsSystemBufferSharingEnabled>, "IsSystemBufferSharingEnabled"}, + {42, D<&ISelfController::GetSystemSharedLayerHandle>, "GetSystemSharedLayerHandle"}, + {43, D<&ISelfController::GetSystemSharedBufferHandle>, "GetSystemSharedBufferHandle"}, + {44, D<&ISelfController::CreateManagedDisplaySeparableLayer>, "CreateManagedDisplaySeparableLayer"}, + {45, nullptr, "SetManagedDisplayLayerSeparationMode"}, + {46, nullptr, "SetRecordingLayerCompositionEnabled"}, + {50, D<&ISelfController::SetHandlesRequestToDisplay>, "SetHandlesRequestToDisplay"}, + {51, D<&ISelfController::ApproveToDisplay>, "ApproveToDisplay"}, + {60, D<&ISelfController::OverrideAutoSleepTimeAndDimmingTime>, "OverrideAutoSleepTimeAndDimmingTime"}, + {61, D<&ISelfController::SetMediaPlaybackState>, "SetMediaPlaybackState"}, + {62, D<&ISelfController::SetIdleTimeDetectionExtension>, "SetIdleTimeDetectionExtension"}, + {63, D<&ISelfController::GetIdleTimeDetectionExtension>, "GetIdleTimeDetectionExtension"}, + {64, nullptr, "SetInputDetectionSourceSet"}, + {65, D<&ISelfController::ReportUserIsActive>, "ReportUserIsActive"}, + {66, nullptr, "GetCurrentIlluminance"}, + {67, nullptr, "IsIlluminanceAvailable"}, + {68, D<&ISelfController::SetAutoSleepDisabled>, "SetAutoSleepDisabled"}, + {69, D<&ISelfController::IsAutoSleepDisabled>, "IsAutoSleepDisabled"}, + {70, nullptr, "ReportMultimediaError"}, + {71, nullptr, "GetCurrentIlluminanceEx"}, + {72, D<&ISelfController::SetInputDetectionPolicy>, "SetInputDetectionPolicy"}, + {80, nullptr, "SetWirelessPriorityMode"}, + {90, D<&ISelfController::GetAccumulatedSuspendedTickValue>, "GetAccumulatedSuspendedTickValue"}, + {91, D<&ISelfController::GetAccumulatedSuspendedTickChangedEvent>, "GetAccumulatedSuspendedTickChangedEvent"}, + {100, D<&ISelfController::SetAlbumImageTakenNotificationEnabled>, "SetAlbumImageTakenNotificationEnabled"}, + {110, nullptr, "SetApplicationAlbumUserData"}, + {120, D<&ISelfController::SaveCurrentScreenshot>, "SaveCurrentScreenshot"}, + {130, D<&ISelfController::SetRecordVolumeMuted>, "SetRecordVolumeMuted"}, + {1000, nullptr, "GetDebugStorageChannel"}, + }; + // clang-format on + + RegisterHandlers(functions); + + std::scoped_lock lk{m_applet->lock}; + m_applet->display_layer_manager.Initialize(system, m_process, m_applet->applet_id, + m_applet->library_applet_mode); +} + +ISelfController::~ISelfController() { + std::scoped_lock lk{m_applet->lock}; + m_applet->display_layer_manager.Finalize(); +} + +Result ISelfController::Exit() { + LOG_DEBUG(Service_AM, "called"); + + // TODO + system.Exit(); + + R_SUCCEED(); +} + +Result ISelfController::LockExit() { + LOG_DEBUG(Service_AM, "called"); + + system.SetExitLocked(true); + + R_SUCCEED(); +} + +Result ISelfController::UnlockExit() { + LOG_DEBUG(Service_AM, "called"); + + system.SetExitLocked(false); + + if (system.GetExitRequested()) { + system.Exit(); + } + + R_SUCCEED(); +} + +Result ISelfController::EnterFatalSection() { + std::scoped_lock lk{m_applet->lock}; + + m_applet->fatal_section_count++; + LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", m_applet->fatal_section_count); + + R_SUCCEED(); +} + +Result ISelfController::LeaveFatalSection() { + LOG_DEBUG(Service_AM, "called"); + + // Entry and exit of fatal sections must be balanced. + std::scoped_lock lk{m_applet->lock}; + R_UNLESS(m_applet->fatal_section_count > 0, AM::ResultFatalSectionCountImbalance); + m_applet->fatal_section_count--; + + R_SUCCEED(); +} + +Result ISelfController::GetLibraryAppletLaunchableEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + m_applet->library_applet_launchable_event.Signal(); + *out_event = m_applet->library_applet_launchable_event.GetHandle(); + + R_SUCCEED(); +} + +Result ISelfController::SetScreenShotPermission(ScreenshotPermission screen_shot_permission) { + LOG_DEBUG(Service_AM, "called, permission={}", screen_shot_permission); + + std::scoped_lock lk{m_applet->lock}; + m_applet->screenshot_permission = screen_shot_permission; + + R_SUCCEED(); +} + +Result ISelfController::SetOperationModeChangedNotification(bool enabled) { + LOG_INFO(Service_AM, "called, enabled={}", enabled); + + std::scoped_lock lk{m_applet->lock}; + m_applet->operation_mode_changed_notification_enabled = enabled; + + R_SUCCEED(); +} + +Result ISelfController::SetPerformanceModeChangedNotification(bool enabled) { + LOG_INFO(Service_AM, "called, enabled={}", enabled); + + std::scoped_lock lk{m_applet->lock}; + m_applet->performance_mode_changed_notification_enabled = enabled; + + R_SUCCEED(); +} + +Result ISelfController::SetFocusHandlingMode(bool notify, bool background, bool suspend) { + LOG_WARNING(Service_AM, "(STUBBED) called, notify={} background={} suspend={}", notify, + background, suspend); + + std::scoped_lock lk{m_applet->lock}; + m_applet->focus_handling_mode = {notify, background, suspend}; + + R_SUCCEED(); +} + +Result ISelfController::SetRestartMessageEnabled(bool enabled) { + LOG_INFO(Service_AM, "called, enabled={}", enabled); + + std::scoped_lock lk{m_applet->lock}; + m_applet->restart_message_enabled = enabled; + + R_SUCCEED(); +} + +Result ISelfController::SetScreenShotAppletIdentityInfo( + AppletIdentityInfo screen_shot_applet_identity_info) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{m_applet->lock}; + m_applet->screen_shot_identity = screen_shot_applet_identity_info; + + R_SUCCEED(); +} + +Result ISelfController::SetOutOfFocusSuspendingEnabled(bool enabled) { + LOG_INFO(Service_AM, "called, enabled={}", enabled); + + std::scoped_lock lk{m_applet->lock}; + m_applet->out_of_focus_suspension_enabled = enabled; + + R_SUCCEED(); +} + +Result ISelfController::SetAlbumImageOrientation( + Capture::AlbumImageOrientation album_image_orientation) { + LOG_WARNING(Service_AM, "(STUBBED) called, orientation={}", album_image_orientation); + + std::scoped_lock lk{m_applet->lock}; + m_applet->album_image_orientation = album_image_orientation; + + R_SUCCEED(); +} + +Result ISelfController::IsSystemBufferSharingEnabled() { + LOG_INFO(Service_AM, "called"); + + std::scoped_lock lk{m_applet->lock}; + R_RETURN(m_applet->display_layer_manager.IsSystemBufferSharingEnabled()); +} + +Result ISelfController::GetSystemSharedBufferHandle(Out<u64> out_buffer_id) { + LOG_INFO(Service_AM, "called"); + + u64 layer_id; + + std::scoped_lock lk{m_applet->lock}; + R_RETURN(m_applet->display_layer_manager.GetSystemSharedLayerHandle(out_buffer_id, &layer_id)); +} + +Result ISelfController::GetSystemSharedLayerHandle(Out<u64> out_buffer_id, Out<u64> out_layer_id) { + LOG_INFO(Service_AM, "called"); + + std::scoped_lock lk{m_applet->lock}; + R_RETURN( + m_applet->display_layer_manager.GetSystemSharedLayerHandle(out_buffer_id, out_layer_id)); +} + +Result ISelfController::CreateManagedDisplayLayer(Out<u64> out_layer_id) { + LOG_INFO(Service_AM, "called"); + + std::scoped_lock lk{m_applet->lock}; + R_RETURN(m_applet->display_layer_manager.CreateManagedDisplayLayer(out_layer_id)); +} + +Result ISelfController::CreateManagedDisplaySeparableLayer(Out<u64> out_layer_id, + Out<u64> out_recording_layer_id) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{m_applet->lock}; + R_RETURN(m_applet->display_layer_manager.CreateManagedDisplaySeparableLayer( + out_layer_id, out_recording_layer_id)); +} + +Result ISelfController::SetHandlesRequestToDisplay(bool enable) { + LOG_WARNING(Service_AM, "(STUBBED) called, enable={}", enable); + R_SUCCEED(); +} + +Result ISelfController::ApproveToDisplay() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result ISelfController::SetMediaPlaybackState(bool state) { + LOG_WARNING(Service_AM, "(STUBBED) called, state={}", state); + R_SUCCEED(); +} + +Result ISelfController::OverrideAutoSleepTimeAndDimmingTime(s32 a, s32 b, s32 c, s32 d) { + LOG_WARNING(Service_AM, "(STUBBED) called, a={}, b={}, c={}, d={}", a, b, c, d); + R_SUCCEED(); +} + +Result ISelfController::SetIdleTimeDetectionExtension( + IdleTimeDetectionExtension idle_time_detection_extension) { + LOG_DEBUG(Service_AM, "(STUBBED) called extension={}", idle_time_detection_extension); + + std::scoped_lock lk{m_applet->lock}; + m_applet->idle_time_detection_extension = idle_time_detection_extension; + + R_SUCCEED(); +} + +Result ISelfController::GetIdleTimeDetectionExtension( + Out<IdleTimeDetectionExtension> out_idle_time_detection_extension) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{m_applet->lock}; + *out_idle_time_detection_extension = m_applet->idle_time_detection_extension; + + R_SUCCEED(); +} + +Result ISelfController::ReportUserIsActive() { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result ISelfController::SetAutoSleepDisabled(bool is_auto_sleep_disabled) { + LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", is_auto_sleep_disabled); + + // On the system itself, if the previous state of is_auto_sleep_disabled + // differed from the current value passed in, it'd signify the internal + // window manager to update (and also increment some statistics like update counts) + // + // It'd also indicate this change to an idle handling context. + // + // However, given we're emulating this behavior, most of this can be ignored + // and it's sufficient to simply set the member variable for querying via + // IsAutoSleepDisabled(). + + std::scoped_lock lk{m_applet->lock}; + m_applet->auto_sleep_disabled = is_auto_sleep_disabled; + + R_SUCCEED(); +} + +Result ISelfController::IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled) { + LOG_DEBUG(Service_AM, "called."); + + std::scoped_lock lk{m_applet->lock}; + *out_is_auto_sleep_disabled = m_applet->auto_sleep_disabled; + + R_SUCCEED(); +} + +Result ISelfController::SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result ISelfController::GetAccumulatedSuspendedTickValue( + Out<u64> out_accumulated_suspended_tick_value) { + LOG_DEBUG(Service_AM, "called."); + + // This command returns the total number of system ticks since ISelfController creation + // where the game was suspended. Since Yuzu doesn't implement game suspension, this command + // can just always return 0 ticks. + std::scoped_lock lk{m_applet->lock}; + *out_accumulated_suspended_tick_value = m_applet->suspended_ticks; + + R_SUCCEED(); +} + +Result ISelfController::GetAccumulatedSuspendedTickChangedEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_DEBUG(Service_AM, "called."); + + *out_event = m_applet->accumulated_suspended_tick_changed_event.GetHandle(); + R_SUCCEED(); +} + +Result ISelfController::SetAlbumImageTakenNotificationEnabled(bool enabled) { + LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled); + + // This service call sets an internal flag whether a notification is shown when an image is + // captured. Currently we do not support capturing images via the capture button, so this can be + // stubbed for now. + std::scoped_lock lk{m_applet->lock}; + m_applet->album_image_taken_notification_enabled = enabled; + + R_SUCCEED(); +} + +Result ISelfController::SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option) { + LOG_INFO(Service_AM, "called, report_option={}", album_report_option); + + const auto screenshot_service = + system.ServiceManager().GetService<Service::Capture::IScreenShotApplicationService>( + "caps:su"); + + if (screenshot_service) { + screenshot_service->CaptureAndSaveScreenshot(album_report_option); + } + + R_SUCCEED(); +} + +Result ISelfController::SetRecordVolumeMuted(bool muted) { + LOG_WARNING(Service_AM, "(STUBBED) called. muted={}", muted); + + std::scoped_lock lk{m_applet->lock}; + m_applet->record_volume_muted = muted; + + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/self_controller.h b/src/core/hle/service/am/service/self_controller.h new file mode 100644 index 000000000..eca083cfe --- /dev/null +++ b/src/core/hle/service/am/service/self_controller.h @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KReadableEvent; +} + +namespace Service::Capture { +enum class AlbumImageOrientation; +enum class AlbumReportOption; +} // namespace Service::Capture + +namespace Service::AM { + +struct Applet; + +class ISelfController final : public ServiceFramework<ISelfController> { +public: + explicit ISelfController(Core::System& system_, std::shared_ptr<Applet> applet, + Kernel::KProcess* process); + ~ISelfController() override; + +private: + Result Exit(); + Result LockExit(); + Result UnlockExit(); + Result EnterFatalSection(); + Result LeaveFatalSection(); + Result GetLibraryAppletLaunchableEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result SetScreenShotPermission(ScreenshotPermission screen_shot_permission); + Result SetOperationModeChangedNotification(bool enabled); + Result SetPerformanceModeChangedNotification(bool enabled); + Result SetFocusHandlingMode(bool notify, bool background, bool suspend); + Result SetRestartMessageEnabled(bool enabled); + Result SetScreenShotAppletIdentityInfo(AppletIdentityInfo screen_shot_applet_identity_info); + Result SetOutOfFocusSuspendingEnabled(bool enabled); + Result SetAlbumImageOrientation(Capture::AlbumImageOrientation album_image_orientation); + Result IsSystemBufferSharingEnabled(); + Result GetSystemSharedBufferHandle(Out<u64> out_buffer_id); + Result GetSystemSharedLayerHandle(Out<u64> out_buffer_id, Out<u64> out_layer_id); + Result CreateManagedDisplayLayer(Out<u64> out_layer_id); + Result CreateManagedDisplaySeparableLayer(Out<u64> out_layer_id, + Out<u64> out_recording_layer_id); + Result SetHandlesRequestToDisplay(bool enable); + Result ApproveToDisplay(); + Result SetMediaPlaybackState(bool state); + Result OverrideAutoSleepTimeAndDimmingTime(s32 a, s32 b, s32 c, s32 d); + Result SetIdleTimeDetectionExtension(IdleTimeDetectionExtension idle_time_detection_extension); + Result GetIdleTimeDetectionExtension( + Out<IdleTimeDetectionExtension> out_idle_time_detection_extension); + Result ReportUserIsActive(); + Result SetAutoSleepDisabled(bool is_auto_sleep_disabled); + Result IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled); + Result SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy); + Result GetAccumulatedSuspendedTickValue(Out<u64> out_accumulated_suspended_tick_value); + Result GetAccumulatedSuspendedTickChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result SetAlbumImageTakenNotificationEnabled(bool enabled); + Result SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option); + Result SetRecordVolumeMuted(bool muted); + + Kernel::KProcess* const m_process; + const std::shared_ptr<Applet> m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/storage.cpp b/src/core/hle/service/am/service/storage.cpp new file mode 100644 index 000000000..25ee0afbd --- /dev/null +++ b/src/core/hle/service/am/service/storage.cpp @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/library_applet_storage.h" +#include "core/hle/service/am/service/storage.h" +#include "core/hle/service/am/service/storage_accessor.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IStorage::IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl) + : ServiceFramework{system_, "IStorage"}, m_impl{std::move(impl)} { + static const FunctionInfo functions[] = { + {0, D<&IStorage::Open>, "Open"}, + {1, D<&IStorage::OpenTransferStorage>, "OpenTransferStorage"}, + }; + + RegisterHandlers(functions); +} + +IStorage::IStorage(Core::System& system_, std::vector<u8>&& data) + : IStorage(system_, CreateStorage(std::move(data))) {} + +IStorage::~IStorage() = default; + +Result IStorage::Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor) { + LOG_DEBUG(Service_AM, "called"); + + R_UNLESS(m_impl->GetHandle() == nullptr, AM::ResultInvalidStorageType); + + *out_storage_accessor = std::make_shared<IStorageAccessor>(system, m_impl); + R_SUCCEED(); +} + +Result IStorage::OpenTransferStorage( + Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor) { + R_UNLESS(m_impl->GetHandle() != nullptr, AM::ResultInvalidStorageType); + + *out_transfer_storage_accessor = std::make_shared<ITransferStorageAccessor>(system, m_impl); + R_SUCCEED(); +} + +std::vector<u8> IStorage::GetData() const { + return m_impl->GetData(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/storage.h b/src/core/hle/service/am/service/storage.h new file mode 100644 index 000000000..cde2ed0ea --- /dev/null +++ b/src/core/hle/service/am/service/storage.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +class LibraryAppletStorage; +class IStorageAccessor; +class ITransferStorageAccessor; + +class IStorage final : public ServiceFramework<IStorage> { +public: + explicit IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl); + explicit IStorage(Core::System& system_, std::vector<u8>&& buffer); + ~IStorage() override; + + std::shared_ptr<LibraryAppletStorage> GetImpl() const { + return m_impl; + } + + std::vector<u8> GetData() const; + +private: + Result Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor); + Result OpenTransferStorage( + Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor); + + const std::shared_ptr<LibraryAppletStorage> m_impl; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/storage_accessor.cpp b/src/core/hle/service/am/service/storage_accessor.cpp new file mode 100644 index 000000000..84577fee4 --- /dev/null +++ b/src/core/hle/service/am/service/storage_accessor.cpp @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/am/library_applet_storage.h" +#include "core/hle/service/am/service/storage_accessor.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IStorageAccessor::IStorageAccessor(Core::System& system_, + std::shared_ptr<LibraryAppletStorage> impl) + : ServiceFramework{system_, "IStorageAccessor"}, m_impl{std::move(impl)} { + static const FunctionInfo functions[] = { + {0, D<&IStorageAccessor::GetSize>, "GetSize"}, + {10, D<&IStorageAccessor::Write>, "Write"}, + {11, D<&IStorageAccessor::Read>, "Read"}, + }; + + RegisterHandlers(functions); +} + +IStorageAccessor::~IStorageAccessor() = default; + +Result IStorageAccessor::GetSize(Out<s64> out_size) { + LOG_DEBUG(Service_AM, "called"); + *out_size = m_impl->GetSize(); + R_SUCCEED(); +} + +Result IStorageAccessor::Write(InBuffer<BufferAttr_HipcAutoSelect> buffer, s64 offset) { + LOG_DEBUG(Service_AM, "called, offset={} size={}", offset, buffer.size()); + R_RETURN(m_impl->Write(offset, buffer.data(), buffer.size())); +} + +Result IStorageAccessor::Read(OutBuffer<BufferAttr_HipcAutoSelect> out_buffer, s64 offset) { + LOG_DEBUG(Service_AM, "called, offset={} size={}", offset, out_buffer.size()); + R_RETURN(m_impl->Read(offset, out_buffer.data(), out_buffer.size())); +} + +ITransferStorageAccessor::ITransferStorageAccessor(Core::System& system_, + std::shared_ptr<LibraryAppletStorage> impl) + : ServiceFramework{system_, "ITransferStorageAccessor"}, m_impl{std::move(impl)} { + static const FunctionInfo functions[] = { + {0, D<&ITransferStorageAccessor::GetSize>, "GetSize"}, + {1, D<&ITransferStorageAccessor::GetHandle>, "GetHandle"}, + }; + + RegisterHandlers(functions); +} + +ITransferStorageAccessor::~ITransferStorageAccessor() = default; + +Result ITransferStorageAccessor::GetSize(Out<s64> out_size) { + LOG_DEBUG(Service_AM, "called"); + *out_size = m_impl->GetSize(); + R_SUCCEED(); +} + +Result ITransferStorageAccessor::GetHandle(Out<s64> out_size, + OutCopyHandle<Kernel::KTransferMemory> out_handle) { + LOG_INFO(Service_AM, "called"); + *out_size = m_impl->GetSize(); + *out_handle = m_impl->GetHandle(); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/storage_accessor.h b/src/core/hle/service/am/service/storage_accessor.h new file mode 100644 index 000000000..1a01730e0 --- /dev/null +++ b/src/core/hle/service/am/service/storage_accessor.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/am/library_applet_storage.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { +public: + explicit IStorageAccessor(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl); + ~IStorageAccessor() override; + +private: + Result GetSize(Out<s64> out_size); + Result Write(InBuffer<BufferAttr_HipcAutoSelect> buffer, s64 offset); + Result Read(OutBuffer<BufferAttr_HipcAutoSelect> out_buffer, s64 offset); + + const std::shared_ptr<LibraryAppletStorage> m_impl; +}; + +class ITransferStorageAccessor final : public ServiceFramework<ITransferStorageAccessor> { +public: + explicit ITransferStorageAccessor(Core::System& system_, + std::shared_ptr<LibraryAppletStorage> impl); + ~ITransferStorageAccessor() override; + +private: + Result GetSize(Out<s64> out_size); + Result GetHandle(Out<s64> out_size, OutCopyHandle<Kernel::KTransferMemory> out_handle); + + const std::shared_ptr<LibraryAppletStorage> m_impl; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/system_applet_proxy.cpp b/src/core/hle/service/am/service/system_applet_proxy.cpp new file mode 100644 index 000000000..d1871ef9b --- /dev/null +++ b/src/core/hle/service/am/service/system_applet_proxy.cpp @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/service/applet_common_functions.h" +#include "core/hle/service/am/service/application_creator.h" +#include "core/hle/service/am/service/audio_controller.h" +#include "core/hle/service/am/service/common_state_getter.h" +#include "core/hle/service/am/service/debug_functions.h" +#include "core/hle/service/am/service/display_controller.h" +#include "core/hle/service/am/service/global_state_controller.h" +#include "core/hle/service/am/service/home_menu_functions.h" +#include "core/hle/service/am/service/library_applet_creator.h" +#include "core/hle/service/am/service/process_winding_controller.h" +#include "core/hle/service/am/service/self_controller.h" +#include "core/hle/service/am/service/system_applet_proxy.h" +#include "core/hle/service/am/service/window_controller.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +ISystemAppletProxy::ISystemAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet, + Kernel::KProcess* process) + : ServiceFramework{system_, "ISystemAppletProxy"}, m_process{process}, m_applet{ + std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&ISystemAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, + {1, D<&ISystemAppletProxy::GetSelfController>, "GetSelfController"}, + {2, D<&ISystemAppletProxy::GetWindowController>, "GetWindowController"}, + {3, D<&ISystemAppletProxy::GetAudioController>, "GetAudioController"}, + {4, D<&ISystemAppletProxy::GetDisplayController>, "GetDisplayController"}, + {10, D<&ISystemAppletProxy::GetProcessWindingController>, "GetProcessWindingController"}, + {11, D<&ISystemAppletProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"}, + {20, D<&ISystemAppletProxy::GetHomeMenuFunctions>, "GetHomeMenuFunctions"}, + {21, D<&ISystemAppletProxy::GetGlobalStateController>, "GetGlobalStateController"}, + {22, D<&ISystemAppletProxy::GetApplicationCreator>, "GetApplicationCreator"}, + {23, D<&ISystemAppletProxy::GetAppletCommonFunctions>, "GetAppletCommonFunctions"}, + {1000, D<&ISystemAppletProxy::GetDebugFunctions>, "GetDebugFunctions"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ISystemAppletProxy::~ISystemAppletProxy() = default; + +Result ISystemAppletProxy::GetAudioController( + Out<SharedPointer<IAudioController>> out_audio_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_audio_controller = std::make_shared<IAudioController>(system); + R_SUCCEED(); +} + +Result ISystemAppletProxy::GetDisplayController( + Out<SharedPointer<IDisplayController>> out_display_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_display_controller = std::make_shared<IDisplayController>(system, m_applet); + R_SUCCEED(); +} + +Result ISystemAppletProxy::GetProcessWindingController( + Out<SharedPointer<IProcessWindingController>> out_process_winding_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_process_winding_controller = std::make_shared<IProcessWindingController>(system, m_applet); + R_SUCCEED(); +} + +Result ISystemAppletProxy::GetDebugFunctions( + Out<SharedPointer<IDebugFunctions>> out_debug_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_debug_functions = std::make_shared<IDebugFunctions>(system); + R_SUCCEED(); +} + +Result ISystemAppletProxy::GetWindowController( + Out<SharedPointer<IWindowController>> out_window_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_window_controller = std::make_shared<IWindowController>(system, m_applet); + R_SUCCEED(); +} + +Result ISystemAppletProxy::GetSelfController( + Out<SharedPointer<ISelfController>> out_self_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process); + R_SUCCEED(); +} + +Result ISystemAppletProxy::GetCommonStateGetter( + Out<SharedPointer<ICommonStateGetter>> out_common_state_getter) { + LOG_DEBUG(Service_AM, "called"); + *out_common_state_getter = std::make_shared<ICommonStateGetter>(system, m_applet); + R_SUCCEED(); +} + +Result ISystemAppletProxy::GetLibraryAppletCreator( + Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator) { + LOG_DEBUG(Service_AM, "called"); + *out_library_applet_creator = std::make_shared<ILibraryAppletCreator>(system, m_applet); + R_SUCCEED(); +} + +Result ISystemAppletProxy::GetApplicationCreator( + Out<SharedPointer<IApplicationCreator>> out_application_creator) { + LOG_DEBUG(Service_AM, "called"); + *out_application_creator = std::make_shared<IApplicationCreator>(system); + R_SUCCEED(); +} + +Result ISystemAppletProxy::GetAppletCommonFunctions( + Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_applet_common_functions = std::make_shared<IAppletCommonFunctions>(system, m_applet); + R_SUCCEED(); +} + +Result ISystemAppletProxy::GetHomeMenuFunctions( + Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_home_menu_functions = std::make_shared<IHomeMenuFunctions>(system, m_applet); + R_SUCCEED(); +} + +Result ISystemAppletProxy::GetGlobalStateController( + Out<SharedPointer<IGlobalStateController>> out_global_state_controller) { + LOG_DEBUG(Service_AM, "called"); + *out_global_state_controller = std::make_shared<IGlobalStateController>(system); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/system_applet_proxy.h b/src/core/hle/service/am/service/system_applet_proxy.h new file mode 100644 index 000000000..67cd50e03 --- /dev/null +++ b/src/core/hle/service/am/service/system_applet_proxy.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; +class IAppletCommonFunctions; +class IApplicationCreator; +class IAudioController; +class ICommonStateGetter; +class IDebugFunctions; +class IDisplayController; +class IHomeMenuFunctions; +class IGlobalStateController; +class ILibraryAppletCreator; +class IProcessWindingController; +class ISelfController; +class IWindowController; + +class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { +public: + explicit ISystemAppletProxy(Core::System& system, std::shared_ptr<Applet> applet, + Kernel::KProcess* process); + ~ISystemAppletProxy(); + +private: + Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller); + Result GetDisplayController(Out<SharedPointer<IDisplayController>> out_display_controller); + Result GetProcessWindingController( + Out<SharedPointer<IProcessWindingController>> out_process_winding_controller); + Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions); + Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller); + Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller); + Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter); + Result GetLibraryAppletCreator( + Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator); + Result GetApplicationCreator(Out<SharedPointer<IApplicationCreator>> out_application_creator); + Result GetAppletCommonFunctions( + Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions); + Result GetHomeMenuFunctions(Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions); + Result GetGlobalStateController( + Out<SharedPointer<IGlobalStateController>> out_global_state_controller); + + Kernel::KProcess* const m_process; + const std::shared_ptr<Applet> m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/window_controller.cpp b/src/core/hle/service/am/service/window_controller.cpp new file mode 100644 index 000000000..99a4f50a2 --- /dev/null +++ b/src/core/hle/service/am/service/window_controller.cpp @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/service/window_controller.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::AM { + +IWindowController::IWindowController(Core::System& system_, std::shared_ptr<Applet> applet) + : ServiceFramework{system_, "IWindowController"}, m_applet{std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CreateWindow"}, + {1, D<&IWindowController::GetAppletResourceUserId>, "GetAppletResourceUserId"}, + {2, D<&IWindowController::GetAppletResourceUserIdOfCallerApplet>, "GetAppletResourceUserIdOfCallerApplet"}, + {10, D<&IWindowController::AcquireForegroundRights>, "AcquireForegroundRights"}, + {11, D<&IWindowController::ReleaseForegroundRights>, "ReleaseForegroundRights"}, + {12, D<&IWindowController::RejectToChangeIntoBackground>, "RejectToChangeIntoBackground"}, + {20, D<&IWindowController::SetAppletWindowVisibility>, "SetAppletWindowVisibility"}, + {21, D<&IWindowController::SetAppletGpuTimeSlice>, "SetAppletGpuTimeSlice"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IWindowController::~IWindowController() = default; + +Result IWindowController::GetAppletResourceUserId(Out<AppletResourceUserId> out_aruid) { + LOG_INFO(Service_AM, "called"); + *out_aruid = m_applet->aruid; + R_SUCCEED(); +} + +Result IWindowController::GetAppletResourceUserIdOfCallerApplet( + Out<AppletResourceUserId> out_aruid) { + LOG_INFO(Service_AM, "called"); + + if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet != nullptr) { + *out_aruid = caller_applet->aruid; + } else { + *out_aruid = AppletResourceUserId{}; + } + + R_SUCCEED(); +} + +Result IWindowController::AcquireForegroundRights() { + LOG_INFO(Service_AM, "called"); + R_SUCCEED(); +} + +Result IWindowController::ReleaseForegroundRights() { + LOG_INFO(Service_AM, "called"); + R_SUCCEED(); +} + +Result IWindowController::RejectToChangeIntoBackground() { + LOG_INFO(Service_AM, "called"); + R_SUCCEED(); +} + +Result IWindowController::SetAppletWindowVisibility(bool visible) { + m_applet->display_layer_manager.SetWindowVisibility(visible); + m_applet->hid_registration.EnableAppletToGetInput(visible); + + if (visible) { + m_applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground); + m_applet->focus_state = FocusState::InFocus; + } else { + m_applet->focus_state = FocusState::NotInFocus; + } + + m_applet->message_queue.PushMessage(AppletMessage::FocusStateChanged); + + R_SUCCEED(); +} + +Result IWindowController::SetAppletGpuTimeSlice(s64 time_slice) { + LOG_WARNING(Service_AM, "(STUBBED) called, time_slice={}", time_slice); + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/window_controller.h b/src/core/hle/service/am/service/window_controller.h new file mode 100644 index 000000000..bfbad9bcc --- /dev/null +++ b/src/core/hle/service/am/service/window_controller.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class IWindowController final : public ServiceFramework<IWindowController> { +public: + explicit IWindowController(Core::System& system_, std::shared_ptr<Applet> applet); + ~IWindowController() override; + +private: + Result GetAppletResourceUserId(Out<AppletResourceUserId> out_aruid); + Result GetAppletResourceUserIdOfCallerApplet(Out<AppletResourceUserId> out_aruid); + Result AcquireForegroundRights(); + Result ReleaseForegroundRights(); + Result RejectToChangeIntoBackground(); + Result SetAppletWindowVisibility(bool visible); + Result SetAppletGpuTimeSlice(s64 time_slice); + + const std::shared_ptr<Applet> m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp deleted file mode 100644 index ec581e32b..000000000 --- a/src/core/hle/service/am/spsm.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/spsm.h" - -namespace Service::AM { - -SPSM::SPSM(Core::System& system_) : ServiceFramework{system_, "spsm"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetState"}, - {1, nullptr, "EnterSleep"}, - {2, nullptr, "GetLastWakeReason"}, - {3, nullptr, "Shutdown"}, - {4, nullptr, "GetNotificationMessageEventHandle"}, - {5, nullptr, "ReceiveNotificationMessage"}, - {6, nullptr, "AnalyzeLogForLastSleepWakeSequence"}, - {7, nullptr, "ResetEventLog"}, - {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"}, - {9, nullptr, "ChangeHomeButtonLongPressingTime"}, - {10, nullptr, "PutErrorState"}, - {11, nullptr, "InvalidateCurrentHomeButtonPressing"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -SPSM::~SPSM() = default; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/spsm.h b/src/core/hle/service/am/spsm.h deleted file mode 100644 index 922f8863e..000000000 --- a/src/core/hle/service/am/spsm.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Core { -class System; -} - -namespace Service::AM { - -class SPSM final : public ServiceFramework<SPSM> { -public: - explicit SPSM(Core::System& system_); - ~SPSM() override; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/storage.cpp b/src/core/hle/service/am/storage.cpp deleted file mode 100644 index 4e82afd1c..000000000 --- a/src/core/hle/service/am/storage.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/am_results.h" -#include "core/hle/service/am/library_applet_storage.h" -#include "core/hle/service/am/storage.h" -#include "core/hle/service/am/storage_accessor.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -IStorage::IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_) - : ServiceFramework{system_, "IStorage"}, impl{std::move(impl_)} { - static const FunctionInfo functions[] = { - {0, &IStorage::Open, "Open"}, - {1, &IStorage::OpenTransferStorage, "OpenTransferStorage"}, - }; - - RegisterHandlers(functions); -} - -IStorage::IStorage(Core::System& system_, std::vector<u8>&& data) - : IStorage(system_, CreateStorage(std::move(data))) {} - -IStorage::~IStorage() = default; - -void IStorage::Open(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - if (impl->GetHandle() != nullptr) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultInvalidStorageType); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IStorageAccessor>(system, impl); -} - -void IStorage::OpenTransferStorage(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - if (impl->GetHandle() == nullptr) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultInvalidStorageType); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ITransferStorageAccessor>(system, impl); -} - -std::vector<u8> IStorage::GetData() const { - return impl->GetData(); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/storage.h b/src/core/hle/service/am/storage.h deleted file mode 100644 index 10d00b141..000000000 --- a/src/core/hle/service/am/storage.h +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -class LibraryAppletStorage; - -class IStorage final : public ServiceFramework<IStorage> { -public: - explicit IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_); - explicit IStorage(Core::System& system_, std::vector<u8>&& buffer); - ~IStorage() override; - - std::shared_ptr<LibraryAppletStorage> GetImpl() const { - return impl; - } - - std::vector<u8> GetData() const; - -private: - void Open(HLERequestContext& ctx); - void OpenTransferStorage(HLERequestContext& ctx); - - const std::shared_ptr<LibraryAppletStorage> impl; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/storage_accessor.cpp b/src/core/hle/service/am/storage_accessor.cpp deleted file mode 100644 index a1184b065..000000000 --- a/src/core/hle/service/am/storage_accessor.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/service/am/am_results.h" -#include "core/hle/service/am/library_applet_storage.h" -#include "core/hle/service/am/storage_accessor.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -IStorageAccessor::IStorageAccessor(Core::System& system_, - std::shared_ptr<LibraryAppletStorage> impl_) - : ServiceFramework{system_, "IStorageAccessor"}, impl{std::move(impl_)} { - static const FunctionInfo functions[] = { - {0, &IStorageAccessor::GetSize, "GetSize"}, - {10, &IStorageAccessor::Write, "Write"}, - {11, &IStorageAccessor::Read, "Read"}, - }; - - RegisterHandlers(functions); -} - -IStorageAccessor::~IStorageAccessor() = default; - -void IStorageAccessor::GetSize(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 4}; - - rb.Push(ResultSuccess); - rb.Push(impl->GetSize()); -} - -void IStorageAccessor::Write(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const s64 offset{rp.Pop<s64>()}; - const auto data{ctx.ReadBuffer()}; - LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size()); - - const auto res{impl->Write(offset, data.data(), data.size())}; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); -} - -void IStorageAccessor::Read(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const s64 offset{rp.Pop<s64>()}; - std::vector<u8> data(ctx.GetWriteBufferSize()); - - LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size()); - - const auto res{impl->Read(offset, data.data(), data.size())}; - - ctx.WriteBuffer(data); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); -} - -ITransferStorageAccessor::ITransferStorageAccessor(Core::System& system_, - std::shared_ptr<LibraryAppletStorage> impl_) - : ServiceFramework{system_, "ITransferStorageAccessor"}, impl{std::move(impl_)} { - static const FunctionInfo functions[] = { - {0, &ITransferStorageAccessor::GetSize, "GetSize"}, - {1, &ITransferStorageAccessor::GetHandle, "GetHandle"}, - }; - - RegisterHandlers(functions); -} - -ITransferStorageAccessor::~ITransferStorageAccessor() = default; - -void ITransferStorageAccessor::GetSize(HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(impl->GetSize()); -} - -void ITransferStorageAccessor::GetHandle(HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 4, 1}; - rb.Push(ResultSuccess); - rb.Push(impl->GetSize()); - rb.PushCopyObjects(impl->GetHandle()); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/storage_accessor.h b/src/core/hle/service/am/storage_accessor.h deleted file mode 100644 index b9aa85a66..000000000 --- a/src/core/hle/service/am/storage_accessor.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/am/storage.h" -#include "core/hle/service/service.h" - -namespace Service::AM { - -class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { -public: - explicit IStorageAccessor(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_); - ~IStorageAccessor() override; - -private: - void GetSize(HLERequestContext& ctx); - void Write(HLERequestContext& ctx); - void Read(HLERequestContext& ctx); - - const std::shared_ptr<LibraryAppletStorage> impl; -}; - -class ITransferStorageAccessor final : public ServiceFramework<ITransferStorageAccessor> { -public: - explicit ITransferStorageAccessor(Core::System& system_, - std::shared_ptr<LibraryAppletStorage> impl_); - ~ITransferStorageAccessor() override; - -private: - void GetSize(HLERequestContext& ctx); - void GetHandle(HLERequestContext& ctx); - - const std::shared_ptr<LibraryAppletStorage> impl; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/system_applet_proxy.cpp b/src/core/hle/service/am/system_applet_proxy.cpp deleted file mode 100644 index 38643408e..000000000 --- a/src/core/hle/service/am/system_applet_proxy.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/applet_common_functions.h" -#include "core/hle/service/am/application_creator.h" -#include "core/hle/service/am/audio_controller.h" -#include "core/hle/service/am/common_state_getter.h" -#include "core/hle/service/am/debug_functions.h" -#include "core/hle/service/am/display_controller.h" -#include "core/hle/service/am/global_state_controller.h" -#include "core/hle/service/am/home_menu_functions.h" -#include "core/hle/service/am/library_applet_creator.h" -#include "core/hle/service/am/library_applet_self_accessor.h" -#include "core/hle/service/am/process_winding_controller.h" -#include "core/hle/service/am/self_controller.h" -#include "core/hle/service/am/system_applet_proxy.h" -#include "core/hle/service/am/window_controller.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -ISystemAppletProxy::ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr<Applet> applet_, Core::System& system_) - : ServiceFramework{system_, "ISystemAppletProxy"}, nvnflinger{nvnflinger_}, applet{std::move( - applet_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, - {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, - {2, &ISystemAppletProxy::GetWindowController, "GetWindowController"}, - {3, &ISystemAppletProxy::GetAudioController, "GetAudioController"}, - {4, &ISystemAppletProxy::GetDisplayController, "GetDisplayController"}, - {10, nullptr, "GetProcessWindingController"}, - {11, &ISystemAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, - {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, - {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, - {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"}, - {23, &ISystemAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"}, - {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -ISystemAppletProxy::~ISystemAppletProxy() = default; - -void ISystemAppletProxy::GetCommonStateGetter(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ICommonStateGetter>(system, applet); -} - -void ISystemAppletProxy::GetSelfController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger); -} - -void ISystemAppletProxy::GetWindowController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IWindowController>(system, applet); -} - -void ISystemAppletProxy::GetAudioController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioController>(system); -} - -void ISystemAppletProxy::GetDisplayController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDisplayController>(system, applet); -} - -void ISystemAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ILibraryAppletCreator>(system, applet); -} - -void ISystemAppletProxy::GetHomeMenuFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IHomeMenuFunctions>(system); -} - -void ISystemAppletProxy::GetGlobalStateController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IGlobalStateController>(system); -} - -void ISystemAppletProxy::GetApplicationCreator(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IApplicationCreator>(system); -} - -void ISystemAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IAppletCommonFunctions>(system, applet); -} - -void ISystemAppletProxy::GetDebugFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDebugFunctions>(system); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/system_applet_proxy.h b/src/core/hle/service/am/system_applet_proxy.h deleted file mode 100644 index 0390cd1e5..000000000 --- a/src/core/hle/service/am/system_applet_proxy.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/am/applet_message_queue.h" -#include "core/hle/service/service.h" - -namespace Service::AM { - -struct Applet; - -class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { -public: - explicit ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr<Applet> applet_, Core::System& system_); - ~ISystemAppletProxy(); - -private: - void GetCommonStateGetter(HLERequestContext& ctx); - void GetSelfController(HLERequestContext& ctx); - void GetWindowController(HLERequestContext& ctx); - void GetAudioController(HLERequestContext& ctx); - void GetDisplayController(HLERequestContext& ctx); - void GetLibraryAppletCreator(HLERequestContext& ctx); - void GetHomeMenuFunctions(HLERequestContext& ctx); - void GetGlobalStateController(HLERequestContext& ctx); - void GetApplicationCreator(HLERequestContext& ctx); - void GetAppletCommonFunctions(HLERequestContext& ctx); - void GetDebugFunctions(HLERequestContext& ctx); - - Nvnflinger::Nvnflinger& nvnflinger; - std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/system_buffer_manager.cpp b/src/core/hle/service/am/system_buffer_manager.cpp deleted file mode 100644 index 60a9afc9d..000000000 --- a/src/core/hle/service/am/system_buffer_manager.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/system_buffer_manager.h" -#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" -#include "core/hle/service/nvnflinger/nvnflinger.h" -#include "core/hle/service/vi/vi_results.h" - -namespace Service::AM { - -SystemBufferManager::SystemBufferManager() = default; - -SystemBufferManager::~SystemBufferManager() { - if (!m_nvnflinger) { - return; - } - - // Clean up shared layers. - if (m_buffer_sharing_enabled) { - } -} - -bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process, - AppletId applet_id) { - if (m_nvnflinger) { - return m_buffer_sharing_enabled; - } - - m_process = process; - m_nvnflinger = nvnflinger; - m_buffer_sharing_enabled = false; - m_system_shared_buffer_id = 0; - m_system_shared_layer_id = 0; - - if (applet_id <= AppletId::Application) { - return false; - } - - const auto display_id = m_nvnflinger->OpenDisplay("Default").value(); - const auto res = m_nvnflinger->GetSystemBufferManager().Initialize( - &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id); - - if (res.IsSuccess()) { - m_buffer_sharing_enabled = true; - m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible); - } - - return m_buffer_sharing_enabled; -} - -void SystemBufferManager::SetWindowVisibility(bool visible) { - if (m_visible == visible) { - return; - } - - m_visible = visible; - - if (m_nvnflinger) { - m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible); - } -} - -Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, - s32* out_fbshare_layer_index) { - // TODO - R_SUCCEED(); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/system_buffer_manager.h b/src/core/hle/service/am/system_buffer_manager.h deleted file mode 100644 index 98c3cf055..000000000 --- a/src/core/hle/service/am/system_buffer_manager.h +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <set> - -#include "common/common_funcs.h" -#include "common/common_types.h" - -#include "core/hle/service/am/am_types.h" - -namespace Kernel { -class KProcess; -} - -namespace Service::Nvnflinger { -class Nvnflinger; -} - -union Result; - -namespace Service::AM { - -class SystemBufferManager { -public: - SystemBufferManager(); - ~SystemBufferManager(); - - bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id); - - void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id, - u64* out_system_shared_layer_id) { - *out_system_shared_buffer_id = m_system_shared_buffer_id; - *out_system_shared_layer_id = m_system_shared_layer_id; - } - - void SetWindowVisibility(bool visible); - - Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index); - -private: - Kernel::KProcess* m_process{}; - Nvnflinger::Nvnflinger* m_nvnflinger{}; - bool m_buffer_sharing_enabled{}; - bool m_visible{true}; - u64 m_system_shared_buffer_id{}; - u64 m_system_shared_layer_id{}; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/window_controller.cpp b/src/core/hle/service/am/window_controller.cpp deleted file mode 100644 index f00957f83..000000000 --- a/src/core/hle/service/am/window_controller.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/am/applet.h" -#include "core/hle/service/am/window_controller.h" -#include "core/hle/service/ipc_helpers.h" - -namespace Service::AM { - -IWindowController::IWindowController(Core::System& system_, std::shared_ptr<Applet> applet_) - : ServiceFramework{system_, "IWindowController"}, applet{std::move(applet_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "CreateWindow"}, - {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, - {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"}, - {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, - {11, nullptr, "ReleaseForegroundRights"}, - {12, nullptr, "RejectToChangeIntoBackground"}, - {20, &IWindowController::SetAppletWindowVisibility, "SetAppletWindowVisibility"}, - {21, &IWindowController::SetAppletGpuTimeSlice, "SetAppletGpuTimeSlice"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IWindowController::~IWindowController() = default; - -void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(applet->aruid); -} - -void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) { - u64 aruid = 0; - if (auto caller = applet->caller_applet.lock(); caller) { - aruid = caller->aruid; - } - - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(aruid); -} - -void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IWindowController::SetAppletWindowVisibility(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - IPC::RequestParser rp{ctx}; - const bool visible = rp.Pop<bool>(); - - applet->system_buffer_manager.SetWindowVisibility(visible); - applet->hid_registration.EnableAppletToGetInput(visible); - - if (visible) { - applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); - applet->focus_state = FocusState::InFocus; - } else { - applet->focus_state = FocusState::NotInFocus; - } - applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IWindowController::SetAppletGpuTimeSlice(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto time_slice = rp.Pop<s64>(); - - LOG_WARNING(Service_AM, "(STUBBED) called, time_slice={}", time_slice); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/window_controller.h b/src/core/hle/service/am/window_controller.h deleted file mode 100644 index a28219abe..000000000 --- a/src/core/hle/service/am/window_controller.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -struct Applet; - -class IWindowController final : public ServiceFramework<IWindowController> { -public: - explicit IWindowController(Core::System& system_, std::shared_ptr<Applet> applet_); - ~IWindowController() override; - -private: - void GetAppletResourceUserId(HLERequestContext& ctx); - void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx); - void AcquireForegroundRights(HLERequestContext& ctx); - void SetAppletWindowVisibility(HLERequestContext& ctx); - void SetAppletGpuTimeSlice(HLERequestContext& ctx); - - const std::shared_ptr<Applet> applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp deleted file mode 100644 index 3101cf447..000000000 --- a/src/core/hle/service/audio/audctl.cpp +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/hle/service/audio/audctl.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/set/system_settings_server.h" -#include "core/hle/service/sm/sm.h" - -namespace Service::Audio { - -AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetTargetVolume"}, - {1, nullptr, "SetTargetVolume"}, - {2, &AudCtl::GetTargetVolumeMin, "GetTargetVolumeMin"}, - {3, &AudCtl::GetTargetVolumeMax, "GetTargetVolumeMax"}, - {4, nullptr, "IsTargetMute"}, - {5, nullptr, "SetTargetMute"}, - {6, nullptr, "IsTargetConnected"}, - {7, nullptr, "SetDefaultTarget"}, - {8, nullptr, "GetDefaultTarget"}, - {9, &AudCtl::GetAudioOutputMode, "GetAudioOutputMode"}, - {10, &AudCtl::SetAudioOutputMode, "SetAudioOutputMode"}, - {11, nullptr, "SetForceMutePolicy"}, - {12, &AudCtl::GetForceMutePolicy, "GetForceMutePolicy"}, - {13, &AudCtl::GetOutputModeSetting, "GetOutputModeSetting"}, - {14, &AudCtl::SetOutputModeSetting, "SetOutputModeSetting"}, - {15, nullptr, "SetOutputTarget"}, - {16, nullptr, "SetInputTargetForceEnabled"}, - {17, &AudCtl::SetHeadphoneOutputLevelMode, "SetHeadphoneOutputLevelMode"}, - {18, &AudCtl::GetHeadphoneOutputLevelMode, "GetHeadphoneOutputLevelMode"}, - {19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"}, - {20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"}, - {21, nullptr, "GetAudioOutputTargetForPlayReport"}, - {22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"}, - {23, nullptr, "SetSystemOutputMasterVolume"}, - {24, nullptr, "GetSystemOutputMasterVolume"}, - {25, nullptr, "GetAudioVolumeDataForPlayReport"}, - {26, nullptr, "UpdateHeadphoneSettings"}, - {27, nullptr, "SetVolumeMappingTableForDev"}, - {28, nullptr, "GetAudioOutputChannelCountForPlayReport"}, - {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, - {30, &AudCtl::SetSpeakerAutoMuteEnabled, "SetSpeakerAutoMuteEnabled"}, - {31, &AudCtl::IsSpeakerAutoMuteEnabled, "IsSpeakerAutoMuteEnabled"}, - {32, nullptr, "GetActiveOutputTarget"}, - {33, nullptr, "GetTargetDeviceInfo"}, - {34, nullptr, "AcquireTargetNotification"}, - {35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"}, - {36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"}, - {37, nullptr, "SetHearingProtectionSafeguardEnabled"}, - {38, nullptr, "IsHearingProtectionSafeguardEnabled"}, - {39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"}, - {40, nullptr, "GetSystemInformationForDebug"}, - {41, nullptr, "SetVolumeButtonLongPressTime"}, - {42, nullptr, "SetNativeVolumeForDebug"}, - {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"}, - {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"}, - {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"}, - {10100, nullptr, "GetAudioVolumeDataForPlayReport"}, - {10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"}, - {10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"}, - {10103, nullptr, "GetAudioOutputTargetForPlayReport"}, - {10104, nullptr, "GetAudioOutputChannelCountForPlayReport"}, - {10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, - {10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"}, - {50000, nullptr, "SetAnalogInputBoostGainForPrototyping"}, - }; - // clang-format on - - RegisterHandlers(functions); - - m_set_sys = - system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); -} - -AudCtl::~AudCtl() = default; - -void AudCtl::GetTargetVolumeMin(HLERequestContext& ctx) { - LOG_DEBUG(Audio, "called."); - - // This service function is currently hardcoded on the - // actual console to this value (as of 8.0.0). - constexpr s32 target_min_volume = 0; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(target_min_volume); -} - -void AudCtl::GetTargetVolumeMax(HLERequestContext& ctx) { - LOG_DEBUG(Audio, "called."); - - // This service function is currently hardcoded on the - // actual console to this value (as of 8.0.0). - constexpr s32 target_max_volume = 15; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(target_max_volume); -} - -void AudCtl::GetAudioOutputMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()}; - - Set::AudioOutputMode output_mode{}; - const auto result = m_set_sys->GetAudioOutputMode(output_mode, target); - - LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.PushEnum(output_mode); -} - -void AudCtl::SetAudioOutputMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()}; - const auto output_mode{rp.PopEnum<Set::AudioOutputMode>()}; - - const auto result = m_set_sys->SetAudioOutputMode(target, output_mode); - - LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void AudCtl::GetForceMutePolicy(HLERequestContext& ctx) { - LOG_WARNING(Audio, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(ForceMutePolicy::Disable); -} - -void AudCtl::GetOutputModeSetting(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()}; - - LOG_WARNING(Audio, "(STUBBED) called, target={}", target); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(Set::AudioOutputMode::ch_7_1); -} - -void AudCtl::SetOutputModeSetting(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()}; - const auto output_mode{rp.PopEnum<Set::AudioOutputMode>()}; - - LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void AudCtl::SetHeadphoneOutputLevelMode(HLERequestContext& ctx) { - LOG_WARNING(Audio, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void AudCtl::GetHeadphoneOutputLevelMode(HLERequestContext& ctx) { - LOG_WARNING(Audio, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(HeadphoneOutputLevelMode::Normal); -} - -void AudCtl::SetSpeakerAutoMuteEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto is_speaker_auto_mute_enabled{rp.Pop<bool>()}; - - LOG_WARNING(Audio, "(STUBBED) called, is_speaker_auto_mute_enabled={}", - is_speaker_auto_mute_enabled); - - const auto result = m_set_sys->SetSpeakerAutoMuteFlag(is_speaker_auto_mute_enabled); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void AudCtl::IsSpeakerAutoMuteEnabled(HLERequestContext& ctx) { - bool is_speaker_auto_mute_enabled{}; - const auto result = m_set_sys->GetSpeakerAutoMuteFlag(is_speaker_auto_mute_enabled); - - LOG_WARNING(Audio, "(STUBBED) called, is_speaker_auto_mute_enabled={}", - is_speaker_auto_mute_enabled); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push<u8>(is_speaker_auto_mute_enabled); -} - -} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h deleted file mode 100644 index 4c90ead70..000000000 --- a/src/core/hle/service/audio/audctl.h +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Core { -class System; -} - -namespace Service::Set { -class ISystemSettingsServer; -} - -namespace Service::Audio { - -class AudCtl final : public ServiceFramework<AudCtl> { -public: - explicit AudCtl(Core::System& system_); - ~AudCtl() override; - -private: - enum class ForceMutePolicy { - Disable, - SpeakerMuteOnHeadphoneUnplugged, - }; - - enum class HeadphoneOutputLevelMode { - Normal, - HighPower, - }; - - void GetTargetVolumeMin(HLERequestContext& ctx); - void GetTargetVolumeMax(HLERequestContext& ctx); - void GetAudioOutputMode(HLERequestContext& ctx); - void SetAudioOutputMode(HLERequestContext& ctx); - void GetForceMutePolicy(HLERequestContext& ctx); - void GetOutputModeSetting(HLERequestContext& ctx); - void SetOutputModeSetting(HLERequestContext& ctx); - void SetHeadphoneOutputLevelMode(HLERequestContext& ctx); - void GetHeadphoneOutputLevelMode(HLERequestContext& ctx); - void SetSpeakerAutoMuteEnabled(HLERequestContext& ctx); - void IsSpeakerAutoMuteEnabled(HLERequestContext& ctx); - void AcquireTargetNotification(HLERequestContext& ctx); - - std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; -}; - -} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index dccd16309..44af030eb 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -2,9 +2,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" -#include "core/hle/service/audio/audctl.h" #include "core/hle/service/audio/audin_u.h" #include "core/hle/service/audio/audio.h" +#include "core/hle/service/audio/audio_controller.h" #include "core/hle/service/audio/audout_u.h" #include "core/hle/service/audio/audrec_a.h" #include "core/hle/service/audio/audrec_u.h" @@ -18,7 +18,7 @@ namespace Service::Audio { void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); - server_manager->RegisterNamedService("audctl", std::make_shared<AudCtl>(system)); + server_manager->RegisterNamedService("audctl", std::make_shared<IAudioController>(system)); server_manager->RegisterNamedService("audout:u", std::make_shared<AudOutU>(system)); server_manager->RegisterNamedService("audin:u", std::make_shared<AudInU>(system)); server_manager->RegisterNamedService("audrec:a", std::make_shared<AudRecA>(system)); diff --git a/src/core/hle/service/audio/audio_controller.cpp b/src/core/hle/service/audio/audio_controller.cpp new file mode 100644 index 000000000..a6da66d0f --- /dev/null +++ b/src/core/hle/service/audio/audio_controller.cpp @@ -0,0 +1,174 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/hle/service/audio/audio_controller.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Audio { + +IAudioController::IAudioController(Core::System& system_) + : ServiceFramework{system_, "audctl"}, service_context{system, "audctl"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetTargetVolume"}, + {1, nullptr, "SetTargetVolume"}, + {2, C<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"}, + {3, C<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"}, + {4, nullptr, "IsTargetMute"}, + {5, nullptr, "SetTargetMute"}, + {6, nullptr, "IsTargetConnected"}, + {7, nullptr, "SetDefaultTarget"}, + {8, nullptr, "GetDefaultTarget"}, + {9, C<&IAudioController::GetAudioOutputMode>, "GetAudioOutputMode"}, + {10, C<&IAudioController::SetAudioOutputMode>, "SetAudioOutputMode"}, + {11, nullptr, "SetForceMutePolicy"}, + {12, C<&IAudioController::GetForceMutePolicy>, "GetForceMutePolicy"}, + {13, C<&IAudioController::GetOutputModeSetting>, "GetOutputModeSetting"}, + {14, C<&IAudioController::SetOutputModeSetting>, "SetOutputModeSetting"}, + {15, nullptr, "SetOutputTarget"}, + {16, nullptr, "SetInputTargetForceEnabled"}, + {17, C<&IAudioController::SetHeadphoneOutputLevelMode>, "SetHeadphoneOutputLevelMode"}, + {18, C<&IAudioController::GetHeadphoneOutputLevelMode>, "GetHeadphoneOutputLevelMode"}, + {19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"}, + {20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"}, + {21, nullptr, "GetAudioOutputTargetForPlayReport"}, + {22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"}, + {23, nullptr, "SetSystemOutputMasterVolume"}, + {24, nullptr, "GetSystemOutputMasterVolume"}, + {25, nullptr, "GetAudioVolumeDataForPlayReport"}, + {26, nullptr, "UpdateHeadphoneSettings"}, + {27, nullptr, "SetVolumeMappingTableForDev"}, + {28, nullptr, "GetAudioOutputChannelCountForPlayReport"}, + {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, + {30, C<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"}, + {31, C<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"}, + {32, nullptr, "GetActiveOutputTarget"}, + {33, nullptr, "GetTargetDeviceInfo"}, + {34, C<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"}, + {35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"}, + {36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"}, + {37, nullptr, "SetHearingProtectionSafeguardEnabled"}, + {38, nullptr, "IsHearingProtectionSafeguardEnabled"}, + {39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"}, + {40, nullptr, "GetSystemInformationForDebug"}, + {41, nullptr, "SetVolumeButtonLongPressTime"}, + {42, nullptr, "SetNativeVolumeForDebug"}, + {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"}, + {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"}, + {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"}, + {10100, nullptr, "GetAudioVolumeDataForPlayReport"}, + {10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"}, + {10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"}, + {10103, nullptr, "GetAudioOutputTargetForPlayReport"}, + {10104, nullptr, "GetAudioOutputChannelCountForPlayReport"}, + {10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, + {10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"}, + {50000, nullptr, "SetAnalogInputBoostGainForPrototyping"}, + }; + // clang-format on + + RegisterHandlers(functions); + + m_set_sys = + system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); + notification_event = service_context.CreateEvent("IAudioController:NotificationEvent"); +} + +IAudioController::~IAudioController() { + service_context.CloseEvent(notification_event); +}; + +Result IAudioController::GetTargetVolumeMin(Out<s32> out_target_min_volume) { + LOG_DEBUG(Audio, "called."); + + // This service function is currently hardcoded on the + // actual console to this value (as of 8.0.0). + *out_target_min_volume = 0; + R_SUCCEED(); +} + +Result IAudioController::GetTargetVolumeMax(Out<s32> out_target_max_volume) { + LOG_DEBUG(Audio, "called."); + + // This service function is currently hardcoded on the + // actual console to this value (as of 8.0.0). + *out_target_max_volume = 15; + R_SUCCEED(); +} + +Result IAudioController::GetAudioOutputMode(Out<Set::AudioOutputMode> out_output_mode, + Set::AudioOutputModeTarget target) { + const auto result = m_set_sys->GetAudioOutputMode(out_output_mode, target); + + LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, *out_output_mode); + R_RETURN(result); +} + +Result IAudioController::SetAudioOutputMode(Set::AudioOutputModeTarget target, + Set::AudioOutputMode output_mode) { + LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode); + + R_RETURN(m_set_sys->SetAudioOutputMode(target, output_mode)); +} + +Result IAudioController::GetForceMutePolicy(Out<ForceMutePolicy> out_mute_policy) { + LOG_WARNING(Audio, "(STUBBED) called"); + + // Removed on FW 13.2.1+ + *out_mute_policy = ForceMutePolicy::Disable; + R_SUCCEED(); +} + +Result IAudioController::GetOutputModeSetting(Out<Set::AudioOutputMode> out_output_mode, + Set::AudioOutputModeTarget target) { + LOG_WARNING(Audio, "(STUBBED) called, target={}", target); + + *out_output_mode = Set::AudioOutputMode::ch_7_1; + R_SUCCEED(); +} + +Result IAudioController::SetOutputModeSetting(Set::AudioOutputModeTarget target, + Set::AudioOutputMode output_mode) { + LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode); + R_SUCCEED(); +} + +Result IAudioController::SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode) { + LOG_WARNING(Audio, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IAudioController::GetHeadphoneOutputLevelMode( + Out<HeadphoneOutputLevelMode> out_output_level_mode) { + LOG_INFO(Audio, "called"); + + *out_output_level_mode = HeadphoneOutputLevelMode::Normal; + R_SUCCEED(); +} + +Result IAudioController::SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled) { + LOG_INFO(Audio, "called, is_speaker_auto_mute_enabled={}", is_speaker_auto_mute_enabled); + + R_RETURN(m_set_sys->SetSpeakerAutoMuteFlag(is_speaker_auto_mute_enabled)); +} + +Result IAudioController::IsSpeakerAutoMuteEnabled(Out<bool> out_is_speaker_auto_mute_enabled) { + const auto result = m_set_sys->GetSpeakerAutoMuteFlag(out_is_speaker_auto_mute_enabled); + + LOG_INFO(Audio, "called, is_speaker_auto_mute_enabled={}", *out_is_speaker_auto_mute_enabled); + R_RETURN(result); +} + +Result IAudioController::AcquireTargetNotification( + OutCopyHandle<Kernel::KReadableEvent> out_notification_event) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + *out_notification_event = ¬ification_event->GetReadableEvent(); + R_SUCCEED(); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio_controller.h b/src/core/hle/service/audio/audio_controller.h new file mode 100644 index 000000000..9e8514373 --- /dev/null +++ b/src/core/hle/service/audio/audio_controller.h @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" +#include "core/hle/service/set/settings_types.h" + +namespace Core { +class System; +} + +namespace Service::Set { +class ISystemSettingsServer; +} + +namespace Service::Audio { + +class IAudioController final : public ServiceFramework<IAudioController> { +public: + explicit IAudioController(Core::System& system_); + ~IAudioController() override; + +private: + enum class ForceMutePolicy { + Disable, + SpeakerMuteOnHeadphoneUnplugged, + }; + + enum class HeadphoneOutputLevelMode { + Normal, + HighPower, + }; + + Result GetTargetVolumeMin(Out<s32> out_target_min_volume); + Result GetTargetVolumeMax(Out<s32> out_target_max_volume); + Result GetAudioOutputMode(Out<Set::AudioOutputMode> out_output_mode, + Set::AudioOutputModeTarget target); + Result SetAudioOutputMode(Set::AudioOutputModeTarget target, Set::AudioOutputMode output_mode); + Result GetForceMutePolicy(Out<ForceMutePolicy> out_mute_policy); + Result GetOutputModeSetting(Out<Set::AudioOutputMode> out_output_mode, + Set::AudioOutputModeTarget target); + Result SetOutputModeSetting(Set::AudioOutputModeTarget target, + Set::AudioOutputMode output_mode); + Result SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode); + Result GetHeadphoneOutputLevelMode(Out<HeadphoneOutputLevelMode> out_output_level_mode); + Result SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled); + Result IsSpeakerAutoMuteEnabled(Out<bool> out_is_speaker_auto_mute_enabled); + Result AcquireTargetNotification(OutCopyHandle<Kernel::KReadableEvent> out_notification_event); + + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* notification_event; + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp index 5be167fce..ed393f7a2 100644 --- a/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp +++ b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp @@ -12,7 +12,7 @@ INewlyArrivedEventHolder::INewlyArrivedEventHolder(Core::System& system_) "INewlyArrivedEventHolder"} { // clang-format off static const FunctionInfo functions[] = { - {0, C<&INewlyArrivedEventHolder::Get>, "Get"}, + {0, D<&INewlyArrivedEventHolder::Get>, "Get"}, }; // clang-format on diff --git a/src/core/hle/service/bcat/news/news_database_service.cpp b/src/core/hle/service/bcat/news/news_database_service.cpp index 18109f9b0..b94ef0636 100644 --- a/src/core/hle/service/bcat/news/news_database_service.cpp +++ b/src/core/hle/service/bcat/news/news_database_service.cpp @@ -11,12 +11,12 @@ INewsDatabaseService::INewsDatabaseService(Core::System& system_) // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetListV1"}, - {1, C<&INewsDatabaseService::Count>, "Count"}, + {1, D<&INewsDatabaseService::Count>, "Count"}, {2, nullptr, "CountWithKey"}, {3, nullptr, "UpdateIntegerValue"}, - {4, nullptr, "UpdateIntegerValueWithAddition"}, + {4, D<&INewsDatabaseService::UpdateIntegerValueWithAddition>, "UpdateIntegerValueWithAddition"}, {5, nullptr, "UpdateStringValue"}, - {1000, nullptr, "GetList"}, + {1000, D<&INewsDatabaseService::GetList>, "GetList"}, }; // clang-format on @@ -32,4 +32,22 @@ Result INewsDatabaseService::Count(Out<s32> out_count, R_SUCCEED(); } +Result INewsDatabaseService::UpdateIntegerValueWithAddition( + u32 value, InBuffer<BufferAttr_HipcPointer> buffer_data_1, + InBuffer<BufferAttr_HipcPointer> buffer_data_2) { + LOG_WARNING(Service_BCAT, "(STUBBED) called, value={}, buffer_size_1={}, buffer_data_2={}", + value, buffer_data_1.size(), buffer_data_2.size()); + R_SUCCEED(); +} + +Result INewsDatabaseService::GetList(Out<s32> out_count, u32 value, + OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data, + InBuffer<BufferAttr_HipcPointer> buffer_data_1, + InBuffer<BufferAttr_HipcPointer> buffer_data_2) { + LOG_WARNING(Service_BCAT, "(STUBBED) called, value={}, buffer_size_1={}, buffer_data_2={}", + value, buffer_data_1.size(), buffer_data_2.size()); + *out_count = 0; + R_SUCCEED(); +} + } // namespace Service::News diff --git a/src/core/hle/service/bcat/news/news_database_service.h b/src/core/hle/service/bcat/news/news_database_service.h index f5916634b..860b7074c 100644 --- a/src/core/hle/service/bcat/news/news_database_service.h +++ b/src/core/hle/service/bcat/news/news_database_service.h @@ -19,6 +19,14 @@ public: private: Result Count(Out<s32> out_count, InBuffer<BufferAttr_HipcPointer> buffer_data); + + Result UpdateIntegerValueWithAddition(u32 value, InBuffer<BufferAttr_HipcPointer> buffer_data_1, + InBuffer<BufferAttr_HipcPointer> buffer_data_2); + + Result GetList(Out<s32> out_count, u32 value, + OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data, + InBuffer<BufferAttr_HipcPointer> buffer_data_1, + InBuffer<BufferAttr_HipcPointer> buffer_data_2); }; } // namespace Service::News diff --git a/src/core/hle/service/bcat/news/news_service.cpp b/src/core/hle/service/bcat/news/news_service.cpp index e19cea7b5..bc6c2afd2 100644 --- a/src/core/hle/service/bcat/news/news_service.cpp +++ b/src/core/hle/service/bcat/news/news_service.cpp @@ -11,10 +11,10 @@ INewsService::INewsService(Core::System& system_) : ServiceFramework{system_, "I static const FunctionInfo functions[] = { {10100, nullptr, "PostLocalNews"}, {20100, nullptr, "SetPassphrase"}, - {30100, C<&INewsService::GetSubscriptionStatus>, "GetSubscriptionStatus"}, + {30100, D<&INewsService::GetSubscriptionStatus>, "GetSubscriptionStatus"}, {30101, nullptr, "GetTopicList"}, {30110, nullptr, "Unknown30110"}, - {30200, nullptr, "IsSystemUpdateRequired"}, + {30200, D<&INewsService::IsSystemUpdateRequired>, "IsSystemUpdateRequired"}, {30201, nullptr, "Unknown30201"}, {30210, nullptr, "Unknown30210"}, {30300, nullptr, "RequestImmediateReception"}, @@ -24,7 +24,7 @@ INewsService::INewsService(Core::System& system_) : ServiceFramework{system_, "I {30901, nullptr, "Unknown30901"}, {30902, nullptr, "Unknown30902"}, {40100, nullptr, "SetSubscriptionStatus"}, - {40101, nullptr, "RequestAutoSubscription"}, + {40101, D<&INewsService::RequestAutoSubscription>, "RequestAutoSubscription"}, {40200, nullptr, "ClearStorage"}, {40201, nullptr, "ClearSubscriptionStatusAll"}, {90100, nullptr, "GetNewsDatabaseDump"}, @@ -43,4 +43,15 @@ Result INewsService::GetSubscriptionStatus(Out<u32> out_status, R_SUCCEED(); } +Result INewsService::IsSystemUpdateRequired(Out<bool> out_is_system_update_required) { + LOG_WARNING(Service_BCAT, "(STUBBED) called"); + *out_is_system_update_required = false; + R_SUCCEED(); +} + +Result INewsService::RequestAutoSubscription(u64 value) { + LOG_WARNING(Service_BCAT, "(STUBBED) called"); + R_SUCCEED(); +} + } // namespace Service::News diff --git a/src/core/hle/service/bcat/news/news_service.h b/src/core/hle/service/bcat/news/news_service.h index 8d06be9d6..f1716a302 100644 --- a/src/core/hle/service/bcat/news/news_service.h +++ b/src/core/hle/service/bcat/news/news_service.h @@ -19,6 +19,10 @@ public: private: Result GetSubscriptionStatus(Out<u32> out_status, InBuffer<BufferAttr_HipcPointer> buffer_data); + + Result IsSystemUpdateRequired(Out<bool> out_is_system_update_required); + + Result RequestAutoSubscription(u64 value); }; } // namespace Service::News diff --git a/src/core/hle/service/bcat/news/overwrite_event_holder.cpp b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp index c32a5ca8f..1712971e4 100644 --- a/src/core/hle/service/bcat/news/overwrite_event_holder.cpp +++ b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp @@ -11,7 +11,7 @@ IOverwriteEventHolder::IOverwriteEventHolder(Core::System& system_) "IOverwriteEventHolder"} { // clang-format off static const FunctionInfo functions[] = { - {0, C<&IOverwriteEventHolder::Get>, "Get"}, + {0, D<&IOverwriteEventHolder::Get>, "Get"}, }; // clang-format on diff --git a/src/core/hle/service/bcat/news/service_creator.cpp b/src/core/hle/service/bcat/news/service_creator.cpp index d5ba5dff7..a1b22c004 100644 --- a/src/core/hle/service/bcat/news/service_creator.cpp +++ b/src/core/hle/service/bcat/news/service_creator.cpp @@ -15,11 +15,11 @@ IServiceCreator::IServiceCreator(Core::System& system_, u32 permissions_, const : ServiceFramework{system_, name_}, permissions{permissions_} { // clang-format off static const FunctionInfo functions[] = { - {0, C<&IServiceCreator::CreateNewsService>, "CreateNewsService"}, - {1, C<&IServiceCreator::CreateNewlyArrivedEventHolder>, "CreateNewlyArrivedEventHolder"}, - {2, C<&IServiceCreator::CreateNewsDataService>, "CreateNewsDataService"}, - {3, C<&IServiceCreator::CreateNewsDatabaseService>, "CreateNewsDatabaseService"}, - {4, C<&IServiceCreator::CreateOverwriteEventHolder>, "CreateOverwriteEventHolder"}, + {0, D<&IServiceCreator::CreateNewsService>, "CreateNewsService"}, + {1, D<&IServiceCreator::CreateNewlyArrivedEventHolder>, "CreateNewlyArrivedEventHolder"}, + {2, D<&IServiceCreator::CreateNewsDataService>, "CreateNewsDataService"}, + {3, D<&IServiceCreator::CreateNewsDatabaseService>, "CreateNewsDatabaseService"}, + {4, D<&IServiceCreator::CreateOverwriteEventHolder>, "CreateOverwriteEventHolder"}, }; // clang-format on diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 2dc23e674..d120dade8 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -3,141 +3,18 @@ #include <memory> -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hle/kernel/k_event.h" #include "core/hle/service/btm/btm.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/btm/btm_debug.h" +#include "core/hle/service/btm/btm_system.h" +#include "core/hle/service/btm/btm_user.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" namespace Service::BTM { -class IBtmUserCore final : public ServiceFramework<IBtmUserCore> { +class IBtm final : public ServiceFramework<IBtm> { public: - explicit IBtmUserCore(Core::System& system_) - : ServiceFramework{system_, "IBtmUserCore"}, service_context{system_, "IBtmUserCore"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"}, - {1, nullptr, "GetBleScanFilterParameter"}, - {2, nullptr, "GetBleScanFilterParameter2"}, - {3, nullptr, "StartBleScanForGeneral"}, - {4, nullptr, "StopBleScanForGeneral"}, - {5, nullptr, "GetBleScanResultsForGeneral"}, - {6, nullptr, "StartBleScanForPaired"}, - {7, nullptr, "StopBleScanForPaired"}, - {8, nullptr, "StartBleScanForSmartDevice"}, - {9, nullptr, "StopBleScanForSmartDevice"}, - {10, nullptr, "GetBleScanResultsForSmartDevice"}, - {17, &IBtmUserCore::AcquireBleConnectionEvent, "AcquireBleConnectionEvent"}, - {18, nullptr, "BleConnect"}, - {19, nullptr, "BleDisconnect"}, - {20, nullptr, "BleGetConnectionState"}, - {21, nullptr, "AcquireBlePairingEvent"}, - {22, nullptr, "BlePairDevice"}, - {23, nullptr, "BleUnPairDevice"}, - {24, nullptr, "BleUnPairDevice2"}, - {25, nullptr, "BleGetPairedDevices"}, - {26, &IBtmUserCore::AcquireBleServiceDiscoveryEvent, "AcquireBleServiceDiscoveryEvent"}, - {27, nullptr, "GetGattServices"}, - {28, nullptr, "GetGattService"}, - {29, nullptr, "GetGattIncludedServices"}, - {30, nullptr, "GetBelongingGattService"}, - {31, nullptr, "GetGattCharacteristics"}, - {32, nullptr, "GetGattDescriptors"}, - {33, &IBtmUserCore::AcquireBleMtuConfigEvent, "AcquireBleMtuConfigEvent"}, - {34, nullptr, "ConfigureBleMtu"}, - {35, nullptr, "GetBleMtu"}, - {36, nullptr, "RegisterBleGattDataPath"}, - {37, nullptr, "UnregisterBleGattDataPath"}, - }; - // clang-format on - RegisterHandlers(functions); - - scan_event = service_context.CreateEvent("IBtmUserCore:ScanEvent"); - connection_event = service_context.CreateEvent("IBtmUserCore:ConnectionEvent"); - service_discovery_event = service_context.CreateEvent("IBtmUserCore:DiscoveryEvent"); - config_event = service_context.CreateEvent("IBtmUserCore:ConfigEvent"); - } - - ~IBtmUserCore() override { - service_context.CloseEvent(scan_event); - service_context.CloseEvent(connection_event); - service_context.CloseEvent(service_discovery_event); - service_context.CloseEvent(config_event); - } - -private: - void AcquireBleScanEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3, 1}; - rb.Push(ResultSuccess); - rb.Push(true); - rb.PushCopyObjects(scan_event->GetReadableEvent()); - } - - void AcquireBleConnectionEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3, 1}; - rb.Push(ResultSuccess); - rb.Push(true); - rb.PushCopyObjects(connection_event->GetReadableEvent()); - } - - void AcquireBleServiceDiscoveryEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3, 1}; - rb.Push(ResultSuccess); - rb.Push(true); - rb.PushCopyObjects(service_discovery_event->GetReadableEvent()); - } - - void AcquireBleMtuConfigEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3, 1}; - rb.Push(ResultSuccess); - rb.Push(true); - rb.PushCopyObjects(config_event->GetReadableEvent()); - } - - KernelHelpers::ServiceContext service_context; - - Kernel::KEvent* scan_event; - Kernel::KEvent* connection_event; - Kernel::KEvent* service_discovery_event; - Kernel::KEvent* config_event; -}; - -class BTM_USR final : public ServiceFramework<BTM_USR> { -public: - explicit BTM_USR(Core::System& system_) : ServiceFramework{system_, "btm:u"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &BTM_USR::GetCore, "GetCore"}, - }; - // clang-format on - RegisterHandlers(functions); - } - -private: - void GetCore(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IBtmUserCore>(system); - } -}; - -class BTM final : public ServiceFramework<BTM> { -public: - explicit BTM(Core::System& system_) : ServiceFramework{system_, "btm"} { + explicit IBtm(Core::System& system_) : ServiceFramework{system_, "btm"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetState"}, @@ -232,144 +109,13 @@ public: } }; -class BTM_DBG final : public ServiceFramework<BTM_DBG> { -public: - explicit BTM_DBG(Core::System& system_) : ServiceFramework{system_, "btm:dbg"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "AcquireDiscoveryEvent"}, - {1, nullptr, "StartDiscovery"}, - {2, nullptr, "CancelDiscovery"}, - {3, nullptr, "GetDeviceProperty"}, - {4, nullptr, "CreateBond"}, - {5, nullptr, "CancelBond"}, - {6, nullptr, "SetTsiMode"}, - {7, nullptr, "GeneralTest"}, - {8, nullptr, "HidConnect"}, - {9, nullptr, "GeneralGet"}, - {10, nullptr, "GetGattClientDisconnectionReason"}, - {11, nullptr, "GetBleConnectionParameter"}, - {12, nullptr, "GetBleConnectionParameterRequest"}, - {13, nullptr, "Unknown13"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - -class IBtmSystemCore final : public ServiceFramework<IBtmSystemCore> { -public: - explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"}, - {1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"}, - {2, nullptr, "ClearGamepadPairingDatabase"}, - {3, nullptr, "GetPairedGamepadCount"}, - {4, nullptr, "EnableRadio"}, - {5, nullptr, "DisableRadio"}, - {6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"}, - {7, nullptr, "AcquireRadioEvent"}, - {8, nullptr, "AcquireGamepadPairingEvent"}, - {9, nullptr, "IsGamepadPairingStarted"}, - {10, nullptr, "StartAudioDeviceDiscovery"}, - {11, nullptr, "StopAudioDeviceDiscovery"}, - {12, nullptr, "IsDiscoveryingAudioDevice"}, - {13, nullptr, "GetDiscoveredAudioDevice"}, - {14, nullptr, "AcquireAudioDeviceConnectionEvent"}, - {15, nullptr, "ConnectAudioDevice"}, - {16, nullptr, "IsConnectingAudioDevice"}, - {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"}, - {18, nullptr, "DisconnectAudioDevice"}, - {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, - {20, &IBtmSystemCore::GetPairedAudioDevices, "GetPairedAudioDevices"}, - {21, nullptr, "RemoveAudioDevicePairing"}, - {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"}, - {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"} - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void IsRadioEnabled(HLERequestContext& ctx) { - LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(true); - } - - void StartGamepadPairing(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void CancelGamepadPairing(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetConnectedAudioDevices(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(0); - } - - void GetPairedAudioDevices(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(0); - } - - void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } -}; - -class BTM_SYS final : public ServiceFramework<BTM_SYS> { -public: - explicit BTM_SYS(Core::System& system_) : ServiceFramework{system_, "btm:sys"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &BTM_SYS::GetCore, "GetCore"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void GetCore(HLERequestContext& ctx) { - LOG_WARNING(Service_BTM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IBtmSystemCore>(system); - } -}; - void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); - server_manager->RegisterNamedService("btm", std::make_shared<BTM>(system)); - server_manager->RegisterNamedService("btm:dbg", std::make_shared<BTM_DBG>(system)); - server_manager->RegisterNamedService("btm:sys", std::make_shared<BTM_SYS>(system)); - server_manager->RegisterNamedService("btm:u", std::make_shared<BTM_USR>(system)); + server_manager->RegisterNamedService("btm", std::make_shared<IBtm>(system)); + server_manager->RegisterNamedService("btm:dbg", std::make_shared<IBtmDebug>(system)); + server_manager->RegisterNamedService("btm:sys", std::make_shared<IBtmSystem>(system)); + server_manager->RegisterNamedService("btm:u", std::make_shared<IBtmUser>(system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h index a99b34364..0bf77d053 100644 --- a/src/core/hle/service/btm/btm.h +++ b/src/core/hle/service/btm/btm.h @@ -3,10 +3,6 @@ #pragma once -namespace Service::SM { -class ServiceManager; -} - namespace Core { class System; }; diff --git a/src/core/hle/service/btm/btm_debug.cpp b/src/core/hle/service/btm/btm_debug.cpp new file mode 100644 index 000000000..4d61d2641 --- /dev/null +++ b/src/core/hle/service/btm/btm_debug.cpp @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/btm/btm_debug.h" + +namespace Service::BTM { + +IBtmDebug::IBtmDebug(Core::System& system_) : ServiceFramework{system_, "btm:dbg"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "AcquireDiscoveryEvent"}, + {1, nullptr, "StartDiscovery"}, + {2, nullptr, "CancelDiscovery"}, + {3, nullptr, "GetDeviceProperty"}, + {4, nullptr, "CreateBond"}, + {5, nullptr, "CancelBond"}, + {6, nullptr, "SetTsiMode"}, + {7, nullptr, "GeneralTest"}, + {8, nullptr, "HidConnect"}, + {9, nullptr, "GeneralGet"}, + {10, nullptr, "GetGattClientDisconnectionReason"}, + {11, nullptr, "GetBleConnectionParameter"}, + {12, nullptr, "GetBleConnectionParameterRequest"}, + {13, nullptr, "Unknown13"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IBtmDebug::~IBtmDebug() = default; + +} // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm_debug.h b/src/core/hle/service/btm/btm_debug.h new file mode 100644 index 000000000..bf4f7e14f --- /dev/null +++ b/src/core/hle/service/btm/btm_debug.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::BTM { + +class IBtmDebug final : public ServiceFramework<IBtmDebug> { +public: + explicit IBtmDebug(Core::System& system_); + ~IBtmDebug() override; +}; + +} // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm_system.cpp b/src/core/hle/service/btm/btm_system.cpp new file mode 100644 index 000000000..99718a7b0 --- /dev/null +++ b/src/core/hle/service/btm/btm_system.cpp @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/logging/log.h" +#include "core/hle/service/btm/btm_system.h" +#include "core/hle/service/btm/btm_system_core.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/service.h" + +namespace Service::BTM { + +IBtmSystem::IBtmSystem(Core::System& system_) : ServiceFramework{system_, "btm:sys"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, C<&IBtmSystem::GetCore>, "GetCore"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IBtmSystem::~IBtmSystem() = default; + +Result IBtmSystem::GetCore(OutInterface<IBtmSystemCore> out_interface) { + LOG_WARNING(Service_BTM, "called"); + + *out_interface = std::make_shared<IBtmSystemCore>(system); + R_SUCCEED(); +} + +} // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm_system.h b/src/core/hle/service/btm/btm_system.h new file mode 100644 index 000000000..fe1c6dbd7 --- /dev/null +++ b/src/core/hle/service/btm/btm_system.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::BTM { +class IBtmSystemCore; + +class IBtmSystem final : public ServiceFramework<IBtmSystem> { +public: + explicit IBtmSystem(Core::System& system_); + ~IBtmSystem() override; + +private: + Result GetCore(OutInterface<IBtmSystemCore> out_interface); +}; + +} // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm_system_core.cpp b/src/core/hle/service/btm/btm_system_core.cpp new file mode 100644 index 000000000..4bc8a9e8b --- /dev/null +++ b/src/core/hle/service/btm/btm_system_core.cpp @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/logging/log.h" +#include "core/hle/service/btm/btm_system_core.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::BTM { + +IBtmSystemCore::IBtmSystemCore(Core::System& system_) + : ServiceFramework{system_, "IBtmSystemCore"}, service_context{system_, "IBtmSystemCore"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, C<&IBtmSystemCore::StartGamepadPairing>, "StartGamepadPairing"}, + {1, C<&IBtmSystemCore::CancelGamepadPairing>, "CancelGamepadPairing"}, + {2, nullptr, "ClearGamepadPairingDatabase"}, + {3, nullptr, "GetPairedGamepadCount"}, + {4, C<&IBtmSystemCore::EnableRadio>, "EnableRadio"}, + {5, C<&IBtmSystemCore::DisableRadio>, "DisableRadio"}, + {6, C<&IBtmSystemCore::IsRadioEnabled>, "IsRadioEnabled"}, + {7, C<&IBtmSystemCore::AcquireRadioEvent>, "AcquireRadioEvent"}, + {8, nullptr, "AcquireGamepadPairingEvent"}, + {9, nullptr, "IsGamepadPairingStarted"}, + {10, nullptr, "StartAudioDeviceDiscovery"}, + {11, nullptr, "StopAudioDeviceDiscovery"}, + {12, nullptr, "IsDiscoveryingAudioDevice"}, + {13, nullptr, "GetDiscoveredAudioDevice"}, + {14, C<&IBtmSystemCore::AcquireAudioDeviceConnectionEvent>, "AcquireAudioDeviceConnectionEvent"}, + {15, nullptr, "ConnectAudioDevice"}, + {16, nullptr, "IsConnectingAudioDevice"}, + {17, C<&IBtmSystemCore::GetConnectedAudioDevices>, "GetConnectedAudioDevices"}, + {18, nullptr, "DisconnectAudioDevice"}, + {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, + {20, C<&IBtmSystemCore::GetPairedAudioDevices>, "GetPairedAudioDevices"}, + {21, nullptr, "RemoveAudioDevicePairing"}, + {22, C<&IBtmSystemCore::RequestAudioDeviceConnectionRejection>, "RequestAudioDeviceConnectionRejection"}, + {23, C<&IBtmSystemCore::CancelAudioDeviceConnectionRejection>, "CancelAudioDeviceConnectionRejection"} + }; + // clang-format on + + RegisterHandlers(functions); + radio_event = service_context.CreateEvent("IBtmSystemCore::RadioEvent"); + audio_device_connection_event = + service_context.CreateEvent("IBtmSystemCore::AudioDeviceConnectionEvent"); + + m_set_sys = + system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); +} + +IBtmSystemCore::~IBtmSystemCore() { + service_context.CloseEvent(radio_event); + service_context.CloseEvent(audio_device_connection_event); +} + +Result IBtmSystemCore::StartGamepadPairing() { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IBtmSystemCore::CancelGamepadPairing() { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IBtmSystemCore::EnableRadio() { + LOG_DEBUG(Service_BTM, "called"); + + R_RETURN(m_set_sys->SetBluetoothEnableFlag(true)); +} +Result IBtmSystemCore::DisableRadio() { + LOG_DEBUG(Service_BTM, "called"); + + R_RETURN(m_set_sys->SetBluetoothEnableFlag(false)); +} + +Result IBtmSystemCore::IsRadioEnabled(Out<bool> out_is_enabled) { + LOG_DEBUG(Service_BTM, "called"); + + R_RETURN(m_set_sys->GetBluetoothEnableFlag(out_is_enabled)); +} + +Result IBtmSystemCore::AcquireRadioEvent(Out<bool> out_is_valid, + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + *out_is_valid = true; + *out_event = &radio_event->GetReadableEvent(); + R_SUCCEED(); +} + +Result IBtmSystemCore::AcquireAudioDeviceConnectionEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + *out_event = &audio_device_connection_event->GetReadableEvent(); + R_SUCCEED(); +} + +Result IBtmSystemCore::GetConnectedAudioDevices( + Out<s32> out_count, OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + *out_count = 0; + R_SUCCEED(); +} + +Result IBtmSystemCore::GetPairedAudioDevices( + Out<s32> out_count, OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + *out_count = 0; + R_SUCCEED(); +} + +Result IBtmSystemCore::RequestAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid) { + LOG_WARNING(Service_BTM, "(STUBBED) called, applet_resource_user_id={}", aruid.pid); + R_SUCCEED(); +} + +Result IBtmSystemCore::CancelAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid) { + LOG_WARNING(Service_BTM, "(STUBBED) called, applet_resource_user_id={}", aruid.pid); + R_SUCCEED(); +} + +} // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm_system_core.h b/src/core/hle/service/btm/btm_system_core.h new file mode 100644 index 000000000..06498b21e --- /dev/null +++ b/src/core/hle/service/btm/btm_system_core.h @@ -0,0 +1,60 @@ +// 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/service.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Core { +class System; +} + +namespace Service::Set { +class ISystemSettingsServer; +} + +namespace Service::BTM { + +class IBtmSystemCore final : public ServiceFramework<IBtmSystemCore> { +public: + explicit IBtmSystemCore(Core::System& system_); + ~IBtmSystemCore() override; + +private: + Result StartGamepadPairing(); + Result CancelGamepadPairing(); + Result EnableRadio(); + Result DisableRadio(); + Result IsRadioEnabled(Out<bool> out_is_enabled); + + Result AcquireRadioEvent(Out<bool> out_is_valid, + OutCopyHandle<Kernel::KReadableEvent> out_event); + + Result AcquireAudioDeviceConnectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + + Result GetConnectedAudioDevices( + Out<s32> out_count, + OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices); + + Result GetPairedAudioDevices( + Out<s32> out_count, + OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices); + + Result RequestAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid); + Result CancelAudioDeviceConnectionRejection(ClientAppletResourceUserId aruid); + + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* radio_event; + Kernel::KEvent* audio_device_connection_event; + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; +}; + +} // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm_user.cpp b/src/core/hle/service/btm/btm_user.cpp new file mode 100644 index 000000000..d2e228f8d --- /dev/null +++ b/src/core/hle/service/btm/btm_user.cpp @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/logging/log.h" +#include "core/hle/service/btm/btm_user.h" +#include "core/hle/service/btm/btm_user_core.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::BTM { + +IBtmUser::IBtmUser(Core::System& system_) : ServiceFramework{system_, "btm:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, C<&IBtmUser::GetCore>, "GetCore"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IBtmUser::~IBtmUser() = default; + +Result IBtmUser::GetCore(OutInterface<IBtmUserCore> out_interface) { + LOG_WARNING(Service_BTM, "called"); + + *out_interface = std::make_shared<IBtmUserCore>(system); + R_SUCCEED(); +} + +} // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm_user.h b/src/core/hle/service/btm/btm_user.h new file mode 100644 index 000000000..d9ee5db45 --- /dev/null +++ b/src/core/hle/service/btm/btm_user.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::BTM { +class IBtmUserCore; + +class IBtmUser final : public ServiceFramework<IBtmUser> { +public: + explicit IBtmUser(Core::System& system_); + ~IBtmUser() override; + +private: + Result GetCore(OutInterface<IBtmUserCore> out_interface); +}; + +} // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm_user_core.cpp b/src/core/hle/service/btm/btm_user_core.cpp new file mode 100644 index 000000000..6f9fa589b --- /dev/null +++ b/src/core/hle/service/btm/btm_user_core.cpp @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <memory> + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/btm/btm_user_core.h" +#include "core/hle/service/cmif_serialization.h" + +namespace Service::BTM { + +IBtmUserCore::IBtmUserCore(Core::System& system_) + : ServiceFramework{system_, "IBtmUserCore"}, service_context{system_, "IBtmUserCore"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, C<&IBtmUserCore::AcquireBleScanEvent>, "AcquireBleScanEvent"}, + {1, nullptr, "GetBleScanFilterParameter"}, + {2, nullptr, "GetBleScanFilterParameter2"}, + {3, nullptr, "StartBleScanForGeneral"}, + {4, nullptr, "StopBleScanForGeneral"}, + {5, nullptr, "GetBleScanResultsForGeneral"}, + {6, nullptr, "StartBleScanForPaired"}, + {7, nullptr, "StopBleScanForPaired"}, + {8, nullptr, "StartBleScanForSmartDevice"}, + {9, nullptr, "StopBleScanForSmartDevice"}, + {10, nullptr, "GetBleScanResultsForSmartDevice"}, + {17, C<&IBtmUserCore::AcquireBleConnectionEvent>, "AcquireBleConnectionEvent"}, + {18, nullptr, "BleConnect"}, + {19, nullptr, "BleDisconnect"}, + {20, nullptr, "BleGetConnectionState"}, + {21, nullptr, "AcquireBlePairingEvent"}, + {22, nullptr, "BlePairDevice"}, + {23, nullptr, "BleUnPairDevice"}, + {24, nullptr, "BleUnPairDevice2"}, + {25, nullptr, "BleGetPairedDevices"}, + {26, C<&IBtmUserCore::AcquireBleServiceDiscoveryEvent>, "AcquireBleServiceDiscoveryEvent"}, + {27, nullptr, "GetGattServices"}, + {28, nullptr, "GetGattService"}, + {29, nullptr, "GetGattIncludedServices"}, + {30, nullptr, "GetBelongingGattService"}, + {31, nullptr, "GetGattCharacteristics"}, + {32, nullptr, "GetGattDescriptors"}, + {33, C<&IBtmUserCore::AcquireBleMtuConfigEvent>, "AcquireBleMtuConfigEvent"}, + {34, nullptr, "ConfigureBleMtu"}, + {35, nullptr, "GetBleMtu"}, + {36, nullptr, "RegisterBleGattDataPath"}, + {37, nullptr, "UnregisterBleGattDataPath"}, + }; + // clang-format on + RegisterHandlers(functions); + + scan_event = service_context.CreateEvent("IBtmUserCore:ScanEvent"); + connection_event = service_context.CreateEvent("IBtmUserCore:ConnectionEvent"); + service_discovery_event = service_context.CreateEvent("IBtmUserCore:DiscoveryEvent"); + config_event = service_context.CreateEvent("IBtmUserCore:ConfigEvent"); +} + +IBtmUserCore::~IBtmUserCore() { + service_context.CloseEvent(scan_event); + service_context.CloseEvent(connection_event); + service_context.CloseEvent(service_discovery_event); + service_context.CloseEvent(config_event); +} + +Result IBtmUserCore::AcquireBleScanEvent(Out<bool> out_is_valid, + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + *out_is_valid = true; + *out_event = &scan_event->GetReadableEvent(); + R_SUCCEED(); +} + +Result IBtmUserCore::AcquireBleConnectionEvent(Out<bool> out_is_valid, + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + *out_is_valid = true; + *out_event = &connection_event->GetReadableEvent(); + R_SUCCEED(); +} + +Result IBtmUserCore::AcquireBleServiceDiscoveryEvent( + Out<bool> out_is_valid, OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + *out_is_valid = true; + *out_event = &service_discovery_event->GetReadableEvent(); + R_SUCCEED(); +} + +Result IBtmUserCore::AcquireBleMtuConfigEvent(Out<bool> out_is_valid, + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + *out_is_valid = true; + *out_event = &config_event->GetReadableEvent(); + R_SUCCEED(); +} + +} // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm_user_core.h b/src/core/hle/service/btm/btm_user_core.h new file mode 100644 index 000000000..dc0a22e81 --- /dev/null +++ b/src/core/hle/service/btm/btm_user_core.h @@ -0,0 +1,47 @@ +// 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/service.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Core { +class System; +} + +namespace Service::BTM { + +class IBtmUserCore final : public ServiceFramework<IBtmUserCore> { +public: + explicit IBtmUserCore(Core::System& system_); + ~IBtmUserCore() override; + +private: + Result AcquireBleScanEvent(Out<bool> out_is_valid, + OutCopyHandle<Kernel::KReadableEvent> out_event); + + Result AcquireBleConnectionEvent(Out<bool> out_is_valid, + OutCopyHandle<Kernel::KReadableEvent> out_event); + + Result AcquireBleServiceDiscoveryEvent(Out<bool> out_is_valid, + OutCopyHandle<Kernel::KReadableEvent> out_event); + + Result AcquireBleMtuConfigEvent(Out<bool> out_is_valid, + OutCopyHandle<Kernel::KReadableEvent> out_event); + + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* scan_event; + Kernel::KEvent* connection_event; + Kernel::KEvent* service_discovery_event; + Kernel::KEvent* config_event; +}; + +} // namespace Service::BTM diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 47ff072c5..52228b830 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -16,7 +16,7 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_, // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetAlbumFileCount"}, - {1, nullptr, "GetAlbumFileList"}, + {1, C<&IAlbumAccessorService::GetAlbumFileList>, "GetAlbumFileList"}, {2, nullptr, "LoadAlbumFile"}, {3, C<&IAlbumAccessorService::DeleteAlbumFile>, "DeleteAlbumFile"}, {4, nullptr, "StorageCopyAlbumFile"}, @@ -62,6 +62,15 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_, IAlbumAccessorService::~IAlbumAccessorService() = default; +Result IAlbumAccessorService::GetAlbumFileList( + Out<u64> out_count, AlbumStorage storage, + OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries) { + LOG_INFO(Service_Capture, "called, storage={}", storage); + + const Result result = manager->GetAlbumFileList(out_entries, *out_count, storage, 0); + R_RETURN(TranslateResult(result)); +} + Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) { LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}", file_id.application_id, file_id.storage, file_id.type); diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h index 2cb9b4547..c7a5208e3 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h @@ -21,6 +21,9 @@ public: ~IAlbumAccessorService() override; private: + Result GetAlbumFileList(Out<u64> out_count, AlbumStorage storage, + OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries); + Result DeleteAlbumFile(AlbumFileId file_id); Result IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage); diff --git a/src/core/hle/service/erpt/erpt.cpp b/src/core/hle/service/erpt/erpt.cpp index 3ea862fad..39ae3a723 100644 --- a/src/core/hle/service/erpt/erpt.cpp +++ b/src/core/hle/service/erpt/erpt.cpp @@ -3,6 +3,8 @@ #include <memory> +#include "common/logging/log.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/erpt/erpt.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" @@ -15,7 +17,7 @@ public: explicit ErrorReportContext(Core::System& system_) : ServiceFramework{system_, "erpt:c"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "SubmitContext"}, + {0, C<&ErrorReportContext::SubmitContext>, "SubmitContext"}, {1, nullptr, "CreateReportV0"}, {2, nullptr, "SetInitialLaunchSettingsCompletionTime"}, {3, nullptr, "ClearInitialLaunchSettingsCompletionTime"}, @@ -36,6 +38,14 @@ public: RegisterHandlers(functions); } + +private: + Result SubmitContext(InBuffer<BufferAttr_HipcMapAlias> buffer_a, + InBuffer<BufferAttr_HipcMapAlias> buffer_b) { + LOG_WARNING(Service_SET, "(STUBBED) called, buffer_a_size={}, buffer_b_size={}", + buffer_a.size(), buffer_b.size()); + R_SUCCEED(); + } }; class ErrorReportSession final : public ServiceFramework<ErrorReportSession> { diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp index 63c2d3a58..2d49f30c8 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp @@ -336,7 +336,7 @@ FSP_SRV::FSP_SRV(Core::System& system_) {1012, nullptr, "GetFsStackUsage"}, {1013, nullptr, "UnsetSaveDataRootPath"}, {1014, nullptr, "OutputMultiProgramTagAccessLog"}, - {1016, nullptr, "FlushAccessLogOnSdCard"}, + {1016, &FSP_SRV::FlushAccessLogOnSdCard, "FlushAccessLogOnSdCard"}, {1017, nullptr, "OutputApplicationInfoAccessLog"}, {1018, nullptr, "SetDebugOption"}, {1019, nullptr, "UnsetDebugOption"}, @@ -706,6 +706,13 @@ void FSP_SRV::GetProgramIndexForAccessLog(HLERequestContext& ctx) { rb.Push(access_log_program_index); } +void FSP_SRV::FlushAccessLogOnSdCard(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void FSP_SRV::GetCacheStorageSize(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto index{rp.Pop<s32>()}; diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.h b/src/core/hle/service/filesystem/fsp/fsp_srv.h index 26980af99..59406e6f9 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.h @@ -58,6 +58,7 @@ private: void SetGlobalAccessLogMode(HLERequestContext& ctx); void GetGlobalAccessLogMode(HLERequestContext& ctx); void OutputAccessLogToSdCard(HLERequestContext& ctx); + void FlushAccessLogOnSdCard(HLERequestContext& ctx); void GetProgramIndexForAccessLog(HLERequestContext& ctx); void OpenMultiCommitManager(HLERequestContext& ctx); void GetCacheStorageSize(HLERequestContext& ctx); diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp index 0c27e8029..059ac3fc9 100644 --- a/src/core/hle/service/glue/time/manager.cpp +++ b/src/core/hle/service/glue/time/manager.cpp @@ -21,19 +21,6 @@ namespace Service::Glue::Time { namespace { - -template <typename T> -T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys, - const char* category, const char* name) { - std::vector<u8> interval_buf; - auto res = set_sys->GetSettingsItemValue(interval_buf, category, name); - ASSERT(res == ResultSuccess); - - T v{}; - std::memcpy(&v, interval_buf.data(), sizeof(T)); - return v; -} - s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) { constexpr auto is_leap = [](s32 year) -> bool { return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)); @@ -65,13 +52,15 @@ s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) { s64 GetEpochTimeFromInitialYear(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys) { Service::PSC::Time::CalendarTime calendar{ - .year = GetSettingsItemValue<s16>(set_sys, "time", "standard_user_clock_initial_year"), + .year = 2000, .month = 1, .day = 1, .hour = 0, .minute = 0, .second = 0, }; + set_sys->GetSettingsItemValueImpl<s16>(calendar.year, "time", + "standard_user_clock_initial_year"); return CalendarTimeToEpoch(calendar); } @@ -124,7 +113,7 @@ TimeManager::TimeManager(Core::System& system) ASSERT(res == ResultSuccess); Service::PSC::Time::SystemClockContext user_clock_context{}; - res = m_set_sys->GetUserSystemClockContext(user_clock_context); + res = m_set_sys->GetUserSystemClockContext(&user_clock_context); ASSERT(res == ResultSuccess); // TODO the local clock should initialise with this epoch time, and be updated somewhere else on @@ -140,11 +129,12 @@ TimeManager::TimeManager(Core::System& system) ASSERT(res == ResultSuccess); Service::PSC::Time::SystemClockContext network_clock_context{}; - res = m_set_sys->GetNetworkSystemClockContext(network_clock_context); + res = m_set_sys->GetNetworkSystemClockContext(&network_clock_context); ASSERT(res == ResultSuccess); - auto network_accuracy_m{GetSettingsItemValue<s32>( - m_set_sys, "time", "standard_network_clock_sufficient_accuracy_minutes")}; + s32 network_accuracy_m{}; + m_set_sys->GetSettingsItemValueImpl<s32>(network_accuracy_m, "time", + "standard_network_clock_sufficient_accuracy_minutes"); auto one_minute_ns{ std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()}; s64 network_accuracy_ns{network_accuracy_m * one_minute_ns}; @@ -153,12 +143,12 @@ TimeManager::TimeManager(Core::System& system) ASSERT(res == ResultSuccess); bool is_automatic_correction_enabled{}; - res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(is_automatic_correction_enabled); + res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(&is_automatic_correction_enabled); ASSERT(res == ResultSuccess); Service::PSC::Time::SteadyClockTimePoint automatic_correction_time_point{}; res = m_set_sys->GetUserSystemClockAutomaticCorrectionUpdatedTime( - automatic_correction_time_point); + &automatic_correction_time_point); ASSERT(res == ResultSuccess); res = m_time_m->SetupStandardUserSystemClockCore(is_automatic_correction_enabled, @@ -196,13 +186,17 @@ TimeManager::TimeManager(Core::System& system) } } +TimeManager::~TimeManager() { + ResetTimeZoneBinary(); +} + Result TimeManager::SetupStandardSteadyClockCore() { Common::UUID external_clock_source_id{}; - auto res = m_set_sys->GetExternalSteadyClockSourceId(external_clock_source_id); + auto res = m_set_sys->GetExternalSteadyClockSourceId(&external_clock_source_id); ASSERT(res == ResultSuccess); s64 external_steady_clock_internal_offset_s{}; - res = m_set_sys->GetExternalSteadyClockInternalOffset(external_steady_clock_internal_offset_s); + res = m_set_sys->GetExternalSteadyClockInternalOffset(&external_steady_clock_internal_offset_s); ASSERT(res == ResultSuccess); auto one_second_ns{ @@ -210,8 +204,9 @@ Result TimeManager::SetupStandardSteadyClockCore() { s64 external_steady_clock_internal_offset_ns{external_steady_clock_internal_offset_s * one_second_ns}; - s32 standard_steady_clock_test_offset_m{ - GetSettingsItemValue<s32>(m_set_sys, "time", "standard_steady_clock_test_offset_minutes")}; + s32 standard_steady_clock_test_offset_m{}; + m_set_sys->GetSettingsItemValueImpl<s32>(standard_steady_clock_test_offset_m, "time", + "standard_steady_clock_test_offset_minutes"); auto one_minute_ns{ std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()}; s64 standard_steady_clock_test_offset_ns{standard_steady_clock_test_offset_m * one_minute_ns}; @@ -237,7 +232,7 @@ Result TimeManager::SetupStandardSteadyClockCore() { Result TimeManager::SetupTimeZoneServiceCore() { Service::PSC::Time::LocationName name{}; - auto res = m_set_sys->GetDeviceTimeZoneLocationName(name); + auto res = m_set_sys->GetDeviceTimeZoneLocationName(&name); ASSERT(res == ResultSuccess); auto configured_zone = GetTimeZoneString(name); @@ -255,7 +250,7 @@ Result TimeManager::SetupTimeZoneServiceCore() { } Service::PSC::Time::SteadyClockTimePoint time_point{}; - res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(time_point); + res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(&time_point); ASSERT(res == ResultSuccess); auto location_count = GetTimeZoneCount(); diff --git a/src/core/hle/service/glue/time/manager.h b/src/core/hle/service/glue/time/manager.h index 1de93f8f9..bb4b65049 100644 --- a/src/core/hle/service/glue/time/manager.h +++ b/src/core/hle/service/glue/time/manager.h @@ -26,6 +26,7 @@ namespace Service::Glue::Time { class TimeManager { public: explicit TimeManager(Core::System& system); + ~TimeManager(); std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp index f8c1218f3..b801faef2 100644 --- a/src/core/hle/service/glue/time/static.cpp +++ b/src/core/hle/service/glue/time/static.cpp @@ -20,19 +20,6 @@ #include "core/hle/service/sm/sm.h" namespace Service::Glue::Time { -namespace { -template <typename T> -T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys, - const char* category, const char* name) { - std::vector<u8> interval_buf; - auto res = set_sys->GetSettingsItemValue(interval_buf, category, name); - ASSERT(res == ResultSuccess); - - T v{}; - std::memcpy(&v, interval_buf.data(), sizeof(T)); - return v; -} -} // namespace StaticService::StaticService(Core::System& system_, Service::PSC::Time::StaticServiceSetupInfo setup_info, @@ -155,16 +142,18 @@ Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) { } Result StaticService::GetStandardSteadyClockRtcValue(Out<s64> out_rtc_value) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value); + }; R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(*out_rtc_value)); } Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled( Out<bool> out_automatic_correction) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. out_automatic_correction={}", *out_automatic_correction); - }); + }; R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled( out_automatic_correction)); @@ -179,21 +168,27 @@ Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled( } Result StaticService::GetStandardUserSystemClockInitialYear(Out<s32> out_year) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_year={}", *out_year); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_year={}", *out_year); + }; - *out_year = GetSettingsItemValue<s32>(m_set_sys, "time", "standard_user_clock_initial_year"); - R_SUCCEED(); + R_RETURN(m_set_sys->GetSettingsItemValueImpl<s32>(*out_year, "time", + "standard_user_clock_initial_year")); } Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> out_is_sufficient) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient); + }; R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient)); } Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); + }; R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( out_time_point)); @@ -201,15 +196,18 @@ Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( Out<s64> out_time, const Service::PSC::Time::SystemClockContext& context) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); + }; R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context)); } Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, Service::PSC::Time::TimeType type) { - SCOPE_EXIT( - { LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot); + }; R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type)); } @@ -218,11 +216,11 @@ Result StaticService::GetClockSnapshotFromSystemClockContext( Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot, const Service::PSC::Time::SystemClockContext& user_context, const Service::PSC::Time::SystemClockContext& network_context) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. type={} out_snapshot={} user_context={} network_context={}", type, *out_snapshot, user_context, network_context); - }); + }; R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext( type, out_snapshot, user_context, network_context)); @@ -231,14 +229,18 @@ Result StaticService::GetClockSnapshotFromSystemClockContext( Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); + }; R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b)); } Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); + }; R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b)); } diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp index 36f163419..f4d0c87d5 100644 --- a/src/core/hle/service/glue/time/time_zone.cpp +++ b/src/core/hle/service/glue/time/time_zone.cpp @@ -57,7 +57,9 @@ TimeZoneService::~TimeZoneService() = default; Result TimeZoneService::GetDeviceLocationName( Out<Service::PSC::Time::LocationName> out_location_name) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name); + }; R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name)); } @@ -94,7 +96,9 @@ Result TimeZoneService::SetDeviceLocationName( } Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_count={}", *out_count); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_count={}", *out_count); + }; R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count)); } @@ -102,10 +106,10 @@ Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) { Result TimeZoneService::LoadLocationNameList( Out<u32> out_count, OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. index={} out_count={} out_names[0]={} out_names[1]={}", index, *out_count, out_names[0], out_names[1]); - }); + }; std::scoped_lock l{m_mutex}; R_RETURN(GetTimeZoneLocationList(*out_count, out_names, out_names.size(), index)); @@ -124,7 +128,9 @@ Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, Result TimeZoneService::GetTimeZoneRuleVersion( Out<Service::PSC::Time::RuleVersion> out_rule_version) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version); + }; R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version)); } @@ -132,10 +138,10 @@ Result TimeZoneService::GetTimeZoneRuleVersion( Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime( Out<Service::PSC::Time::LocationName> location_name, Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. location_name={} out_time_point={}", *location_name, *out_time_point); - }); + }; R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(location_name, out_time_point)); } @@ -178,10 +184,10 @@ Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle( Result TimeZoneService::ToCalendarTime( Out<Service::PSC::Time::CalendarTime> out_calendar_time, Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time, InRule rule) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time, *out_calendar_time, *out_additional_info); - }); + }; R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule)); } @@ -189,10 +195,10 @@ Result TimeZoneService::ToCalendarTime( Result TimeZoneService::ToCalendarTimeWithMyRule( Out<Service::PSC::Time::CalendarTime> out_calendar_time, Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time, *out_calendar_time, *out_additional_info); - }); + }; R_RETURN( m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time)); @@ -202,11 +208,11 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times, const Service::PSC::Time::CalendarTime& calendar_time, InRule rule) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}", calendar_time, *out_count, out_times[0], out_times[1]); - }); + }; R_RETURN(m_wrapped_service->ToPosixTime(out_count, out_times, calendar_time, rule)); } @@ -214,11 +220,11 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count, Result TimeZoneService::ToPosixTimeWithMyRule( Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times, const Service::PSC::Time::CalendarTime& calendar_time) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}", calendar_time, *out_count, out_times[0], out_times[1]); - }); + }; R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, calendar_time)); } diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp index 8787f2dcd..b28569b68 100644 --- a/src/core/hle/service/glue/time/worker.cpp +++ b/src/core/hle/service/glue/time/worker.cpp @@ -27,7 +27,7 @@ template <typename T> T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys, const char* category, const char* name) { std::vector<u8> interval_buf; - auto res = set_sys->GetSettingsItemValue(interval_buf, category, name); + auto res = set_sys->GetSettingsItemValueImpl(interval_buf, category, name); ASSERT(res == ResultSuccess); T v{}; 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/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index 0086f82c5..adaaea571 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -207,7 +207,8 @@ private: Result DestroyFile() { bool is_db_test_mode_enabled{}; - m_set_sys->GetSettingsItemValue(is_db_test_mode_enabled, "mii", "is_db_test_mode_enabled"); + m_set_sys->GetSettingsItemValueImpl(is_db_test_mode_enabled, "mii", + "is_db_test_mode_enabled"); LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled); R_UNLESS(is_db_test_mode_enabled, ResultTestModeOnly); @@ -217,7 +218,8 @@ private: Result DeleteFile() { bool is_db_test_mode_enabled{}; - m_set_sys->GetSettingsItemValue(is_db_test_mode_enabled, "mii", "is_db_test_mode_enabled"); + m_set_sys->GetSettingsItemValueImpl(is_db_test_mode_enabled, "mii", + "is_db_test_mode_enabled"); LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled); R_UNLESS(is_db_test_mode_enabled, ResultTestModeOnly); @@ -227,7 +229,8 @@ private: Result Format() { bool is_db_test_mode_enabled{}; - m_set_sys->GetSettingsItemValue(is_db_test_mode_enabled, "mii", "is_db_test_mode_enabled"); + m_set_sys->GetSettingsItemValueImpl(is_db_test_mode_enabled, "mii", + "is_db_test_mode_enabled"); LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled); R_UNLESS(is_db_test_mode_enabled, ResultTestModeOnly); diff --git a/src/core/hle/service/ns/account_proxy_interface.cpp b/src/core/hle/service/ns/account_proxy_interface.cpp new file mode 100644 index 000000000..e5041af66 --- /dev/null +++ b/src/core/hle/service/ns/account_proxy_interface.cpp @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/ns/account_proxy_interface.h" + +namespace Service::NS { + +IAccountProxyInterface::IAccountProxyInterface(Core::System& system_) + : ServiceFramework{system_, "IAccountProxyInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CreateUserAccount"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IAccountProxyInterface::~IAccountProxyInterface() = default; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/account_proxy_interface.h b/src/core/hle/service/ns/account_proxy_interface.h new file mode 100644 index 000000000..e944d2a75 --- /dev/null +++ b/src/core/hle/service/ns/account_proxy_interface.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { +public: + explicit IAccountProxyInterface(Core::System& system_); + ~IAccountProxyInterface() override; +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/application_manager_interface.cpp b/src/core/hle/service/ns/application_manager_interface.cpp new file mode 100644 index 000000000..2e3a44c0d --- /dev/null +++ b/src/core/hle/service/ns/application_manager_interface.cpp @@ -0,0 +1,519 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/registered_cache.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/ns/application_manager_interface.h" +#include "core/hle/service/ns/content_management_interface.h" +#include "core/hle/service/ns/read_only_application_control_data_interface.h" + +namespace Service::NS { + +IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_) + : ServiceFramework{system_, "IApplicationManagerInterface"}, + service_context{system, "IApplicationManagerInterface"}, + record_update_system_event{service_context}, sd_card_mount_status_event{service_context}, + gamecard_update_detection_event{service_context}, + gamecard_mount_status_event{service_context}, gamecard_mount_failure_event{service_context} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IApplicationManagerInterface::ListApplicationRecord>, "ListApplicationRecord"}, + {1, nullptr, "GenerateApplicationRecordCount"}, + {2, D<&IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent>, "GetApplicationRecordUpdateSystemEvent"}, + {3, nullptr, "GetApplicationViewDeprecated"}, + {4, nullptr, "DeleteApplicationEntity"}, + {5, nullptr, "DeleteApplicationCompletely"}, + {6, nullptr, "IsAnyApplicationEntityRedundant"}, + {7, nullptr, "DeleteRedundantApplicationEntity"}, + {8, nullptr, "IsApplicationEntityMovable"}, + {9, nullptr, "MoveApplicationEntity"}, + {11, nullptr, "CalculateApplicationOccupiedSize"}, + {16, nullptr, "PushApplicationRecord"}, + {17, nullptr, "ListApplicationRecordContentMeta"}, + {19, nullptr, "LaunchApplicationOld"}, + {21, nullptr, "GetApplicationContentPath"}, + {22, nullptr, "TerminateApplication"}, + {23, nullptr, "ResolveApplicationContentPath"}, + {26, nullptr, "BeginInstallApplication"}, + {27, nullptr, "DeleteApplicationRecord"}, + {30, nullptr, "RequestApplicationUpdateInfo"}, + {31, nullptr, "Unknown31"}, + {32, nullptr, "CancelApplicationDownload"}, + {33, nullptr, "ResumeApplicationDownload"}, + {35, nullptr, "UpdateVersionList"}, + {36, nullptr, "PushLaunchVersion"}, + {37, nullptr, "ListRequiredVersion"}, + {38, D<&IApplicationManagerInterface::CheckApplicationLaunchVersion>, "CheckApplicationLaunchVersion"}, + {39, nullptr, "CheckApplicationLaunchRights"}, + {40, nullptr, "GetApplicationLogoData"}, + {41, nullptr, "CalculateApplicationDownloadRequiredSize"}, + {42, nullptr, "CleanupSdCard"}, + {43, D<&IApplicationManagerInterface::CheckSdCardMountStatus>, "CheckSdCardMountStatus"}, + {44, D<&IApplicationManagerInterface::GetSdCardMountStatusChangedEvent>, "GetSdCardMountStatusChangedEvent"}, + {45, nullptr, "GetGameCardAttachmentEvent"}, + {46, nullptr, "GetGameCardAttachmentInfo"}, + {47, nullptr, "GetTotalSpaceSize"}, + {48, D<&IApplicationManagerInterface::GetFreeSpaceSize>, "GetFreeSpaceSize"}, + {49, nullptr, "GetSdCardRemovedEvent"}, + {52, D<&IApplicationManagerInterface::GetGameCardUpdateDetectionEvent>, "GetGameCardUpdateDetectionEvent"}, + {53, nullptr, "DisableApplicationAutoDelete"}, + {54, nullptr, "EnableApplicationAutoDelete"}, + {55, D<&IApplicationManagerInterface::GetApplicationDesiredLanguage>, "GetApplicationDesiredLanguage"}, + {56, nullptr, "SetApplicationTerminateResult"}, + {57, nullptr, "ClearApplicationTerminateResult"}, + {58, nullptr, "GetLastSdCardMountUnexpectedResult"}, + {59, D<&IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"}, + {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, + {61, nullptr, "GetBackgroundDownloadStressTaskInfo"}, + {62, nullptr, "GetGameCardStopper"}, + {63, nullptr, "IsSystemProgramInstalled"}, + {64, nullptr, "StartApplyDeltaTask"}, + {65, nullptr, "GetRequestServerStopper"}, + {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"}, + {67, nullptr, "CancelApplicationApplyDelta"}, + {68, nullptr, "ResumeApplicationApplyDelta"}, + {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"}, + {70, D<&IApplicationManagerInterface::ResumeAll>, "ResumeAll"}, + {71, D<&IApplicationManagerInterface::GetStorageSize>, "GetStorageSize"}, + {80, nullptr, "RequestDownloadApplication"}, + {81, nullptr, "RequestDownloadAddOnContent"}, + {82, nullptr, "DownloadApplication"}, + {83, nullptr, "CheckApplicationResumeRights"}, + {84, nullptr, "GetDynamicCommitEvent"}, + {85, nullptr, "RequestUpdateApplication2"}, + {86, nullptr, "EnableApplicationCrashReport"}, + {87, nullptr, "IsApplicationCrashReportEnabled"}, + {90, nullptr, "BoostSystemMemoryResourceLimit"}, + {91, nullptr, "DeprecatedLaunchApplication"}, + {92, nullptr, "GetRunningApplicationProgramId"}, + {93, nullptr, "GetMainApplicationProgramIndex"}, + {94, nullptr, "LaunchApplication"}, + {95, nullptr, "GetApplicationLaunchInfo"}, + {96, nullptr, "AcquireApplicationLaunchInfo"}, + {97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"}, + {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, + {99, nullptr, "LaunchDevMenu"}, + {100, nullptr, "ResetToFactorySettings"}, + {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, + {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, + {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"}, + {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"}, + {105, nullptr, "RequestResetToFactorySettingsSecurely"}, + {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"}, + {200, nullptr, "CalculateUserSaveDataStatistics"}, + {201, nullptr, "DeleteUserSaveDataAll"}, + {210, nullptr, "DeleteUserSystemSaveData"}, + {211, nullptr, "DeleteSaveData"}, + {220, nullptr, "UnregisterNetworkServiceAccount"}, + {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"}, + {300, nullptr, "GetApplicationShellEvent"}, + {301, nullptr, "PopApplicationShellEventInfo"}, + {302, nullptr, "LaunchLibraryApplet"}, + {303, nullptr, "TerminateLibraryApplet"}, + {304, nullptr, "LaunchSystemApplet"}, + {305, nullptr, "TerminateSystemApplet"}, + {306, nullptr, "LaunchOverlayApplet"}, + {307, nullptr, "TerminateOverlayApplet"}, + {400, D<&IApplicationManagerInterface::GetApplicationControlData>, "GetApplicationControlData"}, + {401, nullptr, "InvalidateAllApplicationControlCache"}, + {402, nullptr, "RequestDownloadApplicationControlData"}, + {403, nullptr, "GetMaxApplicationControlCacheCount"}, + {404, nullptr, "InvalidateApplicationControlCache"}, + {405, nullptr, "ListApplicationControlCacheEntryInfo"}, + {406, nullptr, "GetApplicationControlProperty"}, + {407, nullptr, "ListApplicationTitle"}, + {408, nullptr, "ListApplicationIcon"}, + {502, nullptr, "RequestCheckGameCardRegistration"}, + {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, + {504, nullptr, "RequestRegisterGameCard"}, + {505, D<&IApplicationManagerInterface::GetGameCardMountFailureEvent>, "GetGameCardMountFailureEvent"}, + {506, nullptr, "IsGameCardInserted"}, + {507, nullptr, "EnsureGameCardAccess"}, + {508, nullptr, "GetLastGameCardMountFailureResult"}, + {509, nullptr, "ListApplicationIdOnGameCard"}, + {510, nullptr, "GetGameCardPlatformRegion"}, + {600, nullptr, "CountApplicationContentMeta"}, + {601, nullptr, "ListApplicationContentMetaStatus"}, + {602, nullptr, "ListAvailableAddOnContent"}, + {603, nullptr, "GetOwnedApplicationContentMetaStatus"}, + {604, nullptr, "RegisterContentsExternalKey"}, + {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, + {606, nullptr, "GetContentMetaStorage"}, + {607, nullptr, "ListAvailableAddOnContent"}, + {609, nullptr, "ListAvailabilityAssuredAddOnContent"}, + {610, nullptr, "GetInstalledContentMetaStorage"}, + {611, nullptr, "PrepareAddOnContent"}, + {700, nullptr, "PushDownloadTaskList"}, + {701, nullptr, "ClearTaskStatusList"}, + {702, nullptr, "RequestDownloadTaskList"}, + {703, nullptr, "RequestEnsureDownloadTask"}, + {704, nullptr, "ListDownloadTaskStatus"}, + {705, nullptr, "RequestDownloadTaskListData"}, + {800, nullptr, "RequestVersionList"}, + {801, nullptr, "ListVersionList"}, + {802, nullptr, "RequestVersionListData"}, + {900, nullptr, "GetApplicationRecord"}, + {901, nullptr, "GetApplicationRecordProperty"}, + {902, nullptr, "EnableApplicationAutoUpdate"}, + {903, nullptr, "DisableApplicationAutoUpdate"}, + {904, nullptr, "TouchApplication"}, + {905, nullptr, "RequestApplicationUpdate"}, + {906, D<&IApplicationManagerInterface::IsApplicationUpdateRequested>, "IsApplicationUpdateRequested"}, + {907, nullptr, "WithdrawApplicationUpdateRequest"}, + {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, + {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, + {910, nullptr, "HasApplicationRecord"}, + {911, nullptr, "SetPreInstalledApplication"}, + {912, nullptr, "ClearPreInstalledApplicationFlag"}, + {913, nullptr, "ListAllApplicationRecord"}, + {914, nullptr, "HideApplicationRecord"}, + {915, nullptr, "ShowApplicationRecord"}, + {916, nullptr, "IsApplicationAutoDeleteDisabled"}, + {1000, nullptr, "RequestVerifyApplicationDeprecated"}, + {1001, nullptr, "CorruptApplicationForDebug"}, + {1002, nullptr, "RequestVerifyAddOnContentsRights"}, + {1003, nullptr, "RequestVerifyApplication"}, + {1004, nullptr, "CorruptContentForDebug"}, + {1200, nullptr, "NeedsUpdateVulnerability"}, + {1300, D<&IApplicationManagerInterface::IsAnyApplicationEntityInstalled>, "IsAnyApplicationEntityInstalled"}, + {1301, nullptr, "DeleteApplicationContentEntities"}, + {1302, nullptr, "CleanupUnrecordedApplicationEntity"}, + {1303, nullptr, "CleanupAddOnContentsWithNoRights"}, + {1304, nullptr, "DeleteApplicationContentEntity"}, + {1305, nullptr, "TryDeleteRunningApplicationEntity"}, + {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, + {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, + {1308, nullptr, "DeleteApplicationCompletelyForDebug"}, + {1309, nullptr, "CleanupUnavailableAddOnContents"}, + {1310, nullptr, "RequestMoveApplicationEntity"}, + {1311, nullptr, "EstimateSizeToMove"}, + {1312, nullptr, "HasMovableEntity"}, + {1313, nullptr, "CleanupOrphanContents"}, + {1314, nullptr, "CheckPreconditionSatisfiedToMove"}, + {1400, nullptr, "PrepareShutdown"}, + {1500, nullptr, "FormatSdCard"}, + {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, + {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"}, + {1504, nullptr, "InsertSdCard"}, + {1505, nullptr, "RemoveSdCard"}, + {1506, nullptr, "GetSdCardStartupStatus"}, + {1600, nullptr, "GetSystemSeedForPseudoDeviceId"}, + {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"}, + {1700, nullptr, "ListApplicationDownloadingContentMeta"}, + {1701, D<&IApplicationManagerInterface::GetApplicationView>, "GetApplicationView"}, + {1702, nullptr, "GetApplicationDownloadTaskStatus"}, + {1703, nullptr, "GetApplicationViewDownloadErrorContext"}, + {1704, D<&IApplicationManagerInterface::GetApplicationViewWithPromotionInfo>, "GetApplicationViewWithPromotionInfo"}, + {1705, nullptr, "IsPatchAutoDeletableApplication"}, + {1800, nullptr, "IsNotificationSetupCompleted"}, + {1801, nullptr, "GetLastNotificationInfoCount"}, + {1802, nullptr, "ListLastNotificationInfo"}, + {1803, nullptr, "ListNotificationTask"}, + {1900, nullptr, "IsActiveAccount"}, + {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"}, + {1902, nullptr, "GetApplicationTicketInfo"}, + {1903, nullptr, "RequestDownloadApplicationPrepurchasedRightsForAccount"}, + {2000, nullptr, "GetSystemDeliveryInfo"}, + {2001, nullptr, "SelectLatestSystemDeliveryInfo"}, + {2002, nullptr, "VerifyDeliveryProtocolVersion"}, + {2003, nullptr, "GetApplicationDeliveryInfo"}, + {2004, nullptr, "HasAllContentsToDeliver"}, + {2005, nullptr, "CompareApplicationDeliveryInfo"}, + {2006, nullptr, "CanDeliverApplication"}, + {2007, nullptr, "ListContentMetaKeyToDeliverApplication"}, + {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"}, + {2009, nullptr, "EstimateRequiredSize"}, + {2010, nullptr, "RequestReceiveApplication"}, + {2011, nullptr, "CommitReceiveApplication"}, + {2012, nullptr, "GetReceiveApplicationProgress"}, + {2013, nullptr, "RequestSendApplication"}, + {2014, nullptr, "GetSendApplicationProgress"}, + {2015, nullptr, "CompareSystemDeliveryInfo"}, + {2016, nullptr, "ListNotCommittedContentMeta"}, + {2017, nullptr, "CreateDownloadTask"}, + {2018, nullptr, "GetApplicationDeliveryInfoHash"}, + {2050, D<&IApplicationManagerInterface::GetApplicationRightsOnClient>, "GetApplicationRightsOnClient"}, + {2051, nullptr, "InvalidateRightsIdCache"}, + {2100, D<&IApplicationManagerInterface::GetApplicationTerminateResult>, "GetApplicationTerminateResult"}, + {2101, nullptr, "GetRawApplicationTerminateResult"}, + {2150, nullptr, "CreateRightsEnvironment"}, + {2151, nullptr, "DestroyRightsEnvironment"}, + {2152, nullptr, "ActivateRightsEnvironment"}, + {2153, nullptr, "DeactivateRightsEnvironment"}, + {2154, nullptr, "ForceActivateRightsContextForExit"}, + {2155, nullptr, "UpdateRightsEnvironmentStatus"}, + {2156, nullptr, "CreateRightsEnvironmentForMicroApplication"}, + {2160, nullptr, "AddTargetApplicationToRightsEnvironment"}, + {2161, nullptr, "SetUsersToRightsEnvironment"}, + {2170, nullptr, "GetRightsEnvironmentStatus"}, + {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"}, + {2180, nullptr, "RequestExtendRightsInRightsEnvironment"}, + {2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"}, + {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"}, + {2190, nullptr, "GetRightsEnvironmentHandleForApplication"}, + {2199, nullptr, "GetRightsEnvironmentCountForDebug"}, + {2200, nullptr, "GetGameCardApplicationCopyIdentifier"}, + {2201, nullptr, "GetInstalledApplicationCopyIdentifier"}, + {2250, nullptr, "RequestReportActiveELicence"}, + {2300, nullptr, "ListEventLog"}, + {2350, nullptr, "PerformAutoUpdateByApplicationId"}, + {2351, nullptr, "RequestNoDownloadRightsErrorResolution"}, + {2352, nullptr, "RequestResolveNoDownloadRightsError"}, + {2353, nullptr, "GetApplicationDownloadTaskInfo"}, + {2354, nullptr, "PrioritizeApplicationBackgroundTask"}, + {2355, nullptr, "PreferStorageEfficientUpdate"}, + {2356, nullptr, "RequestStorageEfficientUpdatePreferable"}, + {2357, nullptr, "EnableMultiCoreDownload"}, + {2358, nullptr, "DisableMultiCoreDownload"}, + {2359, nullptr, "IsMultiCoreDownloadEnabled"}, + {2400, nullptr, "GetPromotionInfo"}, + {2401, nullptr, "CountPromotionInfo"}, + {2402, nullptr, "ListPromotionInfo"}, + {2403, nullptr, "ImportPromotionJsonForDebug"}, + {2404, nullptr, "ClearPromotionInfoForDebug"}, + {2500, nullptr, "ConfirmAvailableTime"}, + {2510, nullptr, "CreateApplicationResource"}, + {2511, nullptr, "GetApplicationResource"}, + {2513, nullptr, "LaunchMicroApplication"}, + {2514, nullptr, "ClearTaskOfAsyncTaskManager"}, + {2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"}, + {2516, nullptr, "EnsureApplicationCertificate"}, + {2517, nullptr, "CreateApplicationInstance"}, + {2518, nullptr, "UpdateQualificationForDebug"}, + {2519, nullptr, "IsQualificationTransitionSupported"}, + {2520, nullptr, "IsQualificationTransitionSupportedByProcessId"}, + {2521, nullptr, "GetRightsUserChangedEvent"}, + {2522, nullptr, "IsRomRedirectionAvailable"}, + {2800, nullptr, "GetApplicationIdOfPreomia"}, + {3000, nullptr, "RegisterDeviceLockKey"}, + {3001, nullptr, "UnregisterDeviceLockKey"}, + {3002, nullptr, "VerifyDeviceLockKey"}, + {3003, nullptr, "HideApplicationIcon"}, + {3004, nullptr, "ShowApplicationIcon"}, + {3005, nullptr, "HideApplicationTitle"}, + {3006, nullptr, "ShowApplicationTitle"}, + {3007, nullptr, "EnableGameCard"}, + {3008, nullptr, "DisableGameCard"}, + {3009, nullptr, "EnableLocalContentShare"}, + {3010, nullptr, "DisableLocalContentShare"}, + {3011, nullptr, "IsApplicationIconHidden"}, + {3012, nullptr, "IsApplicationTitleHidden"}, + {3013, nullptr, "IsGameCardEnabled"}, + {3014, nullptr, "IsLocalContentShareEnabled"}, + {3050, nullptr, "ListAssignELicenseTaskResult"}, + {9999, nullptr, "GetApplicationCertificate"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IApplicationManagerInterface::~IApplicationManagerInterface() = default; + +Result IApplicationManagerInterface::GetApplicationControlData( + OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size, + ApplicationControlSource application_control_source, u64 application_id) { + LOG_DEBUG(Service_NS, "called"); + R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationControlData( + out_buffer, out_actual_size, application_control_source, application_id)); +} + +Result IApplicationManagerInterface::GetApplicationDesiredLanguage( + Out<ApplicationLanguage> out_desired_language, u32 supported_languages) { + LOG_DEBUG(Service_NS, "called"); + R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationDesiredLanguage( + out_desired_language, supported_languages)); +} + +Result IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode( + Out<u64> out_language_code, ApplicationLanguage application_language) { + LOG_DEBUG(Service_NS, "called"); + R_RETURN( + IReadOnlyApplicationControlDataInterface(system).ConvertApplicationLanguageToLanguageCode( + out_language_code, application_language)); +} + +Result IApplicationManagerInterface::ListApplicationRecord( + OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records, Out<s32> out_count, + s32 offset) { + const auto limit = out_records.size(); + + LOG_WARNING(Service_NS, "(STUBBED) called"); + const auto& cache = system.GetContentProviderUnion(); + const auto installed_games = cache.ListEntriesFilterOrigin( + std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program); + + size_t i = 0; + u8 ii = 24; + + for (const auto& [slot, game] : installed_games) { + if (i >= limit) { + break; + } + if (game.title_id == 0 || game.title_id < 0x0100000000001FFFull) { + continue; + } + if (offset > 0) { + offset--; + continue; + } + + ApplicationRecord record{}; + record.application_id = game.title_id; + record.type = ApplicationRecordType::Installed; + record.unknown = 0; // 2 = needs update + record.unknown2 = ii++; + + out_records[i++] = record; + } + + *out_count = static_cast<s32>(i); + R_SUCCEED(); +} + +Result IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_NS, "(STUBBED) called"); + + record_update_system_event.Signal(); + *out_event = record_update_system_event.GetHandle(); + + R_SUCCEED(); +} + +Result IApplicationManagerInterface::GetGameCardMountFailureEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_NS, "(STUBBED) called"); + *out_event = gamecard_mount_failure_event.GetHandle(); + R_SUCCEED(); +} + +Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled( + Out<bool> out_is_any_application_entity_installed) { + LOG_WARNING(Service_NS, "(STUBBED) called"); + *out_is_any_application_entity_installed = true; + R_SUCCEED(); +} + +Result IApplicationManagerInterface::GetApplicationView( + OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views, + InArray<u64, BufferAttr_HipcMapAlias> application_ids) { + const auto size = std::min(out_application_views.size(), application_ids.size()); + LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size()); + + for (size_t i = 0; i < size; i++) { + ApplicationView view{}; + view.application_id = application_ids[i]; + view.unk = 0x70000; + view.flags = 0x401f17; + + out_application_views[i] = view; + } + + R_SUCCEED(); +} + +Result IApplicationManagerInterface::GetApplicationViewWithPromotionInfo( + OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views, + InArray<u64, BufferAttr_HipcMapAlias> application_ids) { + const auto size = std::min(out_application_views.size(), application_ids.size()); + LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size()); + + for (size_t i = 0; i < size; i++) { + ApplicationViewWithPromotionInfo view{}; + view.view.application_id = application_ids[i]; + view.view.unk = 0x70000; + view.view.flags = 0x401f17; + view.promotion = {}; + + out_application_views[i] = view; + } + + R_SUCCEED(); +} + +Result IApplicationManagerInterface::GetApplicationRightsOnClient( + OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count, + Common::UUID account_id, u32 flags, u64 application_id) { + LOG_WARNING(Service_NS, "(STUBBED) called, flags={}, application_id={:016X}, account_id={}", + flags, application_id, account_id.FormattedString()); + + if (!out_rights.empty()) { + ApplicationRightsOnClient rights{}; + rights.application_id = application_id; + rights.uid = account_id; + rights.flags = 0; + rights.flags2 = 0; + + out_rights[0] = rights; + *out_count = 1; + } else { + *out_count = 0; + } + + R_SUCCEED(); +} + +Result IApplicationManagerInterface::CheckSdCardMountStatus() { + LOG_DEBUG(Service_NS, "called"); + R_RETURN(IContentManagementInterface(system).CheckSdCardMountStatus()); +} + +Result IApplicationManagerInterface::GetSdCardMountStatusChangedEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_NS, "(STUBBED) called"); + *out_event = sd_card_mount_status_event.GetHandle(); + R_SUCCEED(); +} + +Result IApplicationManagerInterface::GetFreeSpaceSize(Out<s64> out_free_space_size, + FileSys::StorageId storage_id) { + LOG_DEBUG(Service_NS, "called"); + R_RETURN(IContentManagementInterface(system).GetFreeSpaceSize(out_free_space_size, storage_id)); +} + +Result IApplicationManagerInterface::GetGameCardUpdateDetectionEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_NS, "(STUBBED) called"); + *out_event = gamecard_update_detection_event.GetHandle(); + R_SUCCEED(); +} + +Result IApplicationManagerInterface::ResumeAll() { + LOG_WARNING(Service_NS, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IApplicationManagerInterface::GetStorageSize(Out<s64> out_total_space_size, + Out<s64> out_free_space_size, + FileSys::StorageId storage_id) { + LOG_INFO(Service_NS, "called, storage_id={}", storage_id); + *out_total_space_size = system.GetFileSystemController().GetTotalSpaceSize(storage_id); + *out_free_space_size = system.GetFileSystemController().GetFreeSpaceSize(storage_id); + R_SUCCEED(); +} + +Result IApplicationManagerInterface::IsApplicationUpdateRequested(Out<bool> out_update_required, + Out<u32> out_update_version, + u64 application_id) { + LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id); + *out_update_required = false; + *out_update_version = 0; + R_SUCCEED(); +} + +Result IApplicationManagerInterface::CheckApplicationLaunchVersion(u64 application_id) { + LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id); + R_SUCCEED(); +} + +Result IApplicationManagerInterface::GetApplicationTerminateResult(Out<Result> out_result, + u64 application_id) { + LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id); + *out_result = ResultSuccess; + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/application_manager_interface.h b/src/core/hle/service/ns/application_manager_interface.h new file mode 100644 index 000000000..350ec37ce --- /dev/null +++ b/src/core/hle/service/ns/application_manager_interface.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/ns/language.h" +#include "core/hle/service/ns/ns_types.h" +#include "core/hle/service/os/event.h" +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> { +public: + explicit IApplicationManagerInterface(Core::System& system_); + ~IApplicationManagerInterface() override; + + Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer, + Out<u32> out_actual_size, + ApplicationControlSource application_control_source, + u64 application_id); + Result GetApplicationDesiredLanguage(Out<ApplicationLanguage> out_desired_language, + u32 supported_languages); + Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code, + ApplicationLanguage application_language); + Result ListApplicationRecord(OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records, + Out<s32> out_count, s32 offset); + Result GetApplicationRecordUpdateSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed); + Result GetApplicationView( + OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views, + InArray<u64, BufferAttr_HipcMapAlias> application_ids); + Result GetApplicationViewWithPromotionInfo( + OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views, + InArray<u64, BufferAttr_HipcMapAlias> application_ids); + Result GetApplicationRightsOnClient( + OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count, + Common::UUID account_id, u32 flags, u64 application_id); + Result CheckSdCardMountStatus(); + Result GetSdCardMountStatusChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id); + Result GetGameCardUpdateDetectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); + Result ResumeAll(); + Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size, + FileSys::StorageId storage_id); + Result IsApplicationUpdateRequested(Out<bool> out_update_required, Out<u32> out_update_version, + u64 application_id); + Result CheckApplicationLaunchVersion(u64 application_id); + Result GetApplicationTerminateResult(Out<Result> out_result, u64 application_id); + +private: + KernelHelpers::ServiceContext service_context; + Event record_update_system_event; + Event sd_card_mount_status_event; + Event gamecard_update_detection_event; + Event gamecard_mount_status_event; + Event gamecard_mount_failure_event; +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/application_version_interface.cpp b/src/core/hle/service/ns/application_version_interface.cpp new file mode 100644 index 000000000..b89e127db --- /dev/null +++ b/src/core/hle/service/ns/application_version_interface.cpp @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/ns/application_version_interface.h" + +namespace Service::NS { + +IApplicationVersionInterface::IApplicationVersionInterface(Core::System& system_) + : ServiceFramework{system_, "IApplicationVersionInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetLaunchRequiredVersion"}, + {1, nullptr, "UpgradeLaunchRequiredVersion"}, + {35, nullptr, "UpdateVersionList"}, + {36, nullptr, "PushLaunchVersion"}, + {37, nullptr, "ListRequiredVersion"}, + {800, nullptr, "RequestVersionList"}, + {801, nullptr, "ListVersionList"}, + {802, nullptr, "RequestVersionListData"}, + {900, nullptr, "ImportAutoUpdatePolicyJsonForDebug"}, + {901, nullptr, "ListDefaultAutoUpdatePolicy"}, + {902, nullptr, "ListAutoUpdatePolicyForSpecificApplication"}, + {1000, nullptr, "PerformAutoUpdate"}, + {1001, nullptr, "ListAutoUpdateSchedule"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IApplicationVersionInterface::~IApplicationVersionInterface() = default; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/application_version_interface.h b/src/core/hle/service/ns/application_version_interface.h new file mode 100644 index 000000000..b288cff1b --- /dev/null +++ b/src/core/hle/service/ns/application_version_interface.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { +public: + explicit IApplicationVersionInterface(Core::System& system_); + ~IApplicationVersionInterface() override; +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/content_management_interface.cpp b/src/core/hle/service/ns/content_management_interface.cpp new file mode 100644 index 000000000..69bb3f6e4 --- /dev/null +++ b/src/core/hle/service/ns/content_management_interface.cpp @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/common_funcs.h" +#include "core/core.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/ns/content_management_interface.h" +#include "core/hle/service/ns/ns_types.h" + +namespace Service::NS { + +IContentManagementInterface::IContentManagementInterface(Core::System& system_) + : ServiceFramework{system_, "IContentManagementInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {11, D<&IContentManagementInterface::CalculateApplicationOccupiedSize>, "CalculateApplicationOccupiedSize"}, + {43, D<&IContentManagementInterface::CheckSdCardMountStatus>, "CheckSdCardMountStatus"}, + {47, D<&IContentManagementInterface::GetTotalSpaceSize>, "GetTotalSpaceSize"}, + {48, D<&IContentManagementInterface::GetFreeSpaceSize>, "GetFreeSpaceSize"}, + {600, nullptr, "CountApplicationContentMeta"}, + {601, nullptr, "ListApplicationContentMetaStatus"}, + {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, + {607, nullptr, "IsAnyApplicationRunning"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IContentManagementInterface::~IContentManagementInterface() = default; + +Result IContentManagementInterface::CalculateApplicationOccupiedSize( + Out<ApplicationOccupiedSize> out_size, u64 application_id) { + LOG_WARNING(Service_NS, "(STUBBED) called, application_id={:016X}", application_id); + + using namespace Common::Literals; + + constexpr ApplicationOccupiedSizeEntity stub_entity{ + .storage_id = FileSys::StorageId::SdCard, + .app_size = 8_GiB, + .patch_size = 2_GiB, + .aoc_size = 12_MiB, + }; + + for (auto& entity : out_size->entities) { + entity = stub_entity; + } + + R_SUCCEED(); +} + +Result IContentManagementInterface::CheckSdCardMountStatus() { + LOG_WARNING(Service_NS, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IContentManagementInterface::GetTotalSpaceSize(Out<s64> out_total_space_size, + FileSys::StorageId storage_id) { + LOG_INFO(Service_NS, "(STUBBED) called, storage_id={}", storage_id); + *out_total_space_size = system.GetFileSystemController().GetTotalSpaceSize(storage_id); + R_SUCCEED(); +} + +Result IContentManagementInterface::GetFreeSpaceSize(Out<s64> out_free_space_size, + FileSys::StorageId storage_id) { + LOG_INFO(Service_NS, "(STUBBED) called, storage_id={}", storage_id); + *out_free_space_size = system.GetFileSystemController().GetFreeSpaceSize(storage_id); + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/content_management_interface.h b/src/core/hle/service/ns/content_management_interface.h new file mode 100644 index 000000000..2894628e5 --- /dev/null +++ b/src/core/hle/service/ns/content_management_interface.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/ns/ns_types.h" +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> { +public: + explicit IContentManagementInterface(Core::System& system_); + ~IContentManagementInterface() override; + +public: + Result CalculateApplicationOccupiedSize(Out<ApplicationOccupiedSize> out_size, + u64 application_id); + Result CheckSdCardMountStatus(); + Result GetTotalSpaceSize(Out<s64> out_total_space_size, FileSys::StorageId storage_id); + Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id); +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/develop_interface.cpp b/src/core/hle/service/ns/develop_interface.cpp new file mode 100644 index 000000000..880bdbebb --- /dev/null +++ b/src/core/hle/service/ns/develop_interface.cpp @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/ns/develop_interface.h" + +namespace Service::NS { + +IDevelopInterface::IDevelopInterface(Core::System& system_) : ServiceFramework{system_, "ns:dev"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "LaunchProgram"}, + {1, nullptr, "TerminateProcess"}, + {2, nullptr, "TerminateProgram"}, + {4, nullptr, "GetShellEvent"}, + {5, nullptr, "GetShellEventInfo"}, + {6, nullptr, "TerminateApplication"}, + {7, nullptr, "PrepareLaunchProgramFromHost"}, + {8, nullptr, "LaunchApplicationFromHost"}, + {9, nullptr, "LaunchApplicationWithStorageIdForDevelop"}, + {10, nullptr, "IsSystemMemoryResourceLimitBoosted"}, + {11, nullptr, "GetRunningApplicationProcessIdForDevelop"}, + {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActiveForDevelop"}, + {13, nullptr, "CreateApplicationResourceForDevelop"}, + {14, nullptr, "IsPreomiaForDevelop"}, + {15, nullptr, "GetApplicationProgramIdFromHost"}, + {16, nullptr, "RefreshCachedDebugValues"}, + {17, nullptr, "PrepareLaunchApplicationFromHost"}, + {18, nullptr, "GetLaunchEvent"}, + {19, nullptr, "GetLaunchResult"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IDevelopInterface::~IDevelopInterface() = default; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/develop_interface.h b/src/core/hle/service/ns/develop_interface.h new file mode 100644 index 000000000..a9f81ccd6 --- /dev/null +++ b/src/core/hle/service/ns/develop_interface.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IDevelopInterface final : public ServiceFramework<IDevelopInterface> { +public: + explicit IDevelopInterface(Core::System& system_); + ~IDevelopInterface() override; +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/document_interface.cpp b/src/core/hle/service/ns/document_interface.cpp new file mode 100644 index 000000000..51a1e46c0 --- /dev/null +++ b/src/core/hle/service/ns/document_interface.cpp @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ns/document_interface.h" + +namespace Service::NS { + +IDocumentInterface::IDocumentInterface(Core::System& system_) + : ServiceFramework{system_, "IDocumentInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {21, nullptr, "GetApplicationContentPath"}, + {23, D<&IDocumentInterface::ResolveApplicationContentPath>, "ResolveApplicationContentPath"}, + {92, D<&IDocumentInterface::GetRunningApplicationProgramId>, "GetRunningApplicationProgramId"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IDocumentInterface::~IDocumentInterface() = default; + +Result IDocumentInterface::ResolveApplicationContentPath(ContentPath content_path) { + LOG_WARNING(Service_NS, "(STUBBED) called, file_system_proxy_type={}, program_id={:016X}", + content_path.file_system_proxy_type, content_path.program_id); + R_SUCCEED(); +} + +Result IDocumentInterface::GetRunningApplicationProgramId(Out<u64> out_program_id, + u64 caller_program_id) { + LOG_WARNING(Service_NS, "(STUBBED) called, caller_program_id={:016X}", caller_program_id); + *out_program_id = system.GetApplicationProcessProgramID(); + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/document_interface.h b/src/core/hle/service/ns/document_interface.h new file mode 100644 index 000000000..cd461652c --- /dev/null +++ b/src/core/hle/service/ns/document_interface.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/ns/ns_types.h" +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IDocumentInterface final : public ServiceFramework<IDocumentInterface> { +public: + explicit IDocumentInterface(Core::System& system_); + ~IDocumentInterface() override; + +private: + Result ResolveApplicationContentPath(ContentPath content_path); + Result GetRunningApplicationProgramId(Out<u64> out_program_id, u64 caller_program_id); +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/download_task_interface.cpp b/src/core/hle/service/ns/download_task_interface.cpp new file mode 100644 index 000000000..62dc7f187 --- /dev/null +++ b/src/core/hle/service/ns/download_task_interface.cpp @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ns/download_task_interface.h" + +namespace Service::NS { + +IDownloadTaskInterface::IDownloadTaskInterface(Core::System& system_) + : ServiceFramework{system_, "IDownloadTaskInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {701, nullptr, "ClearTaskStatusList"}, + {702, nullptr, "RequestDownloadTaskList"}, + {703, nullptr, "RequestEnsureDownloadTask"}, + {704, nullptr, "ListDownloadTaskStatus"}, + {705, nullptr, "RequestDownloadTaskListData"}, + {706, nullptr, "TryCommitCurrentApplicationDownloadTask"}, + {707, D<&IDownloadTaskInterface::EnableAutoCommit>, "EnableAutoCommit"}, + {708, D<&IDownloadTaskInterface::DisableAutoCommit>, "DisableAutoCommit"}, + {709, nullptr, "TriggerDynamicCommitEvent"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IDownloadTaskInterface::~IDownloadTaskInterface() = default; + +Result IDownloadTaskInterface::EnableAutoCommit() { + LOG_WARNING(Service_NS, "(STUBBED) called"); + R_SUCCEED(); +} +Result IDownloadTaskInterface::DisableAutoCommit() { + LOG_WARNING(Service_NS, "(STUBBED) called"); + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/download_task_interface.h b/src/core/hle/service/ns/download_task_interface.h new file mode 100644 index 000000000..b1cb69cb8 --- /dev/null +++ b/src/core/hle/service/ns/download_task_interface.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> { +public: + explicit IDownloadTaskInterface(Core::System& system_); + ~IDownloadTaskInterface() override; + +private: + Result EnableAutoCommit(); + Result DisableAutoCommit(); +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/dynamic_rights_interface.cpp b/src/core/hle/service/ns/dynamic_rights_interface.cpp new file mode 100644 index 000000000..ce81e203f --- /dev/null +++ b/src/core/hle/service/ns/dynamic_rights_interface.cpp @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ns/dynamic_rights_interface.h" + +namespace Service::NS { + +IDynamicRightsInterface::IDynamicRightsInterface(Core::System& system_) + : ServiceFramework{system_, "DynamicRightsInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestApplicationRightsOnServer"}, + {1, nullptr, "RequestAssignRights"}, + {4, nullptr, "DeprecatedRequestAssignRightsToResume"}, + {5, D<&IDynamicRightsInterface::VerifyActivatedRightsOwners>, "VerifyActivatedRightsOwners"}, + {6, nullptr, "DeprecatedGetApplicationRightsStatus"}, + {7, nullptr, "RequestPrefetchForDynamicRights"}, + {8, nullptr, "GetDynamicRightsState"}, + {9, nullptr, "RequestApplicationRightsOnServerToResume"}, + {10, nullptr, "RequestAssignRightsToResume"}, + {11, nullptr, "GetActivatedRightsUsers"}, + {12, nullptr, "GetApplicationRightsStatus"}, + {13, D<&IDynamicRightsInterface::GetRunningApplicationStatus>, "GetRunningApplicationStatus"}, + {14, nullptr, "SelectApplicationLicense"}, + {15, nullptr, "RequestContentsAuthorizationToken"}, + {16, nullptr, "QualifyUser"}, + {17, nullptr, "QualifyUserWithProcessId"}, + {18, D<&IDynamicRightsInterface::NotifyApplicationRightsCheckStart>, "NotifyApplicationRightsCheckStart"}, + {19, nullptr, "UpdateUserList"}, + {20, nullptr, "IsRightsLostUser"}, + {21, nullptr, "SetRequiredAddOnContentsOnContentsAvailabilityTransition"}, + {22, nullptr, "GetLimitedApplicationLicense"}, + {23, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"}, + {24, nullptr, "NotifyLimitedApplicationLicenseUpgradableEventForDebug"}, + {25, nullptr, "RequestProceedDynamicRightsState"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IDynamicRightsInterface::~IDynamicRightsInterface() = default; + +Result IDynamicRightsInterface::NotifyApplicationRightsCheckStart() { + LOG_WARNING(Service_NS, "(STUBBED) called"); + R_SUCCEED(); +} + +Result IDynamicRightsInterface::GetRunningApplicationStatus(Out<u32> out_status, + u64 rights_handle) { + LOG_WARNING(Service_NS, "(STUBBED) called, rights_handle={:#x}", rights_handle); + *out_status = 0; + R_SUCCEED(); +} + +Result IDynamicRightsInterface::VerifyActivatedRightsOwners(u64 rights_handle) { + LOG_WARNING(Service_NS, "(STUBBED) called, rights_handle={:#x}", rights_handle); + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/dynamic_rights_interface.h b/src/core/hle/service/ns/dynamic_rights_interface.h new file mode 100644 index 000000000..877e009b0 --- /dev/null +++ b/src/core/hle/service/ns/dynamic_rights_interface.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IDynamicRightsInterface final : public ServiceFramework<IDynamicRightsInterface> { +public: + explicit IDynamicRightsInterface(Core::System& system_); + ~IDynamicRightsInterface() override; + +private: + Result NotifyApplicationRightsCheckStart(); + Result GetRunningApplicationStatus(Out<u32> out_status, u64 rights_handle); + Result VerifyActivatedRightsOwners(u64 rights_handle); +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/ecommerce_interface.cpp b/src/core/hle/service/ns/ecommerce_interface.cpp new file mode 100644 index 000000000..76fc425f0 --- /dev/null +++ b/src/core/hle/service/ns/ecommerce_interface.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/ns/ecommerce_interface.h" + +namespace Service::NS { + +IECommerceInterface::IECommerceInterface(Core::System& system_) + : ServiceFramework{system_, "IECommerceInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestLinkDevice"}, + {1, nullptr, "RequestCleanupAllPreInstalledApplications"}, + {2, nullptr, "RequestCleanupPreInstalledApplication"}, + {3, nullptr, "RequestSyncRights"}, + {4, nullptr, "RequestUnlinkDevice"}, + {5, nullptr, "RequestRevokeAllELicense"}, + {6, nullptr, "RequestSyncRightsBasedOnAssignedELicenses"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IECommerceInterface::~IECommerceInterface() = default; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/ecommerce_interface.h b/src/core/hle/service/ns/ecommerce_interface.h new file mode 100644 index 000000000..4352101f4 --- /dev/null +++ b/src/core/hle/service/ns/ecommerce_interface.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IECommerceInterface final : public ServiceFramework<IECommerceInterface> { +public: + explicit IECommerceInterface(Core::System& system_); + ~IECommerceInterface() override; +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/factory_reset_interface.cpp b/src/core/hle/service/ns/factory_reset_interface.cpp new file mode 100644 index 000000000..fd5cf7e1f --- /dev/null +++ b/src/core/hle/service/ns/factory_reset_interface.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/ns/factory_reset_interface.h" + +namespace Service::NS { + +IFactoryResetInterface::IFactoryResetInterface(Core::System& system_) + : ServiceFramework{system_, "IFactoryResetInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {100, nullptr, "ResetToFactorySettings"}, + {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, + {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, + {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"}, + {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"}, + {105, nullptr, "RequestResetToFactorySettingsSecurely"}, + {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IFactoryResetInterface::~IFactoryResetInterface() = default; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/factory_reset_interface.h b/src/core/hle/service/ns/factory_reset_interface.h new file mode 100644 index 000000000..50d125123 --- /dev/null +++ b/src/core/hle/service/ns/factory_reset_interface.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> { +public: + explicit IFactoryResetInterface(Core::System& system_); + ~IFactoryResetInterface() override; +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/iplatform_service_manager.cpp b/src/core/hle/service/ns/iplatform_service_manager.cpp deleted file mode 100644 index 46268be95..000000000 --- a/src/core/hle/service/ns/iplatform_service_manager.cpp +++ /dev/null @@ -1,305 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <cstring> -#include <vector> - -#include "common/assert.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "common/swap.h" -#include "core/core.h" -#include "core/file_sys/content_archive.h" -#include "core/file_sys/nca_metadata.h" -#include "core/file_sys/registered_cache.h" -#include "core/file_sys/romfs.h" -#include "core/file_sys/system_archive/system_archive.h" -#include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/physical_memory.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/ns/iplatform_service_manager.h" - -namespace Service::NS { - -struct FontRegion { - u32 offset; - u32 size; -}; - -// The below data is specific to shared font data dumped from Switch on f/w 2.2 -// Virtual address and offsets/sizes likely will vary by dump -[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; -constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be -constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be -constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; -constexpr FontRegion EMPTY_REGION{0, 0}; - -enum class LoadState : u32 { - Loading = 0, - Done = 1, -}; - -static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output, - std::size_t& offset) { - ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, - "Shared fonts exceeds 17mb!"); - ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); - - const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor - std::vector<u32> transformed_font(input.size()); - // TODO(ogniK): Figure out a better way to do this - std::transform(input.begin(), input.end(), transformed_font.begin(), - [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); }); - transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size - std::memcpy(output.data() + offset, transformed_font.data(), - transformed_font.size() * sizeof(u32)); - offset += transformed_font.size() * sizeof(u32); -} - -void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output) { - ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); - - if (input.size() < 2) { - LOG_ERROR(Service_NS, "Input font is empty"); - return; - } - - const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor - std::vector<u32> transformed_font(input.size()); - // TODO(ogniK): Figure out a better way to do this - std::transform(input.begin(), input.end(), transformed_font.begin(), - [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); }); - std::memcpy(output.data(), transformed_font.data() + 2, - (transformed_font.size() - 2) * sizeof(u32)); -} - -void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, - std::size_t& offset) { - ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, - "Shared fonts exceeds 17mb!"); - - const auto key = Common::swap32(EXPECTED_RESULT ^ EXPECTED_MAGIC); - std::vector<u32> transformed_font(input.size() + 2); - transformed_font[0] = Common::swap32(EXPECTED_MAGIC); - transformed_font[1] = Common::swap32(static_cast<u32>(input.size() * sizeof(u32))) ^ key; - std::transform(input.begin(), input.end(), transformed_font.begin() + 2, - [key](u32 in) { return in ^ key; }); - std::memcpy(output.data() + offset, transformed_font.data(), - transformed_font.size() * sizeof(u32)); - offset += transformed_font.size() * sizeof(u32); -} - -// Helper function to make BuildSharedFontsRawRegions a bit nicer -static u32 GetU32Swapped(const u8* data) { - u32 value; - std::memcpy(&value, data, sizeof(value)); - return Common::swap32(value); -} - -struct IPlatformServiceManager::Impl { - const FontRegion& GetSharedFontRegion(std::size_t index) const { - if (index >= shared_font_regions.size() || shared_font_regions.empty()) { - // No font fallback - return EMPTY_REGION; - } - return shared_font_regions.at(index); - } - - void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) { - // As we can derive the xor key we can just populate the offsets - // based on the shared memory dump - unsigned cur_offset = 0; - - for (std::size_t i = 0; i < SHARED_FONTS.size(); i++) { - // Out of shared fonts/invalid font - if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) { - break; - } - - // Derive key within inverse xor - const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^ EXPECTED_MAGIC; - const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY; - shared_font_regions.push_back(FontRegion{cur_offset + 8, SIZE}); - cur_offset += SIZE + 8; - } - } - - /// Backing memory for the shared font data - std::shared_ptr<Kernel::PhysicalMemory> shared_font; - - // Automatically populated based on shared_fonts dump or system archives. - std::vector<FontRegion> shared_font_regions; -}; - -IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_) - : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IPlatformServiceManager::RequestLoad, "RequestLoad"}, - {1, &IPlatformServiceManager::GetLoadState, "GetLoadState"}, - {2, &IPlatformServiceManager::GetSize, "GetSize"}, - {3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, - {4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, - {5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, - {6, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriorityForSystem"}, - {100, nullptr, "RequestApplicationFunctionAuthorization"}, - {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"}, - {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"}, - {103, nullptr, "RefreshApplicationFunctionBlackListDebugRecord"}, - {104, nullptr, "RequestApplicationFunctionAuthorizationByProgramId"}, - {105, nullptr, "GetFunctionBlackListSystemVersionToAuthorize"}, - {106, nullptr, "GetFunctionBlackListVersion"}, - {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"}, - {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"}, - }; - // clang-format on - RegisterHandlers(functions); - - auto& fsc = system.GetFileSystemController(); - - // Attempt to load shared font data from disk - const auto* nand = fsc.GetSystemNANDContents(); - std::size_t offset = 0; - // Rebuild shared fonts from data ncas or synthesize - - impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE); - for (auto font : SHARED_FONTS) { - FileSys::VirtualFile romfs; - const auto nca = - nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data); - if (nca) { - romfs = nca->GetRomFS(); - } - - if (!romfs) { - romfs = FileSys::SystemArchive::SynthesizeSystemArchive(static_cast<u64>(font.first)); - } - - if (!romfs) { - LOG_ERROR(Service_NS, "Failed to find or synthesize {:016X}! Skipping", font.first); - continue; - } - - const auto extracted_romfs = FileSys::ExtractRomFS(romfs); - if (!extracted_romfs) { - LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", font.first); - continue; - } - const auto font_fp = extracted_romfs->GetFile(font.second); - if (!font_fp) { - LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", font.first, font.second); - continue; - } - std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32)); - font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize()); - // We need to be BigEndian as u32s for the xor encryption - std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), - Common::swap32); - // Font offset and size do not account for the header - const FontRegion region{static_cast<u32>(offset + 8), - static_cast<u32>((font_data_u32.size() * sizeof(u32)) - 8)}; - DecryptSharedFont(font_data_u32, *impl->shared_font, offset); - impl->shared_font_regions.push_back(region); - } -} - -IPlatformServiceManager::~IPlatformServiceManager() = default; - -void IPlatformServiceManager::RequestLoad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 shared_font_type{rp.Pop<u32>()}; - // Games don't call this so all fonts should be loaded - LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IPlatformServiceManager::GetLoadState(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 font_id{rp.Pop<u32>()}; - LOG_DEBUG(Service_NS, "called, font_id={}", font_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(LoadState::Done)); -} - -void IPlatformServiceManager::GetSize(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 font_id{rp.Pop<u32>()}; - LOG_DEBUG(Service_NS, "called, font_id={}", font_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(impl->GetSharedFontRegion(font_id).size); -} - -void IPlatformServiceManager::GetSharedMemoryAddressOffset(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 font_id{rp.Pop<u32>()}; - LOG_DEBUG(Service_NS, "called, font_id={}", font_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset); -} - -void IPlatformServiceManager::GetSharedMemoryNativeHandle(HLERequestContext& ctx) { - // Map backing memory for the font data - LOG_DEBUG(Service_NS, "called"); - - // Create shared font memory object - std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(), - impl->shared_font->size()); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(&kernel.GetFontSharedMem()); -} - -void IPlatformServiceManager::GetSharedFontInOrderOfPriority(HLERequestContext& ctx) { - // The maximum number of elements that can be returned is 6. Regardless of the available fonts - // or buffer size. - constexpr std::size_t MaxElementCount = 6; - IPC::RequestParser rp{ctx}; - const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for - const std::size_t font_codes_count = - std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(0)); - const std::size_t font_offsets_count = - std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(1)); - const std::size_t font_sizes_count = - std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(2)); - LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code); - - IPC::ResponseBuilder rb{ctx, 4}; - std::vector<u32> font_codes; - std::vector<u32> font_offsets; - std::vector<u32> font_sizes; - - // TODO(ogniK): Have actual priority order - for (std::size_t i = 0; i < impl->shared_font_regions.size(); i++) { - font_codes.push_back(static_cast<u32>(i)); - auto region = impl->GetSharedFontRegion(i); - font_offsets.push_back(region.offset); - font_sizes.push_back(region.size); - } - - // Resize buffers if game requests smaller size output - font_codes.resize(std::min(font_codes.size(), font_codes_count)); - font_offsets.resize(std::min(font_offsets.size(), font_offsets_count)); - font_sizes.resize(std::min(font_sizes.size(), font_sizes_count)); - - ctx.WriteBuffer(font_codes, 0); - ctx.WriteBuffer(font_offsets, 1); - ctx.WriteBuffer(font_sizes, 2); - - rb.Push(ResultSuccess); - rb.Push<u8>(static_cast<u8>(LoadState::Done)); // Fonts Loaded - rb.Push<u32>(static_cast<u32>(font_codes.size())); -} - -} // namespace Service::NS diff --git a/src/core/hle/service/ns/iplatform_service_manager.h b/src/core/hle/service/ns/iplatform_service_manager.h deleted file mode 100644 index 03071e02b..000000000 --- a/src/core/hle/service/ns/iplatform_service_manager.h +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> -#include <vector> -#include "core/hle/service/service.h" - -namespace Service { - -namespace FileSystem { -class FileSystemController; -} // namespace FileSystem - -namespace NS { - -enum class FontArchives : u64 { - Extension = 0x0100000000000810, - Standard = 0x0100000000000811, - Korean = 0x0100000000000812, - ChineseTraditional = 0x0100000000000813, - ChineseSimple = 0x0100000000000814, -}; - -constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{ - std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"), - std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"), - std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"), - std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"), - std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"), - std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"), - std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"), -}; - -void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output); -void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset); - -class IPlatformServiceManager final : public ServiceFramework<IPlatformServiceManager> { -public: - explicit IPlatformServiceManager(Core::System& system_, const char* service_name_); - ~IPlatformServiceManager() override; - -private: - void RequestLoad(HLERequestContext& ctx); - void GetLoadState(HLERequestContext& ctx); - void GetSize(HLERequestContext& ctx); - void GetSharedMemoryAddressOffset(HLERequestContext& ctx); - void GetSharedMemoryNativeHandle(HLERequestContext& ctx); - void GetSharedFontInOrderOfPriority(HLERequestContext& ctx); - - struct Impl; - std::unique_ptr<Impl> impl; -}; - -} // namespace NS - -} // namespace Service diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 19c3ff01b..8402e83cb 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -1,893 +1,38 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/arm/debug.h" -#include "core/core.h" -#include "core/file_sys/control_metadata.h" -#include "core/file_sys/patch_manager.h" -#include "core/file_sys/vfs/vfs.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/glue/glue_manager.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/ns/errors.h" -#include "core/hle/service/ns/iplatform_service_manager.h" -#include "core/hle/service/ns/language.h" +#include "core/hle/service/ns/develop_interface.h" #include "core/hle/service/ns/ns.h" -#include "core/hle/service/ns/pdm_qry.h" +#include "core/hle/service/ns/platform_service_manager.h" +#include "core/hle/service/ns/query_service.h" +#include "core/hle/service/ns/service_getter_interface.h" +#include "core/hle/service/ns/system_update_interface.h" +#include "core/hle/service/ns/vulnerability_manager_interface.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/set/settings_server.h" namespace Service::NS { -IAccountProxyInterface::IAccountProxyInterface(Core::System& system_) - : ServiceFramework{system_, "IAccountProxyInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "CreateUserAccount"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IAccountProxyInterface::~IAccountProxyInterface() = default; - -IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_) - : ServiceFramework{system_, "IApplicationManagerInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "ListApplicationRecord"}, - {1, nullptr, "GenerateApplicationRecordCount"}, - {2, nullptr, "GetApplicationRecordUpdateSystemEvent"}, - {3, nullptr, "GetApplicationViewDeprecated"}, - {4, nullptr, "DeleteApplicationEntity"}, - {5, nullptr, "DeleteApplicationCompletely"}, - {6, nullptr, "IsAnyApplicationEntityRedundant"}, - {7, nullptr, "DeleteRedundantApplicationEntity"}, - {8, nullptr, "IsApplicationEntityMovable"}, - {9, nullptr, "MoveApplicationEntity"}, - {11, nullptr, "CalculateApplicationOccupiedSize"}, - {16, nullptr, "PushApplicationRecord"}, - {17, nullptr, "ListApplicationRecordContentMeta"}, - {19, nullptr, "LaunchApplicationOld"}, - {21, nullptr, "GetApplicationContentPath"}, - {22, nullptr, "TerminateApplication"}, - {23, nullptr, "ResolveApplicationContentPath"}, - {26, nullptr, "BeginInstallApplication"}, - {27, nullptr, "DeleteApplicationRecord"}, - {30, nullptr, "RequestApplicationUpdateInfo"}, - {31, nullptr, "Unknown31"}, - {32, nullptr, "CancelApplicationDownload"}, - {33, nullptr, "ResumeApplicationDownload"}, - {35, nullptr, "UpdateVersionList"}, - {36, nullptr, "PushLaunchVersion"}, - {37, nullptr, "ListRequiredVersion"}, - {38, nullptr, "CheckApplicationLaunchVersion"}, - {39, nullptr, "CheckApplicationLaunchRights"}, - {40, nullptr, "GetApplicationLogoData"}, - {41, nullptr, "CalculateApplicationDownloadRequiredSize"}, - {42, nullptr, "CleanupSdCard"}, - {43, nullptr, "CheckSdCardMountStatus"}, - {44, nullptr, "GetSdCardMountStatusChangedEvent"}, - {45, nullptr, "GetGameCardAttachmentEvent"}, - {46, nullptr, "GetGameCardAttachmentInfo"}, - {47, nullptr, "GetTotalSpaceSize"}, - {48, nullptr, "GetFreeSpaceSize"}, - {49, nullptr, "GetSdCardRemovedEvent"}, - {52, nullptr, "GetGameCardUpdateDetectionEvent"}, - {53, nullptr, "DisableApplicationAutoDelete"}, - {54, nullptr, "EnableApplicationAutoDelete"}, - {55, &IApplicationManagerInterface::GetApplicationDesiredLanguage, "GetApplicationDesiredLanguage"}, - {56, nullptr, "SetApplicationTerminateResult"}, - {57, nullptr, "ClearApplicationTerminateResult"}, - {58, nullptr, "GetLastSdCardMountUnexpectedResult"}, - {59, &IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode, "ConvertApplicationLanguageToLanguageCode"}, - {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, - {61, nullptr, "GetBackgroundDownloadStressTaskInfo"}, - {62, nullptr, "GetGameCardStopper"}, - {63, nullptr, "IsSystemProgramInstalled"}, - {64, nullptr, "StartApplyDeltaTask"}, - {65, nullptr, "GetRequestServerStopper"}, - {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"}, - {67, nullptr, "CancelApplicationApplyDelta"}, - {68, nullptr, "ResumeApplicationApplyDelta"}, - {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"}, - {70, nullptr, "ResumeAll"}, - {71, nullptr, "GetStorageSize"}, - {80, nullptr, "RequestDownloadApplication"}, - {81, nullptr, "RequestDownloadAddOnContent"}, - {82, nullptr, "DownloadApplication"}, - {83, nullptr, "CheckApplicationResumeRights"}, - {84, nullptr, "GetDynamicCommitEvent"}, - {85, nullptr, "RequestUpdateApplication2"}, - {86, nullptr, "EnableApplicationCrashReport"}, - {87, nullptr, "IsApplicationCrashReportEnabled"}, - {90, nullptr, "BoostSystemMemoryResourceLimit"}, - {91, nullptr, "DeprecatedLaunchApplication"}, - {92, nullptr, "GetRunningApplicationProgramId"}, - {93, nullptr, "GetMainApplicationProgramIndex"}, - {94, nullptr, "LaunchApplication"}, - {95, nullptr, "GetApplicationLaunchInfo"}, - {96, nullptr, "AcquireApplicationLaunchInfo"}, - {97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"}, - {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, - {99, nullptr, "LaunchDevMenu"}, - {100, nullptr, "ResetToFactorySettings"}, - {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, - {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, - {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"}, - {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"}, - {105, nullptr, "RequestResetToFactorySettingsSecurely"}, - {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"}, - {200, nullptr, "CalculateUserSaveDataStatistics"}, - {201, nullptr, "DeleteUserSaveDataAll"}, - {210, nullptr, "DeleteUserSystemSaveData"}, - {211, nullptr, "DeleteSaveData"}, - {220, nullptr, "UnregisterNetworkServiceAccount"}, - {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"}, - {300, nullptr, "GetApplicationShellEvent"}, - {301, nullptr, "PopApplicationShellEventInfo"}, - {302, nullptr, "LaunchLibraryApplet"}, - {303, nullptr, "TerminateLibraryApplet"}, - {304, nullptr, "LaunchSystemApplet"}, - {305, nullptr, "TerminateSystemApplet"}, - {306, nullptr, "LaunchOverlayApplet"}, - {307, nullptr, "TerminateOverlayApplet"}, - {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"}, - {401, nullptr, "InvalidateAllApplicationControlCache"}, - {402, nullptr, "RequestDownloadApplicationControlData"}, - {403, nullptr, "GetMaxApplicationControlCacheCount"}, - {404, nullptr, "InvalidateApplicationControlCache"}, - {405, nullptr, "ListApplicationControlCacheEntryInfo"}, - {406, nullptr, "GetApplicationControlProperty"}, - {407, nullptr, "ListApplicationTitle"}, - {408, nullptr, "ListApplicationIcon"}, - {502, nullptr, "RequestCheckGameCardRegistration"}, - {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, - {504, nullptr, "RequestRegisterGameCard"}, - {505, nullptr, "GetGameCardMountFailureEvent"}, - {506, nullptr, "IsGameCardInserted"}, - {507, nullptr, "EnsureGameCardAccess"}, - {508, nullptr, "GetLastGameCardMountFailureResult"}, - {509, nullptr, "ListApplicationIdOnGameCard"}, - {510, nullptr, "GetGameCardPlatformRegion"}, - {600, nullptr, "CountApplicationContentMeta"}, - {601, nullptr, "ListApplicationContentMetaStatus"}, - {602, nullptr, "ListAvailableAddOnContent"}, - {603, nullptr, "GetOwnedApplicationContentMetaStatus"}, - {604, nullptr, "RegisterContentsExternalKey"}, - {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, - {606, nullptr, "GetContentMetaStorage"}, - {607, nullptr, "ListAvailableAddOnContent"}, - {609, nullptr, "ListAvailabilityAssuredAddOnContent"}, - {610, nullptr, "GetInstalledContentMetaStorage"}, - {611, nullptr, "PrepareAddOnContent"}, - {700, nullptr, "PushDownloadTaskList"}, - {701, nullptr, "ClearTaskStatusList"}, - {702, nullptr, "RequestDownloadTaskList"}, - {703, nullptr, "RequestEnsureDownloadTask"}, - {704, nullptr, "ListDownloadTaskStatus"}, - {705, nullptr, "RequestDownloadTaskListData"}, - {800, nullptr, "RequestVersionList"}, - {801, nullptr, "ListVersionList"}, - {802, nullptr, "RequestVersionListData"}, - {900, nullptr, "GetApplicationRecord"}, - {901, nullptr, "GetApplicationRecordProperty"}, - {902, nullptr, "EnableApplicationAutoUpdate"}, - {903, nullptr, "DisableApplicationAutoUpdate"}, - {904, nullptr, "TouchApplication"}, - {905, nullptr, "RequestApplicationUpdate"}, - {906, nullptr, "IsApplicationUpdateRequested"}, - {907, nullptr, "WithdrawApplicationUpdateRequest"}, - {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, - {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, - {910, nullptr, "HasApplicationRecord"}, - {911, nullptr, "SetPreInstalledApplication"}, - {912, nullptr, "ClearPreInstalledApplicationFlag"}, - {913, nullptr, "ListAllApplicationRecord"}, - {914, nullptr, "HideApplicationRecord"}, - {915, nullptr, "ShowApplicationRecord"}, - {916, nullptr, "IsApplicationAutoDeleteDisabled"}, - {1000, nullptr, "RequestVerifyApplicationDeprecated"}, - {1001, nullptr, "CorruptApplicationForDebug"}, - {1002, nullptr, "RequestVerifyAddOnContentsRights"}, - {1003, nullptr, "RequestVerifyApplication"}, - {1004, nullptr, "CorruptContentForDebug"}, - {1200, nullptr, "NeedsUpdateVulnerability"}, - {1300, nullptr, "IsAnyApplicationEntityInstalled"}, - {1301, nullptr, "DeleteApplicationContentEntities"}, - {1302, nullptr, "CleanupUnrecordedApplicationEntity"}, - {1303, nullptr, "CleanupAddOnContentsWithNoRights"}, - {1304, nullptr, "DeleteApplicationContentEntity"}, - {1305, nullptr, "TryDeleteRunningApplicationEntity"}, - {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, - {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, - {1308, nullptr, "DeleteApplicationCompletelyForDebug"}, - {1309, nullptr, "CleanupUnavailableAddOnContents"}, - {1310, nullptr, "RequestMoveApplicationEntity"}, - {1311, nullptr, "EstimateSizeToMove"}, - {1312, nullptr, "HasMovableEntity"}, - {1313, nullptr, "CleanupOrphanContents"}, - {1314, nullptr, "CheckPreconditionSatisfiedToMove"}, - {1400, nullptr, "PrepareShutdown"}, - {1500, nullptr, "FormatSdCard"}, - {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, - {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"}, - {1504, nullptr, "InsertSdCard"}, - {1505, nullptr, "RemoveSdCard"}, - {1506, nullptr, "GetSdCardStartupStatus"}, - {1600, nullptr, "GetSystemSeedForPseudoDeviceId"}, - {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"}, - {1700, nullptr, "ListApplicationDownloadingContentMeta"}, - {1701, nullptr, "GetApplicationView"}, - {1702, nullptr, "GetApplicationDownloadTaskStatus"}, - {1703, nullptr, "GetApplicationViewDownloadErrorContext"}, - {1704, nullptr, "GetApplicationViewWithPromotionInfo"}, - {1705, nullptr, "IsPatchAutoDeletableApplication"}, - {1800, nullptr, "IsNotificationSetupCompleted"}, - {1801, nullptr, "GetLastNotificationInfoCount"}, - {1802, nullptr, "ListLastNotificationInfo"}, - {1803, nullptr, "ListNotificationTask"}, - {1900, nullptr, "IsActiveAccount"}, - {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"}, - {1902, nullptr, "GetApplicationTicketInfo"}, - {1903, nullptr, "RequestDownloadApplicationPrepurchasedRightsForAccount"}, - {2000, nullptr, "GetSystemDeliveryInfo"}, - {2001, nullptr, "SelectLatestSystemDeliveryInfo"}, - {2002, nullptr, "VerifyDeliveryProtocolVersion"}, - {2003, nullptr, "GetApplicationDeliveryInfo"}, - {2004, nullptr, "HasAllContentsToDeliver"}, - {2005, nullptr, "CompareApplicationDeliveryInfo"}, - {2006, nullptr, "CanDeliverApplication"}, - {2007, nullptr, "ListContentMetaKeyToDeliverApplication"}, - {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"}, - {2009, nullptr, "EstimateRequiredSize"}, - {2010, nullptr, "RequestReceiveApplication"}, - {2011, nullptr, "CommitReceiveApplication"}, - {2012, nullptr, "GetReceiveApplicationProgress"}, - {2013, nullptr, "RequestSendApplication"}, - {2014, nullptr, "GetSendApplicationProgress"}, - {2015, nullptr, "CompareSystemDeliveryInfo"}, - {2016, nullptr, "ListNotCommittedContentMeta"}, - {2017, nullptr, "CreateDownloadTask"}, - {2018, nullptr, "GetApplicationDeliveryInfoHash"}, - {2050, nullptr, "GetApplicationRightsOnClient"}, - {2051, nullptr, "InvalidateRightsIdCache"}, - {2100, nullptr, "GetApplicationTerminateResult"}, - {2101, nullptr, "GetRawApplicationTerminateResult"}, - {2150, nullptr, "CreateRightsEnvironment"}, - {2151, nullptr, "DestroyRightsEnvironment"}, - {2152, nullptr, "ActivateRightsEnvironment"}, - {2153, nullptr, "DeactivateRightsEnvironment"}, - {2154, nullptr, "ForceActivateRightsContextForExit"}, - {2155, nullptr, "UpdateRightsEnvironmentStatus"}, - {2156, nullptr, "CreateRightsEnvironmentForMicroApplication"}, - {2160, nullptr, "AddTargetApplicationToRightsEnvironment"}, - {2161, nullptr, "SetUsersToRightsEnvironment"}, - {2170, nullptr, "GetRightsEnvironmentStatus"}, - {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"}, - {2180, nullptr, "RequestExtendRightsInRightsEnvironment"}, - {2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"}, - {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"}, - {2190, nullptr, "GetRightsEnvironmentHandleForApplication"}, - {2199, nullptr, "GetRightsEnvironmentCountForDebug"}, - {2200, nullptr, "GetGameCardApplicationCopyIdentifier"}, - {2201, nullptr, "GetInstalledApplicationCopyIdentifier"}, - {2250, nullptr, "RequestReportActiveELicence"}, - {2300, nullptr, "ListEventLog"}, - {2350, nullptr, "PerformAutoUpdateByApplicationId"}, - {2351, nullptr, "RequestNoDownloadRightsErrorResolution"}, - {2352, nullptr, "RequestResolveNoDownloadRightsError"}, - {2353, nullptr, "GetApplicationDownloadTaskInfo"}, - {2354, nullptr, "PrioritizeApplicationBackgroundTask"}, - {2355, nullptr, "PreferStorageEfficientUpdate"}, - {2356, nullptr, "RequestStorageEfficientUpdatePreferable"}, - {2357, nullptr, "EnableMultiCoreDownload"}, - {2358, nullptr, "DisableMultiCoreDownload"}, - {2359, nullptr, "IsMultiCoreDownloadEnabled"}, - {2400, nullptr, "GetPromotionInfo"}, - {2401, nullptr, "CountPromotionInfo"}, - {2402, nullptr, "ListPromotionInfo"}, - {2403, nullptr, "ImportPromotionJsonForDebug"}, - {2404, nullptr, "ClearPromotionInfoForDebug"}, - {2500, nullptr, "ConfirmAvailableTime"}, - {2510, nullptr, "CreateApplicationResource"}, - {2511, nullptr, "GetApplicationResource"}, - {2513, nullptr, "LaunchMicroApplication"}, - {2514, nullptr, "ClearTaskOfAsyncTaskManager"}, - {2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"}, - {2516, nullptr, "EnsureApplicationCertificate"}, - {2517, nullptr, "CreateApplicationInstance"}, - {2518, nullptr, "UpdateQualificationForDebug"}, - {2519, nullptr, "IsQualificationTransitionSupported"}, - {2520, nullptr, "IsQualificationTransitionSupportedByProcessId"}, - {2521, nullptr, "GetRightsUserChangedEvent"}, - {2522, nullptr, "IsRomRedirectionAvailable"}, - {2800, nullptr, "GetApplicationIdOfPreomia"}, - {3000, nullptr, "RegisterDeviceLockKey"}, - {3001, nullptr, "UnregisterDeviceLockKey"}, - {3002, nullptr, "VerifyDeviceLockKey"}, - {3003, nullptr, "HideApplicationIcon"}, - {3004, nullptr, "ShowApplicationIcon"}, - {3005, nullptr, "HideApplicationTitle"}, - {3006, nullptr, "ShowApplicationTitle"}, - {3007, nullptr, "EnableGameCard"}, - {3008, nullptr, "DisableGameCard"}, - {3009, nullptr, "EnableLocalContentShare"}, - {3010, nullptr, "DisableLocalContentShare"}, - {3011, nullptr, "IsApplicationIconHidden"}, - {3012, nullptr, "IsApplicationTitleHidden"}, - {3013, nullptr, "IsGameCardEnabled"}, - {3014, nullptr, "IsLocalContentShareEnabled"}, - {3050, nullptr, "ListAssignELicenseTaskResult"}, - {9999, nullptr, "GetApplicationCertificate"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IApplicationManagerInterface::~IApplicationManagerInterface() = default; - -void IApplicationManagerInterface::GetApplicationControlData(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto flag = rp.PopRaw<u64>(); - LOG_DEBUG(Service_NS, "called with flag={:016X}", flag); - - const auto title_id = rp.PopRaw<u64>(); - - const auto size = ctx.GetWriteBufferSize(); - - const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), - system.GetContentProvider()}; - const auto control = pm.GetControlMetadata(); - - std::vector<u8> out; - - if (control.first != nullptr) { - if (size < 0x4000) { - LOG_ERROR(Service_NS, - "output buffer is too small! (actual={:016X}, expected_min=0x4000)", size); - IPC::ResponseBuilder rb{ctx, 2}; - // TODO(DarkLordZach): Find a better error code for this. - rb.Push(ResultUnknown); - return; - } - - out.resize(0x4000); - const auto bytes = control.first->GetRawBytes(); - std::memcpy(out.data(), bytes.data(), bytes.size()); - } else { - LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.", - title_id); - out.resize(std::min<u64>(0x4000, size)); - } - - if (control.second != nullptr) { - if (size < 0x4000 + control.second->GetSize()) { - LOG_ERROR(Service_NS, - "output buffer is too small! (actual={:016X}, expected_min={:016X})", size, - 0x4000 + control.second->GetSize()); - IPC::ResponseBuilder rb{ctx, 2}; - // TODO(DarkLordZach): Find a better error code for this. - rb.Push(ResultUnknown); - return; - } - - out.resize(0x4000 + control.second->GetSize()); - control.second->Read(out.data() + 0x4000, control.second->GetSize()); - } else { - LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.", - title_id); - } - - ctx.WriteBuffer(out); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(out.size())); -} - -void IApplicationManagerInterface::GetApplicationDesiredLanguage(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto supported_languages = rp.Pop<u32>(); - - u8 desired_language{}; - const auto res = GetApplicationDesiredLanguage(&desired_language, supported_languages); - if (res == ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(desired_language); - } else { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - } -} - -Result IApplicationManagerInterface::GetApplicationDesiredLanguage(u8* out_desired_language, - const u32 supported_languages) { - LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages); - - // Get language code from settings - const auto language_code = - Set::GetLanguageCodeFromIndex(static_cast<s32>(Settings::values.language_index.GetValue())); - - // Convert to application language, get priority list - const auto application_language = ConvertToApplicationLanguage(language_code); - if (application_language == std::nullopt) { - LOG_ERROR(Service_NS, "Could not convert application language! language_code={}", - language_code); - return Service::NS::ResultApplicationLanguageNotFound; - } - const auto priority_list = GetApplicationLanguagePriorityList(*application_language); - if (!priority_list) { - LOG_ERROR(Service_NS, - "Could not find application language priorities! application_language={}", - *application_language); - return Service::NS::ResultApplicationLanguageNotFound; - } - - // Try to find a valid language. - for (const auto lang : *priority_list) { - const auto supported_flag = GetSupportedLanguageFlag(lang); - if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) { - *out_desired_language = static_cast<u8>(lang); - return ResultSuccess; - } - } - - LOG_ERROR(Service_NS, "Could not find a valid language! supported_languages={:08X}", - supported_languages); - return Service::NS::ResultApplicationLanguageNotFound; -} - -void IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode( - HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto application_language = rp.Pop<u8>(); - - u64 language_code{}; - const auto res = ConvertApplicationLanguageToLanguageCode(&language_code, application_language); - if (res == ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(language_code); - } else { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - } -} - -Result IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode( - u64* out_language_code, u8 application_language) { - const auto language_code = - ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language)); - if (language_code == std::nullopt) { - LOG_ERROR(Service_NS, "Language not found! application_language={}", application_language); - return Service::NS::ResultApplicationLanguageNotFound; - } - - *out_language_code = static_cast<u64>(*language_code); - return ResultSuccess; -} - -IApplicationVersionInterface::IApplicationVersionInterface(Core::System& system_) - : ServiceFramework{system_, "IApplicationVersionInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetLaunchRequiredVersion"}, - {1, nullptr, "UpgradeLaunchRequiredVersion"}, - {35, nullptr, "UpdateVersionList"}, - {36, nullptr, "PushLaunchVersion"}, - {37, nullptr, "ListRequiredVersion"}, - {800, nullptr, "RequestVersionList"}, - {801, nullptr, "ListVersionList"}, - {802, nullptr, "RequestVersionListData"}, - {900, nullptr, "ImportAutoUpdatePolicyJsonForDebug"}, - {901, nullptr, "ListDefaultAutoUpdatePolicy"}, - {902, nullptr, "ListAutoUpdatePolicyForSpecificApplication"}, - {1000, nullptr, "PerformAutoUpdate"}, - {1001, nullptr, "ListAutoUpdateSchedule"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IApplicationVersionInterface::~IApplicationVersionInterface() = default; - -IContentManagementInterface::IContentManagementInterface(Core::System& system_) - : ServiceFramework{system_, "IContentManagementInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {11, nullptr, "CalculateApplicationOccupiedSize"}, - {43, nullptr, "CheckSdCardMountStatus"}, - {47, &IContentManagementInterface::GetTotalSpaceSize, "GetTotalSpaceSize"}, - {48, &IContentManagementInterface::GetFreeSpaceSize, "GetFreeSpaceSize"}, - {600, nullptr, "CountApplicationContentMeta"}, - {601, nullptr, "ListApplicationContentMetaStatus"}, - {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, - {607, nullptr, "IsAnyApplicationRunning"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IContentManagementInterface::~IContentManagementInterface() = default; - -void IContentManagementInterface::GetTotalSpaceSize(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto storage{rp.PopEnum<FileSys::StorageId>()}; - - LOG_INFO(Service_Capture, "called, storage={}", storage); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(system.GetFileSystemController().GetTotalSpaceSize(storage)); -} - -void IContentManagementInterface::GetFreeSpaceSize(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto storage{rp.PopEnum<FileSys::StorageId>()}; - - LOG_INFO(Service_Capture, "called, storage={}", storage); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(system.GetFileSystemController().GetFreeSpaceSize(storage)); -} - -IDocumentInterface::IDocumentInterface(Core::System& system_) - : ServiceFramework{system_, "IDocumentInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {21, nullptr, "GetApplicationContentPath"}, - {23, &IDocumentInterface::ResolveApplicationContentPath, "ResolveApplicationContentPath"}, - {92, &IDocumentInterface::GetRunningApplicationProgramId, "GetRunningApplicationProgramId"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IDocumentInterface::~IDocumentInterface() = default; - -void IDocumentInterface::ResolveApplicationContentPath(HLERequestContext& ctx) { - struct ContentPath { - u8 file_system_proxy_type; - u64 program_id; - }; - static_assert(sizeof(ContentPath) == 0x10, "ContentPath has wrong size"); - - IPC::RequestParser rp{ctx}; - auto content_path = rp.PopRaw<ContentPath>(); - LOG_WARNING(Service_NS, "(STUBBED) called, file_system_proxy_type={}, program_id={:016X}", - content_path.file_system_proxy_type, content_path.program_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IDocumentInterface::GetRunningApplicationProgramId(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto caller_program_id = rp.PopRaw<u64>(); - LOG_WARNING(Service_NS, "(STUBBED) called, caller_program_id={:016X}", caller_program_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(system.GetApplicationProcessProgramID()); -} - -IDownloadTaskInterface::IDownloadTaskInterface(Core::System& system_) - : ServiceFramework{system_, "IDownloadTaskInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {701, nullptr, "ClearTaskStatusList"}, - {702, nullptr, "RequestDownloadTaskList"}, - {703, nullptr, "RequestEnsureDownloadTask"}, - {704, nullptr, "ListDownloadTaskStatus"}, - {705, nullptr, "RequestDownloadTaskListData"}, - {706, nullptr, "TryCommitCurrentApplicationDownloadTask"}, - {707, nullptr, "EnableAutoCommit"}, - {708, nullptr, "DisableAutoCommit"}, - {709, nullptr, "TriggerDynamicCommitEvent"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IDownloadTaskInterface::~IDownloadTaskInterface() = default; - -IECommerceInterface::IECommerceInterface(Core::System& system_) - : ServiceFramework{system_, "IECommerceInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "RequestLinkDevice"}, - {1, nullptr, "RequestCleanupAllPreInstalledApplications"}, - {2, nullptr, "RequestCleanupPreInstalledApplication"}, - {3, nullptr, "RequestSyncRights"}, - {4, nullptr, "RequestUnlinkDevice"}, - {5, nullptr, "RequestRevokeAllELicense"}, - {6, nullptr, "RequestSyncRightsBasedOnAssignedELicenses"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IECommerceInterface::~IECommerceInterface() = default; - -IFactoryResetInterface::IFactoryResetInterface(Core::System& system_) - : ServiceFramework{system_, "IFactoryResetInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {100, nullptr, "ResetToFactorySettings"}, - {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, - {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, - {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"}, - {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"}, - {105, nullptr, "RequestResetToFactorySettingsSecurely"}, - {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IFactoryResetInterface::~IFactoryResetInterface() = default; - -IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::System& system_) - : ServiceFramework{system_, "IReadOnlyApplicationRecordInterface"} { - static const FunctionInfo functions[] = { - {0, &IReadOnlyApplicationRecordInterface::HasApplicationRecord, "HasApplicationRecord"}, - {1, nullptr, "NotifyApplicationFailure"}, - {2, &IReadOnlyApplicationRecordInterface::IsDataCorruptedResult, "IsDataCorruptedResult"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IReadOnlyApplicationRecordInterface::~IReadOnlyApplicationRecordInterface() = default; - -void IReadOnlyApplicationRecordInterface::HasApplicationRecord(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 program_id = rp.PopRaw<u64>(); - LOG_WARNING(Service_NS, "(STUBBED) called, program_id={:X}", program_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(1); -} - -void IReadOnlyApplicationRecordInterface::IsDataCorruptedResult(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto result = rp.PopRaw<Result>(); - LOG_WARNING(Service_NS, "(STUBBED) called, result={:#x}", result.GetInnerValue()); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(0); -} - -IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface( - Core::System& system_) - : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IReadOnlyApplicationControlDataInterface::GetApplicationControlData, "GetApplicationControlData"}, - {1, nullptr, "GetApplicationDesiredLanguage"}, - {2, nullptr, "ConvertApplicationLanguageToLanguageCode"}, - {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, - {4, nullptr, "SelectApplicationDesiredLanguage"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default; - -void IReadOnlyApplicationControlDataInterface::GetApplicationControlData(HLERequestContext& ctx) { - enum class ApplicationControlSource : u8 { - CacheOnly, - Storage, - StorageOnly, - }; - - struct RequestParameters { - ApplicationControlSource source; - u64 application_id; - }; - static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size."); - - IPC::RequestParser rp{ctx}; - std::vector<u8> nacp_data{}; - const auto parameters{rp.PopRaw<RequestParameters>()}; - const auto result = - system.GetARPManager().GetControlProperty(&nacp_data, parameters.application_id); - - if (result == ResultSuccess) { - ctx.WriteBuffer(nacp_data.data(), nacp_data.size()); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} { - // clang-format off - static const FunctionInfo functions[] = { - {7988, nullptr, "GetDynamicRightsInterface"}, - {7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"}, - {7991, &NS::PushInterface<IReadOnlyApplicationRecordInterface>, "GetReadOnlyApplicationRecordInterface"}, - {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, - {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, - {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"}, - {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"}, - {7996, &NS::PushIApplicationManagerInterface, "GetApplicationManagerInterface"}, - {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"}, - {7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"}, - {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -NS::~NS() = default; - -std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const { - return GetInterface<IApplicationManagerInterface>(system); -} - -class NS_DEV final : public ServiceFramework<NS_DEV> { -public: - explicit NS_DEV(Core::System& system_) : ServiceFramework{system_, "ns:dev"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "LaunchProgram"}, - {1, nullptr, "TerminateProcess"}, - {2, nullptr, "TerminateProgram"}, - {4, nullptr, "GetShellEvent"}, - {5, nullptr, "GetShellEventInfo"}, - {6, nullptr, "TerminateApplication"}, - {7, nullptr, "PrepareLaunchProgramFromHost"}, - {8, nullptr, "LaunchApplicationFromHost"}, - {9, nullptr, "LaunchApplicationWithStorageIdForDevelop"}, - {10, nullptr, "IsSystemMemoryResourceLimitBoosted"}, - {11, nullptr, "GetRunningApplicationProcessIdForDevelop"}, - {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActiveForDevelop"}, - {13, nullptr, "CreateApplicationResourceForDevelop"}, - {14, nullptr, "IsPreomiaForDevelop"}, - {15, nullptr, "GetApplicationProgramIdFromHost"}, - {16, nullptr, "RefreshCachedDebugValues"}, - {17, nullptr, "PrepareLaunchApplicationFromHost"}, - {18, nullptr, "GetLaunchEvent"}, - {19, nullptr, "GetLaunchResult"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - -class ISystemUpdateControl final : public ServiceFramework<ISystemUpdateControl> { -public: - explicit ISystemUpdateControl(Core::System& system_) - : ServiceFramework{system_, "ISystemUpdateControl"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "HasDownloaded"}, - {1, nullptr, "RequestCheckLatestUpdate"}, - {2, nullptr, "RequestDownloadLatestUpdate"}, - {3, nullptr, "GetDownloadProgress"}, - {4, nullptr, "ApplyDownloadedUpdate"}, - {5, nullptr, "RequestPrepareCardUpdate"}, - {6, nullptr, "GetPrepareCardUpdateProgress"}, - {7, nullptr, "HasPreparedCardUpdate"}, - {8, nullptr, "ApplyCardUpdate"}, - {9, nullptr, "GetDownloadedEulaDataSize"}, - {10, nullptr, "GetDownloadedEulaData"}, - {11, nullptr, "SetupCardUpdate"}, - {12, nullptr, "GetPreparedCardUpdateEulaDataSize"}, - {13, nullptr, "GetPreparedCardUpdateEulaData"}, - {14, nullptr, "SetupCardUpdateViaSystemUpdater"}, - {15, nullptr, "HasReceived"}, - {16, nullptr, "RequestReceiveSystemUpdate"}, - {17, nullptr, "GetReceiveProgress"}, - {18, nullptr, "ApplyReceivedUpdate"}, - {19, nullptr, "GetReceivedEulaDataSize"}, - {20, nullptr, "GetReceivedEulaData"}, - {21, nullptr, "SetupToReceiveSystemUpdate"}, - {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - -class NS_SU final : public ServiceFramework<NS_SU> { -public: - explicit NS_SU(Core::System& system_) : ServiceFramework{system_, "ns:su"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetBackgroundNetworkUpdateState"}, - {1, &NS_SU::OpenSystemUpdateControl, "OpenSystemUpdateControl"}, - {2, nullptr, "NotifyExFatDriverRequired"}, - {3, nullptr, "ClearExFatDriverStatusForDebug"}, - {4, nullptr, "RequestBackgroundNetworkUpdate"}, - {5, nullptr, "NotifyBackgroundNetworkUpdate"}, - {6, nullptr, "NotifyExFatDriverDownloadedForDebug"}, - {9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"}, - {10, nullptr, "NotifySystemUpdateForContentDelivery"}, - {11, nullptr, "PrepareShutdown"}, - {12, nullptr, "Unknown12"}, - {13, nullptr, "Unknown13"}, - {14, nullptr, "Unknown14"}, - {15, nullptr, "Unknown15"}, - {16, nullptr, "DestroySystemUpdateTask"}, - {17, nullptr, "RequestSendSystemUpdate"}, - {18, nullptr, "GetSendSystemUpdateProgress"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void OpenSystemUpdateControl(HLERequestContext& ctx) { - LOG_DEBUG(Service_NS, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemUpdateControl>(system); - } -}; - -class NS_VM final : public ServiceFramework<NS_VM> { -public: - explicit NS_VM(Core::System& system_) : ServiceFramework{system_, "ns:vm"} { - // clang-format off - static const FunctionInfo functions[] = { - {1200, &NS_VM::NeedsUpdateVulnerability, "NeedsUpdateVulnerability"}, - {1201, nullptr, "UpdateSafeSystemVersionForDebug"}, - {1202, nullptr, "GetSafeSystemVersion"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void NeedsUpdateVulnerability(HLERequestContext& ctx) { - LOG_WARNING(Service_NS, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(false); - } -}; - void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); - server_manager->RegisterNamedService("ns:am2", std::make_shared<NS>("ns:am2", system)); - server_manager->RegisterNamedService("ns:ec", std::make_shared<NS>("ns:ec", system)); - server_manager->RegisterNamedService("ns:rid", std::make_shared<NS>("ns:rid", system)); - server_manager->RegisterNamedService("ns:rt", std::make_shared<NS>("ns:rt", system)); - server_manager->RegisterNamedService("ns:web", std::make_shared<NS>("ns:web", system)); - server_manager->RegisterNamedService("ns:ro", std::make_shared<NS>("ns:ro", system)); - - server_manager->RegisterNamedService("ns:dev", std::make_shared<NS_DEV>(system)); - server_manager->RegisterNamedService("ns:su", std::make_shared<NS_SU>(system)); - server_manager->RegisterNamedService("ns:vm", std::make_shared<NS_VM>(system)); - server_manager->RegisterNamedService("pdm:qry", std::make_shared<PDM_QRY>(system)); + server_manager->RegisterNamedService( + "ns:am2", std::make_shared<IServiceGetterInterface>(system, "ns:am2")); + server_manager->RegisterNamedService( + "ns:ec", std::make_shared<IServiceGetterInterface>(system, "ns:ec")); + server_manager->RegisterNamedService( + "ns:rid", std::make_shared<IServiceGetterInterface>(system, "ns:rid")); + server_manager->RegisterNamedService( + "ns:rt", std::make_shared<IServiceGetterInterface>(system, "ns:rt")); + server_manager->RegisterNamedService( + "ns:web", std::make_shared<IServiceGetterInterface>(system, "ns:web")); + server_manager->RegisterNamedService( + "ns:ro", std::make_shared<IServiceGetterInterface>(system, "ns:ro")); + + server_manager->RegisterNamedService("ns:dev", std::make_shared<IDevelopInterface>(system)); + server_manager->RegisterNamedService("ns:su", std::make_shared<ISystemUpdateInterface>(system)); + server_manager->RegisterNamedService("ns:vm", + std::make_shared<IVulnerabilityManagerInterface>(system)); + server_manager->RegisterNamedService("pdm:qry", std::make_shared<IQueryService>(system)); server_manager->RegisterNamedService("pl:s", std::make_shared<IPlatformServiceManager>(system, "pl:s")); diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index 9ee306ef9..f79b4ae3d 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -3,141 +3,12 @@ #pragma once -#include "core/hle/service/service.h" - namespace Core { class System; } -namespace Service { - -namespace FileSystem { -class FileSystemController; -} // namespace FileSystem - -namespace NS { - -class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { -public: - explicit IAccountProxyInterface(Core::System& system_); - ~IAccountProxyInterface() override; -}; - -class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> { -public: - explicit IApplicationManagerInterface(Core::System& system_); - ~IApplicationManagerInterface() override; - - Result GetApplicationDesiredLanguage(u8* out_desired_language, u32 supported_languages); - Result ConvertApplicationLanguageToLanguageCode(u64* out_language_code, - u8 application_language); - -private: - void GetApplicationControlData(HLERequestContext& ctx); - void GetApplicationDesiredLanguage(HLERequestContext& ctx); - void ConvertApplicationLanguageToLanguageCode(HLERequestContext& ctx); -}; - -class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { -public: - explicit IApplicationVersionInterface(Core::System& system_); - ~IApplicationVersionInterface() override; -}; - -class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> { -public: - explicit IContentManagementInterface(Core::System& system_); - ~IContentManagementInterface() override; - -private: - void GetTotalSpaceSize(HLERequestContext& ctx); - void GetFreeSpaceSize(HLERequestContext& ctx); -}; - -class IDocumentInterface final : public ServiceFramework<IDocumentInterface> { -public: - explicit IDocumentInterface(Core::System& system_); - ~IDocumentInterface() override; - -private: - void ResolveApplicationContentPath(HLERequestContext& ctx); - void GetRunningApplicationProgramId(HLERequestContext& ctx); -}; - -class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> { -public: - explicit IDownloadTaskInterface(Core::System& system_); - ~IDownloadTaskInterface() override; -}; - -class IECommerceInterface final : public ServiceFramework<IECommerceInterface> { -public: - explicit IECommerceInterface(Core::System& system_); - ~IECommerceInterface() override; -}; - -class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> { -public: - explicit IFactoryResetInterface(Core::System& system_); - ~IFactoryResetInterface() override; -}; - -class IReadOnlyApplicationRecordInterface final - : public ServiceFramework<IReadOnlyApplicationRecordInterface> { -public: - explicit IReadOnlyApplicationRecordInterface(Core::System& system_); - ~IReadOnlyApplicationRecordInterface() override; - -private: - void HasApplicationRecord(HLERequestContext& ctx); - void IsDataCorruptedResult(HLERequestContext& ctx); -}; - -class IReadOnlyApplicationControlDataInterface final - : public ServiceFramework<IReadOnlyApplicationControlDataInterface> { -public: - explicit IReadOnlyApplicationControlDataInterface(Core::System& system_); - ~IReadOnlyApplicationControlDataInterface() override; - -private: - void GetApplicationControlData(HLERequestContext& ctx); -}; - -class NS final : public ServiceFramework<NS> { -public: - explicit NS(const char* name, Core::System& system_); - ~NS() override; - - std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const; - -private: - template <typename T, typename... Args> - void PushInterface(HLERequestContext& ctx) { - LOG_DEBUG(Service_NS, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<T>(system); - } - - void PushIApplicationManagerInterface(HLERequestContext& ctx) { - LOG_DEBUG(Service_NS, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IApplicationManagerInterface>(system); - } - - template <typename T, typename... Args> - std::shared_ptr<T> GetInterface(Args&&... args) const { - static_assert(std::is_base_of_v<SessionRequestHandler, T>, - "Not a base of ServiceFrameworkBase"); - - return std::make_shared<T>(std::forward<Args>(args)...); - } -}; +namespace Service::NS { void LoopProcess(Core::System& system); -} // namespace NS -} // namespace Service +} // namespace Service::NS diff --git a/src/core/hle/service/ns/errors.h b/src/core/hle/service/ns/ns_results.h index 16d2ea6f7..16d2ea6f7 100644 --- a/src/core/hle/service/ns/errors.h +++ b/src/core/hle/service/ns/ns_results.h diff --git a/src/core/hle/service/ns/ns_types.h b/src/core/hle/service/ns/ns_types.h new file mode 100644 index 000000000..38421b0f4 --- /dev/null +++ b/src/core/hle/service/ns/ns_types.h @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/uuid.h" +#include "core/file_sys/romfs_factory.h" + +namespace Service::NS { + +enum class ApplicationRecordType : u8 { + Installing = 2, + Installed = 3, + GameCardNotInserted = 5, + Archived = 11, + GameCard = 16, +}; + +enum class ApplicationControlSource : u8 { + CacheOnly = 0, + Storage = 1, + StorageOnly = 2, +}; + +enum class BackgroundNetworkUpdateState : u8 { + None, + InProgress, + Ready, +}; + +struct ApplicationRecord { + u64 application_id; + ApplicationRecordType type; + u8 unknown; + INSERT_PADDING_BYTES_NOINIT(0x6); + u8 unknown2; + INSERT_PADDING_BYTES_NOINIT(0x7); +}; +static_assert(sizeof(ApplicationRecord) == 0x18, "ApplicationRecord has incorrect size."); + +/// ApplicationView +struct ApplicationView { + u64 application_id; ///< ApplicationId. + u32 unk; ///< Unknown. + u32 flags; ///< Flags. + std::array<u8, 0x10> unk_x10; ///< Unknown. + u32 unk_x20; ///< Unknown. + u16 unk_x24; ///< Unknown. + std::array<u8, 0x2> unk_x26; ///< Unknown. + std::array<u8, 0x8> unk_x28; ///< Unknown. + std::array<u8, 0x10> unk_x30; ///< Unknown. + u32 unk_x40; ///< Unknown. + u8 unk_x44; ///< Unknown. + std::array<u8, 0xb> unk_x45; ///< Unknown. +}; +static_assert(sizeof(ApplicationView) == 0x50, "ApplicationView has incorrect size."); + +struct ApplicationRightsOnClient { + u64 application_id; + Common::UUID uid; + u8 flags; + u8 flags2; + INSERT_PADDING_BYTES_NOINIT(0x6); +}; +static_assert(sizeof(ApplicationRightsOnClient) == 0x20, + "ApplicationRightsOnClient has incorrect size."); + +/// NsPromotionInfo +struct PromotionInfo { + u64 start_timestamp; ///< POSIX timestamp for the promotion start. + u64 end_timestamp; ///< POSIX timestamp for the promotion end. + s64 remaining_time; ///< Remaining time until the promotion ends, in nanoseconds + ///< ({end_timestamp - current_time} converted to nanoseconds). + INSERT_PADDING_BYTES_NOINIT(0x4); + u8 flags; ///< Flags. Bit0: whether the PromotionInfo is valid (including bit1). Bit1 clear: + ///< remaining_time is set. + INSERT_PADDING_BYTES_NOINIT(0x3); +}; +static_assert(sizeof(PromotionInfo) == 0x20, "PromotionInfo has incorrect size."); + +/// NsApplicationViewWithPromotionInfo +struct ApplicationViewWithPromotionInfo { + ApplicationView view; ///< \ref NsApplicationView + PromotionInfo promotion; ///< \ref NsPromotionInfo +}; +static_assert(sizeof(ApplicationViewWithPromotionInfo) == 0x70, + "ApplicationViewWithPromotionInfo has incorrect size."); + +struct ApplicationOccupiedSizeEntity { + FileSys::StorageId storage_id; + u64 app_size; + u64 patch_size; + u64 aoc_size; +}; +static_assert(sizeof(ApplicationOccupiedSizeEntity) == 0x20, + "ApplicationOccupiedSizeEntity has incorrect size."); + +struct ApplicationOccupiedSize { + std::array<ApplicationOccupiedSizeEntity, 4> entities; +}; +static_assert(sizeof(ApplicationOccupiedSize) == 0x80, + "ApplicationOccupiedSize has incorrect size."); + +struct ContentPath { + u8 file_system_proxy_type; + u64 program_id; +}; +static_assert(sizeof(ContentPath) == 0x10, "ContentPath has incorrect size."); + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp deleted file mode 100644 index ce0ee30e0..000000000 --- a/src/core/hle/service/ns/pdm_qry.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <memory> - -#include "common/logging/log.h" -#include "common/uuid.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/ns/pdm_qry.h" -#include "core/hle/service/service.h" - -namespace Service::NS { - -PDM_QRY::PDM_QRY(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "QueryAppletEvent"}, - {1, nullptr, "QueryPlayStatistics"}, - {2, nullptr, "QueryPlayStatisticsByUserAccountId"}, - {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"}, - {4, nullptr, "QueryPlayStatisticsByApplicationId"}, - {5, &PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId, "QueryPlayStatisticsByApplicationIdAndUserAccountId"}, - {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"}, - {7, nullptr, "QueryLastPlayTimeV0"}, - {8, nullptr, "QueryPlayEvent"}, - {9, nullptr, "GetAvailablePlayEventRange"}, - {10, nullptr, "QueryAccountEvent"}, - {11, nullptr, "QueryAccountPlayEvent"}, - {12, nullptr, "GetAvailableAccountPlayEventRange"}, - {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"}, - {14, nullptr, "QueryRecentlyPlayedApplication"}, - {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"}, - {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"}, - {17, nullptr, "QueryLastPlayTime"}, - {18, nullptr, "QueryApplicationPlayStatisticsForSystem"}, - {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -PDM_QRY::~PDM_QRY() = default; - -void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto unknown = rp.Pop<bool>(); - rp.Pop<u8>(); // Padding - const auto application_id = rp.Pop<u64>(); - const auto user_account_uid = rp.PopRaw<Common::UUID>(); - - // TODO(German77): Read statistics of the game - PlayStatistics statistics{ - .application_id = application_id, - .total_launches = 1, - }; - - LOG_WARNING(Service_NS, - "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}", - unknown, application_id, user_account_uid.RawString()); - - IPC::ResponseBuilder rb{ctx, 12}; - rb.Push(ResultSuccess); - rb.PushRaw(statistics); -} - -} // namespace Service::NS diff --git a/src/core/hle/service/ns/pdm_qry.h b/src/core/hle/service/ns/pdm_qry.h deleted file mode 100644 index c98e01660..000000000 --- a/src/core/hle/service/ns/pdm_qry.h +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::NS { - -struct PlayStatistics { - u64 application_id{}; - u32 first_entry_index{}; - u32 first_timestamp_user{}; - u32 first_timestamp_network{}; - u32 last_entry_index{}; - u32 last_timestamp_user{}; - u32 last_timestamp_network{}; - u32 play_time_in_minutes{}; - u32 total_launches{}; -}; -static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size"); - -class PDM_QRY final : public ServiceFramework<PDM_QRY> { -public: - explicit PDM_QRY(Core::System& system_); - ~PDM_QRY() override; - -private: - void QueryPlayStatisticsByApplicationIdAndUserAccountId(HLERequestContext& ctx); -}; - -} // namespace Service::NS diff --git a/src/core/hle/service/ns/platform_service_manager.cpp b/src/core/hle/service/ns/platform_service_manager.cpp new file mode 100644 index 000000000..23cf05005 --- /dev/null +++ b/src/core/hle/service/ns/platform_service_manager.cpp @@ -0,0 +1,273 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <cstring> +#include <vector> + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/swap.h" +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/system_archive/system_archive.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/physical_memory.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/ns/platform_service_manager.h" + +namespace Service::NS { + +struct FontRegion { + u32 offset; + u32 size; +}; + +// The below data is specific to shared font data dumped from Switch on f/w 2.2 +// Virtual address and offsets/sizes likely will vary by dump +[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; +constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be +constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be +constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; +constexpr FontRegion EMPTY_REGION{0, 0}; + +static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output, + std::size_t& offset) { + ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, + "Shared fonts exceeds 17mb!"); + ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); + + const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor + std::vector<u32> transformed_font(input.size()); + // TODO(ogniK): Figure out a better way to do this + std::transform(input.begin(), input.end(), transformed_font.begin(), + [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); }); + transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size + std::memcpy(output.data() + offset, transformed_font.data(), + transformed_font.size() * sizeof(u32)); + offset += transformed_font.size() * sizeof(u32); +} + +void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output) { + ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); + + if (input.size() < 2) { + LOG_ERROR(Service_NS, "Input font is empty"); + return; + } + + const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor + std::vector<u32> transformed_font(input.size()); + // TODO(ogniK): Figure out a better way to do this + std::transform(input.begin(), input.end(), transformed_font.begin(), + [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); }); + std::memcpy(output.data(), transformed_font.data() + 2, + (transformed_font.size() - 2) * sizeof(u32)); +} + +void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, + std::size_t& offset) { + ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, + "Shared fonts exceeds 17mb!"); + + const auto key = Common::swap32(EXPECTED_RESULT ^ EXPECTED_MAGIC); + std::vector<u32> transformed_font(input.size() + 2); + transformed_font[0] = Common::swap32(EXPECTED_MAGIC); + transformed_font[1] = Common::swap32(static_cast<u32>(input.size() * sizeof(u32))) ^ key; + std::transform(input.begin(), input.end(), transformed_font.begin() + 2, + [key](u32 in) { return in ^ key; }); + std::memcpy(output.data() + offset, transformed_font.data(), + transformed_font.size() * sizeof(u32)); + offset += transformed_font.size() * sizeof(u32); +} + +// Helper function to make BuildSharedFontsRawRegions a bit nicer +static u32 GetU32Swapped(const u8* data) { + u32 value; + std::memcpy(&value, data, sizeof(value)); + return Common::swap32(value); +} + +struct IPlatformServiceManager::Impl { + const FontRegion& GetSharedFontRegion(std::size_t index) const { + if (index >= shared_font_regions.size() || shared_font_regions.empty()) { + // No font fallback + return EMPTY_REGION; + } + return shared_font_regions.at(index); + } + + void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) { + // As we can derive the xor key we can just populate the offsets + // based on the shared memory dump + unsigned cur_offset = 0; + + for (std::size_t i = 0; i < SHARED_FONTS.size(); i++) { + // Out of shared fonts/invalid font + if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) { + break; + } + + // Derive key within inverse xor + const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^ EXPECTED_MAGIC; + const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY; + shared_font_regions.push_back(FontRegion{cur_offset + 8, SIZE}); + cur_offset += SIZE + 8; + } + } + + /// Backing memory for the shared font data + std::shared_ptr<Kernel::PhysicalMemory> shared_font; + + // Automatically populated based on shared_fonts dump or system archives. + std::vector<FontRegion> shared_font_regions; +}; + +IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_) + : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IPlatformServiceManager::RequestLoad>, "RequestLoad"}, + {1, D<&IPlatformServiceManager::GetLoadState>, "GetLoadState"}, + {2, D<&IPlatformServiceManager::GetSize>, "GetSize"}, + {3, D<&IPlatformServiceManager::GetSharedMemoryAddressOffset>, "GetSharedMemoryAddressOffset"}, + {4, D<&IPlatformServiceManager::GetSharedMemoryNativeHandle>, "GetSharedMemoryNativeHandle"}, + {5, D<&IPlatformServiceManager::GetSharedFontInOrderOfPriority>, "GetSharedFontInOrderOfPriority"}, + {6, D<&IPlatformServiceManager::GetSharedFontInOrderOfPriority>, "GetSharedFontInOrderOfPriorityForSystem"}, + {100, nullptr, "RequestApplicationFunctionAuthorization"}, + {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"}, + {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"}, + {103, nullptr, "RefreshApplicationFunctionBlackListDebugRecord"}, + {104, nullptr, "RequestApplicationFunctionAuthorizationByProgramId"}, + {105, nullptr, "GetFunctionBlackListSystemVersionToAuthorize"}, + {106, nullptr, "GetFunctionBlackListVersion"}, + {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"}, + {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"}, + }; + // clang-format on + RegisterHandlers(functions); + + auto& fsc = system.GetFileSystemController(); + + // Attempt to load shared font data from disk + const auto* nand = fsc.GetSystemNANDContents(); + std::size_t offset = 0; + // Rebuild shared fonts from data ncas or synthesize + + impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE); + for (auto font : SHARED_FONTS) { + FileSys::VirtualFile romfs; + const auto nca = + nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data); + if (nca) { + romfs = nca->GetRomFS(); + } + + if (!romfs) { + romfs = FileSys::SystemArchive::SynthesizeSystemArchive(static_cast<u64>(font.first)); + } + + if (!romfs) { + LOG_ERROR(Service_NS, "Failed to find or synthesize {:016X}! Skipping", font.first); + continue; + } + + const auto extracted_romfs = FileSys::ExtractRomFS(romfs); + if (!extracted_romfs) { + LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", font.first); + continue; + } + const auto font_fp = extracted_romfs->GetFile(font.second); + if (!font_fp) { + LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", font.first, font.second); + continue; + } + std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32)); + font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize()); + // We need to be BigEndian as u32s for the xor encryption + std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), + Common::swap32); + // Font offset and size do not account for the header + const FontRegion region{static_cast<u32>(offset + 8), + static_cast<u32>((font_data_u32.size() * sizeof(u32)) - 8)}; + DecryptSharedFont(font_data_u32, *impl->shared_font, offset); + impl->shared_font_regions.push_back(region); + } +} + +IPlatformServiceManager::~IPlatformServiceManager() = default; + +Result IPlatformServiceManager::RequestLoad(SharedFontType type) { + // Games don't call this so all fonts should be loaded + LOG_DEBUG(Service_NS, "called, shared_font_type={}", type); + R_SUCCEED(); +} + +Result IPlatformServiceManager::GetLoadState(Out<LoadState> out_load_state, SharedFontType type) { + LOG_DEBUG(Service_NS, "called, shared_font_type={}", type); + *out_load_state = LoadState::Loaded; + R_SUCCEED(); +} + +Result IPlatformServiceManager::GetSize(Out<u32> out_size, SharedFontType type) { + LOG_DEBUG(Service_NS, "called, shared_font_type={}", type); + *out_size = impl->GetSharedFontRegion(static_cast<size_t>(type)).size; + R_SUCCEED(); +} + +Result IPlatformServiceManager::GetSharedMemoryAddressOffset(Out<u32> out_shared_memory_offset, + SharedFontType type) { + LOG_DEBUG(Service_NS, "called, shared_font_type={}", type); + *out_shared_memory_offset = impl->GetSharedFontRegion(static_cast<size_t>(type)).offset; + R_SUCCEED(); +} + +Result IPlatformServiceManager::GetSharedMemoryNativeHandle( + OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_native_handle) { + // Map backing memory for the font data + LOG_DEBUG(Service_NS, "called"); + + // Create shared font memory object + std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(), + impl->shared_font->size()); + + // FIXME: this shouldn't belong to the kernel + *out_shared_memory_native_handle = &kernel.GetFontSharedMem(); + R_SUCCEED(); +} + +Result IPlatformServiceManager::GetSharedFontInOrderOfPriority( + OutArray<u32, BufferAttr_HipcMapAlias> out_font_codes, + OutArray<u32, BufferAttr_HipcMapAlias> out_font_offsets, + OutArray<u32, BufferAttr_HipcMapAlias> out_font_sizes, Out<bool> out_fonts_are_loaded, + Out<u32> out_font_count, Set::LanguageCode language_code) { + LOG_DEBUG(Service_NS, "called, language_code={:#x}", language_code); + + // The maximum number of elements that can be returned is 6. Regardless of the available fonts + // or buffer size. + constexpr size_t MaxElementCount = 6; + + // TODO(ogniK): Have actual priority order + const auto max_size = std::min({MaxElementCount, out_font_codes.size(), out_font_offsets.size(), + out_font_sizes.size(), impl->shared_font_regions.size()}); + + for (size_t i = 0; i < max_size; i++) { + auto region = impl->GetSharedFontRegion(i); + + out_font_codes[i] = static_cast<u32>(i); + out_font_offsets[i] = region.offset; + out_font_sizes[i] = region.size; + } + + *out_fonts_are_loaded = true; + *out_font_count = static_cast<u32>(max_size); + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/platform_service_manager.h b/src/core/hle/service/ns/platform_service_manager.h new file mode 100644 index 000000000..b82c385a6 --- /dev/null +++ b/src/core/hle/service/ns/platform_service_manager.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <vector> +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" +#include "core/hle/service/set/settings_types.h" + +namespace Service { + +namespace FileSystem { +class FileSystemController; +} // namespace FileSystem + +namespace NS { + +enum class FontArchives : u64 { + Extension = 0x0100000000000810, + Standard = 0x0100000000000811, + Korean = 0x0100000000000812, + ChineseTraditional = 0x0100000000000813, + ChineseSimple = 0x0100000000000814, +}; + +enum class SharedFontType : u32 { + JapanUSEuropeStandard = 0, + ChineseSimplified = 1, + ExtendedChineseSimplified = 2, + ChineseTraditional = 3, + KoreanHangul = 4, + NintendoExtended = 5, +}; + +enum class LoadState : u32 { + Loading = 0, + Loaded = 1, +}; + +constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{ + std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"), + std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"), + std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"), + std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"), + std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"), + std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"), + std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"), +}; + +void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output); +void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset); + +class IPlatformServiceManager final : public ServiceFramework<IPlatformServiceManager> { +public: + explicit IPlatformServiceManager(Core::System& system_, const char* service_name_); + ~IPlatformServiceManager() override; + +private: + Result RequestLoad(SharedFontType type); + Result GetLoadState(Out<LoadState> out_load_state, SharedFontType type); + Result GetSize(Out<u32> out_size, SharedFontType type); + Result GetSharedMemoryAddressOffset(Out<u32> out_shared_memory_offset, SharedFontType type); + Result GetSharedMemoryNativeHandle( + OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_native_handle); + Result GetSharedFontInOrderOfPriority(OutArray<u32, BufferAttr_HipcMapAlias> out_font_codes, + OutArray<u32, BufferAttr_HipcMapAlias> out_font_offsets, + OutArray<u32, BufferAttr_HipcMapAlias> out_font_sizes, + Out<bool> out_fonts_are_loaded, Out<u32> out_font_count, + Set::LanguageCode language_code); + + struct Impl; + std::unique_ptr<Impl> impl; +}; + +} // namespace NS + +} // namespace Service diff --git a/src/core/hle/service/ns/query_service.cpp b/src/core/hle/service/ns/query_service.cpp new file mode 100644 index 000000000..946b7fa23 --- /dev/null +++ b/src/core/hle/service/ns/query_service.cpp @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "common/uuid.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ns/query_service.h" +#include "core/hle/service/service.h" + +namespace Service::NS { + +IQueryService::IQueryService(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "QueryAppletEvent"}, + {1, nullptr, "QueryPlayStatistics"}, + {2, nullptr, "QueryPlayStatisticsByUserAccountId"}, + {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"}, + {4, nullptr, "QueryPlayStatisticsByApplicationId"}, + {5, D<&IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId>, "QueryPlayStatisticsByApplicationIdAndUserAccountId"}, + {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"}, + {7, nullptr, "QueryLastPlayTimeV0"}, + {8, nullptr, "QueryPlayEvent"}, + {9, nullptr, "GetAvailablePlayEventRange"}, + {10, nullptr, "QueryAccountEvent"}, + {11, nullptr, "QueryAccountPlayEvent"}, + {12, nullptr, "GetAvailableAccountPlayEventRange"}, + {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"}, + {14, nullptr, "QueryRecentlyPlayedApplication"}, + {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"}, + {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"}, + {17, nullptr, "QueryLastPlayTime"}, + {18, nullptr, "QueryApplicationPlayStatisticsForSystem"}, + {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IQueryService::~IQueryService() = default; + +Result IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId( + Out<PlayStatistics> out_play_statistics, bool unknown, Common::UUID account_id, + u64 application_id) { + // TODO(German77): Read statistics of the game + *out_play_statistics = { + .application_id = application_id, + .total_launches = 1, + }; + + LOG_WARNING(Service_NS, "(STUBBED) called. unknown={}. application_id={:016X}, account_id={}", + unknown, application_id, account_id.FormattedString()); + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/query_service.h b/src/core/hle/service/ns/query_service.h new file mode 100644 index 000000000..6cdbfa277 --- /dev/null +++ b/src/core/hle/service/ns/query_service.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/uuid.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::NS { + +struct PlayStatistics { + u64 application_id{}; + u32 first_entry_index{}; + u32 first_timestamp_user{}; + u32 first_timestamp_network{}; + u32 last_entry_index{}; + u32 last_timestamp_user{}; + u32 last_timestamp_network{}; + u32 play_time_in_minutes{}; + u32 total_launches{}; +}; +static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size"); + +class IQueryService final : public ServiceFramework<IQueryService> { +public: + explicit IQueryService(Core::System& system_); + ~IQueryService() override; + +private: + Result QueryPlayStatisticsByApplicationIdAndUserAccountId( + Out<PlayStatistics> out_play_statistics, bool unknown, Common::UUID account_id, + u64 application_id); +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp new file mode 100644 index 000000000..9b2ca94a4 --- /dev/null +++ b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp @@ -0,0 +1,122 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ns/language.h" +#include "core/hle/service/ns/ns_results.h" +#include "core/hle/service/ns/read_only_application_control_data_interface.h" +#include "core/hle/service/set/settings_server.h" + +namespace Service::NS { + +IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface( + Core::System& system_) + : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData>, "GetApplicationControlData"}, + {1, D<&IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage>, "GetApplicationDesiredLanguage"}, + {2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"}, + {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, + {4, nullptr, "SelectApplicationDesiredLanguage"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default; + +Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData( + OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size, + ApplicationControlSource application_control_source, u64 application_id) { + LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}", + application_control_source, application_id); + + const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), + system.GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + const auto size = out_buffer.size(); + + const auto icon_size = control.second ? control.second->GetSize() : 0; + const auto total_size = sizeof(FileSys::RawNACP) + icon_size; + + if (size < total_size) { + LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)", + size); + R_THROW(ResultUnknown); + } + + if (control.first != nullptr) { + const auto bytes = control.first->GetRawBytes(); + std::memcpy(out_buffer.data(), bytes.data(), bytes.size()); + } else { + LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero", + application_id); + std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP)); + } + + if (control.second != nullptr) { + control.second->Read(out_buffer.data() + sizeof(FileSys::RawNACP), icon_size); + } else { + LOG_WARNING(Service_NS, "missing icon data for application_id={:016X}", application_id); + } + + *out_actual_size = static_cast<u32>(total_size); + R_SUCCEED(); +} + +Result IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage( + Out<ApplicationLanguage> out_desired_language, u32 supported_languages) { + LOG_INFO(Service_NS, "called with supported_languages={:08X}", supported_languages); + + // Get language code from settings + const auto language_code = + Set::GetLanguageCodeFromIndex(static_cast<s32>(Settings::values.language_index.GetValue())); + + // Convert to application language, get priority list + const auto application_language = ConvertToApplicationLanguage(language_code); + if (application_language == std::nullopt) { + LOG_ERROR(Service_NS, "Could not convert application language! language_code={}", + language_code); + R_THROW(Service::NS::ResultApplicationLanguageNotFound); + } + const auto priority_list = GetApplicationLanguagePriorityList(*application_language); + if (!priority_list) { + LOG_ERROR(Service_NS, + "Could not find application language priorities! application_language={}", + *application_language); + R_THROW(Service::NS::ResultApplicationLanguageNotFound); + } + + // Try to find a valid language. + for (const auto lang : *priority_list) { + const auto supported_flag = GetSupportedLanguageFlag(lang); + if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) { + *out_desired_language = lang; + R_SUCCEED(); + } + } + + LOG_ERROR(Service_NS, "Could not find a valid language! supported_languages={:08X}", + supported_languages); + R_THROW(Service::NS::ResultApplicationLanguageNotFound); +} + +Result IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode( + Out<u64> out_language_code, ApplicationLanguage application_language) { + const auto language_code = ConvertToLanguageCode(application_language); + if (language_code == std::nullopt) { + LOG_ERROR(Service_NS, "Language not found! application_language={}", application_language); + R_THROW(Service::NS::ResultApplicationLanguageNotFound); + } + + *out_language_code = static_cast<u64>(*language_code); + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.h b/src/core/hle/service/ns/read_only_application_control_data_interface.h new file mode 100644 index 000000000..ac099435a --- /dev/null +++ b/src/core/hle/service/ns/read_only_application_control_data_interface.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/ns/language.h" +#include "core/hle/service/ns/ns_types.h" +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IReadOnlyApplicationControlDataInterface final + : public ServiceFramework<IReadOnlyApplicationControlDataInterface> { +public: + explicit IReadOnlyApplicationControlDataInterface(Core::System& system_); + ~IReadOnlyApplicationControlDataInterface() override; + +public: + Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer, + Out<u32> out_actual_size, + ApplicationControlSource application_control_source, + u64 application_id); + Result GetApplicationDesiredLanguage(Out<ApplicationLanguage> out_desired_language, + u32 supported_languages); + Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code, + ApplicationLanguage application_language); +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/read_only_application_record_interface.cpp b/src/core/hle/service/ns/read_only_application_record_interface.cpp new file mode 100644 index 000000000..816a1e1dc --- /dev/null +++ b/src/core/hle/service/ns/read_only_application_record_interface.cpp @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ns/read_only_application_record_interface.h" + +namespace Service::NS { + +IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::System& system_) + : ServiceFramework{system_, "IReadOnlyApplicationRecordInterface"} { + static const FunctionInfo functions[] = { + {0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"}, + {1, nullptr, "NotifyApplicationFailure"}, + {2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>, + "IsDataCorruptedResult"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IReadOnlyApplicationRecordInterface::~IReadOnlyApplicationRecordInterface() = default; + +Result IReadOnlyApplicationRecordInterface::HasApplicationRecord( + Out<bool> out_has_application_record, u64 program_id) { + LOG_WARNING(Service_NS, "(STUBBED) called, program_id={:016X}", program_id); + *out_has_application_record = true; + R_SUCCEED(); +} + +Result IReadOnlyApplicationRecordInterface::IsDataCorruptedResult( + Out<bool> out_is_data_corrupted_result, Result result) { + LOG_WARNING(Service_NS, "(STUBBED) called, result={:#x}", result.GetInnerValue()); + *out_is_data_corrupted_result = false; + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/read_only_application_record_interface.h b/src/core/hle/service/ns/read_only_application_record_interface.h new file mode 100644 index 000000000..d06e8f5e6 --- /dev/null +++ b/src/core/hle/service/ns/read_only_application_record_interface.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IReadOnlyApplicationRecordInterface final + : public ServiceFramework<IReadOnlyApplicationRecordInterface> { +public: + explicit IReadOnlyApplicationRecordInterface(Core::System& system_); + ~IReadOnlyApplicationRecordInterface() override; + +private: + Result HasApplicationRecord(Out<bool> out_has_application_record, u64 program_id); + Result IsDataCorruptedResult(Out<bool> out_is_data_corrupted_result, Result result); +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/service_getter_interface.cpp b/src/core/hle/service/ns/service_getter_interface.cpp new file mode 100644 index 000000000..1a3dd7166 --- /dev/null +++ b/src/core/hle/service/ns/service_getter_interface.cpp @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ns/account_proxy_interface.h" +#include "core/hle/service/ns/application_manager_interface.h" +#include "core/hle/service/ns/application_version_interface.h" +#include "core/hle/service/ns/content_management_interface.h" +#include "core/hle/service/ns/document_interface.h" +#include "core/hle/service/ns/download_task_interface.h" +#include "core/hle/service/ns/dynamic_rights_interface.h" +#include "core/hle/service/ns/ecommerce_interface.h" +#include "core/hle/service/ns/factory_reset_interface.h" +#include "core/hle/service/ns/read_only_application_control_data_interface.h" +#include "core/hle/service/ns/read_only_application_record_interface.h" +#include "core/hle/service/ns/service_getter_interface.h" + +namespace Service::NS { + +IServiceGetterInterface::IServiceGetterInterface(Core::System& system_, const char* name) + : ServiceFramework{system_, name} { + // clang-format off + static const FunctionInfo functions[] = { + {7988, D<&IServiceGetterInterface::GetDynamicRightsInterface>, "GetDynamicRightsInterface"}, + {7989, D<&IServiceGetterInterface::GetReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"}, + {7991, D<&IServiceGetterInterface::GetReadOnlyApplicationRecordInterface>, "GetReadOnlyApplicationRecordInterface"}, + {7992, D<&IServiceGetterInterface::GetECommerceInterface>, "GetECommerceInterface"}, + {7993, D<&IServiceGetterInterface::GetApplicationVersionInterface>, "GetApplicationVersionInterface"}, + {7994, D<&IServiceGetterInterface::GetFactoryResetInterface>, "GetFactoryResetInterface"}, + {7995, D<&IServiceGetterInterface::GetAccountProxyInterface>, "GetAccountProxyInterface"}, + {7996, D<&IServiceGetterInterface::GetApplicationManagerInterface>, "GetApplicationManagerInterface"}, + {7997, D<&IServiceGetterInterface::GetDownloadTaskInterface>, "GetDownloadTaskInterface"}, + {7998, D<&IServiceGetterInterface::GetContentManagementInterface>, "GetContentManagementInterface"}, + {7999, D<&IServiceGetterInterface::GetDocumentInterface>, "GetDocumentInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IServiceGetterInterface::~IServiceGetterInterface() = default; + +Result IServiceGetterInterface::GetDynamicRightsInterface( + Out<SharedPointer<IDynamicRightsInterface>> out_interface) { + LOG_DEBUG(Service_NS, "called"); + *out_interface = std::make_shared<IDynamicRightsInterface>(system); + R_SUCCEED(); +} + +Result IServiceGetterInterface::GetReadOnlyApplicationControlDataInterface( + Out<SharedPointer<IReadOnlyApplicationControlDataInterface>> out_interface) { + LOG_DEBUG(Service_NS, "called"); + *out_interface = std::make_shared<IReadOnlyApplicationControlDataInterface>(system); + R_SUCCEED(); +} + +Result IServiceGetterInterface::GetReadOnlyApplicationRecordInterface( + Out<SharedPointer<IReadOnlyApplicationRecordInterface>> out_interface) { + LOG_DEBUG(Service_NS, "called"); + *out_interface = std::make_shared<IReadOnlyApplicationRecordInterface>(system); + R_SUCCEED(); +} + +Result IServiceGetterInterface::GetECommerceInterface( + Out<SharedPointer<IECommerceInterface>> out_interface) { + LOG_DEBUG(Service_NS, "called"); + *out_interface = std::make_shared<IECommerceInterface>(system); + R_SUCCEED(); +} + +Result IServiceGetterInterface::GetApplicationVersionInterface( + Out<SharedPointer<IApplicationVersionInterface>> out_interface) { + LOG_DEBUG(Service_NS, "called"); + *out_interface = std::make_shared<IApplicationVersionInterface>(system); + R_SUCCEED(); +} + +Result IServiceGetterInterface::GetFactoryResetInterface( + Out<SharedPointer<IFactoryResetInterface>> out_interface) { + LOG_DEBUG(Service_NS, "called"); + *out_interface = std::make_shared<IFactoryResetInterface>(system); + R_SUCCEED(); +} + +Result IServiceGetterInterface::GetAccountProxyInterface( + Out<SharedPointer<IAccountProxyInterface>> out_interface) { + LOG_DEBUG(Service_NS, "called"); + *out_interface = std::make_shared<IAccountProxyInterface>(system); + R_SUCCEED(); +} + +Result IServiceGetterInterface::GetApplicationManagerInterface( + Out<SharedPointer<IApplicationManagerInterface>> out_interface) { + LOG_DEBUG(Service_NS, "called"); + *out_interface = std::make_shared<IApplicationManagerInterface>(system); + R_SUCCEED(); +} + +Result IServiceGetterInterface::GetDownloadTaskInterface( + Out<SharedPointer<IDownloadTaskInterface>> out_interface) { + LOG_DEBUG(Service_NS, "called"); + *out_interface = std::make_shared<IDownloadTaskInterface>(system); + R_SUCCEED(); +} + +Result IServiceGetterInterface::GetContentManagementInterface( + Out<SharedPointer<IContentManagementInterface>> out_interface) { + LOG_DEBUG(Service_NS, "called"); + *out_interface = std::make_shared<IContentManagementInterface>(system); + R_SUCCEED(); +} + +Result IServiceGetterInterface::GetDocumentInterface( + Out<SharedPointer<IDocumentInterface>> out_interface) { + LOG_DEBUG(Service_NS, "called"); + *out_interface = std::make_shared<IDocumentInterface>(system); + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/service_getter_interface.h b/src/core/hle/service/ns/service_getter_interface.h new file mode 100644 index 000000000..bbc18d444 --- /dev/null +++ b/src/core/hle/service/ns/service_getter_interface.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IDynamicRightsInterface; +class IReadOnlyApplicationControlDataInterface; +class IReadOnlyApplicationRecordInterface; +class IECommerceInterface; +class IApplicationVersionInterface; +class IFactoryResetInterface; +class IAccountProxyInterface; +class IApplicationManagerInterface; +class IDownloadTaskInterface; +class IContentManagementInterface; +class IDocumentInterface; + +class IServiceGetterInterface : public ServiceFramework<IServiceGetterInterface> { +public: + explicit IServiceGetterInterface(Core::System& system_, const char* name); + ~IServiceGetterInterface() override; + +public: + Result GetDynamicRightsInterface(Out<SharedPointer<IDynamicRightsInterface>> out_interface); + Result GetReadOnlyApplicationControlDataInterface( + Out<SharedPointer<IReadOnlyApplicationControlDataInterface>> out_interface); + Result GetReadOnlyApplicationRecordInterface( + Out<SharedPointer<IReadOnlyApplicationRecordInterface>> out_interface); + Result GetECommerceInterface(Out<SharedPointer<IECommerceInterface>> out_interface); + Result GetApplicationVersionInterface( + Out<SharedPointer<IApplicationVersionInterface>> out_interface); + Result GetFactoryResetInterface(Out<SharedPointer<IFactoryResetInterface>> out_interface); + Result GetAccountProxyInterface(Out<SharedPointer<IAccountProxyInterface>> out_interface); + Result GetApplicationManagerInterface( + Out<SharedPointer<IApplicationManagerInterface>> out_interface); + Result GetDownloadTaskInterface(Out<SharedPointer<IDownloadTaskInterface>> out_interface); + Result GetContentManagementInterface( + Out<SharedPointer<IContentManagementInterface>> out_interface); + Result GetDocumentInterface(Out<SharedPointer<IDocumentInterface>> out_interface); +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/system_update_control.cpp b/src/core/hle/service/ns/system_update_control.cpp new file mode 100644 index 000000000..f5f5cfd90 --- /dev/null +++ b/src/core/hle/service/ns/system_update_control.cpp @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ns/system_update_control.h" + +namespace Service::NS { + +ISystemUpdateControl::ISystemUpdateControl(Core::System& system_) + : ServiceFramework{system_, "ISystemUpdateControl"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "HasDownloaded"}, + {1, nullptr, "RequestCheckLatestUpdate"}, + {2, nullptr, "RequestDownloadLatestUpdate"}, + {3, nullptr, "GetDownloadProgress"}, + {4, nullptr, "ApplyDownloadedUpdate"}, + {5, nullptr, "RequestPrepareCardUpdate"}, + {6, nullptr, "GetPrepareCardUpdateProgress"}, + {7, nullptr, "HasPreparedCardUpdate"}, + {8, nullptr, "ApplyCardUpdate"}, + {9, nullptr, "GetDownloadedEulaDataSize"}, + {10, nullptr, "GetDownloadedEulaData"}, + {11, nullptr, "SetupCardUpdate"}, + {12, nullptr, "GetPreparedCardUpdateEulaDataSize"}, + {13, nullptr, "GetPreparedCardUpdateEulaData"}, + {14, nullptr, "SetupCardUpdateViaSystemUpdater"}, + {15, nullptr, "HasReceived"}, + {16, nullptr, "RequestReceiveSystemUpdate"}, + {17, nullptr, "GetReceiveProgress"}, + {18, nullptr, "ApplyReceivedUpdate"}, + {19, nullptr, "GetReceivedEulaDataSize"}, + {20, nullptr, "GetReceivedEulaData"}, + {21, nullptr, "SetupToReceiveSystemUpdate"}, + {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ISystemUpdateControl::~ISystemUpdateControl() = default; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/system_update_control.h b/src/core/hle/service/ns/system_update_control.h new file mode 100644 index 000000000..a30a09000 --- /dev/null +++ b/src/core/hle/service/ns/system_update_control.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::NS { + +class ISystemUpdateControl final : public ServiceFramework<ISystemUpdateControl> { +public: + explicit ISystemUpdateControl(Core::System& system_); + ~ISystemUpdateControl() override; +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/system_update_interface.cpp b/src/core/hle/service/ns/system_update_interface.cpp new file mode 100644 index 000000000..7e22ca3db --- /dev/null +++ b/src/core/hle/service/ns/system_update_interface.cpp @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ns/system_update_control.h" +#include "core/hle/service/ns/system_update_interface.h" + +namespace Service::NS { + +ISystemUpdateInterface::ISystemUpdateInterface(Core::System& system_) + : ServiceFramework{system_, "ns:su"}, service_context{system_, "ns:su"}, + update_notification_event{service_context} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&ISystemUpdateInterface::GetBackgroundNetworkUpdateState>, "GetBackgroundNetworkUpdateState"}, + {1, D<&ISystemUpdateInterface::OpenSystemUpdateControl>, "OpenSystemUpdateControl"}, + {2, nullptr, "NotifyExFatDriverRequired"}, + {3, nullptr, "ClearExFatDriverStatusForDebug"}, + {4, nullptr, "RequestBackgroundNetworkUpdate"}, + {5, nullptr, "NotifyBackgroundNetworkUpdate"}, + {6, nullptr, "NotifyExFatDriverDownloadedForDebug"}, + {9, D<&ISystemUpdateInterface::GetSystemUpdateNotificationEventForContentDelivery>, "GetSystemUpdateNotificationEventForContentDelivery"}, + {10, nullptr, "NotifySystemUpdateForContentDelivery"}, + {11, nullptr, "PrepareShutdown"}, + {12, nullptr, "Unknown12"}, + {13, nullptr, "Unknown13"}, + {14, nullptr, "Unknown14"}, + {15, nullptr, "Unknown15"}, + {16, nullptr, "DestroySystemUpdateTask"}, + {17, nullptr, "RequestSendSystemUpdate"}, + {18, nullptr, "GetSendSystemUpdateProgress"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ISystemUpdateInterface::~ISystemUpdateInterface() = default; + +Result ISystemUpdateInterface::GetBackgroundNetworkUpdateState( + Out<BackgroundNetworkUpdateState> out_background_network_update_state) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + *out_background_network_update_state = BackgroundNetworkUpdateState::None; + R_SUCCEED(); +} + +Result ISystemUpdateInterface::OpenSystemUpdateControl( + Out<SharedPointer<ISystemUpdateControl>> out_system_update_control) { + LOG_WARNING(Service_NS, "(STUBBED) called"); + *out_system_update_control = std::make_shared<ISystemUpdateControl>(system); + R_SUCCEED(); +} + +Result ISystemUpdateInterface::GetSystemUpdateNotificationEventForContentDelivery( + OutCopyHandle<Kernel::KReadableEvent> out_event) { + LOG_WARNING(Service_NS, "(STUBBED) called"); + *out_event = update_notification_event.GetHandle(); + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/system_update_interface.h b/src/core/hle/service/ns/system_update_interface.h new file mode 100644 index 000000000..36a2880ec --- /dev/null +++ b/src/core/hle/service/ns/system_update_interface.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/ns/ns_types.h" +#include "core/hle/service/os/event.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KReadableEvent; +} + +namespace Service::NS { + +class ISystemUpdateControl; + +class ISystemUpdateInterface final : public ServiceFramework<ISystemUpdateInterface> { +public: + explicit ISystemUpdateInterface(Core::System& system_); + ~ISystemUpdateInterface() override; + +private: + Result GetBackgroundNetworkUpdateState( + Out<BackgroundNetworkUpdateState> out_background_network_update_state); + Result OpenSystemUpdateControl( + Out<SharedPointer<ISystemUpdateControl>> out_system_update_control); + Result GetSystemUpdateNotificationEventForContentDelivery( + OutCopyHandle<Kernel::KReadableEvent> out_event); + +private: + KernelHelpers::ServiceContext service_context; + Event update_notification_event; +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/vulnerability_manager_interface.cpp b/src/core/hle/service/ns/vulnerability_manager_interface.cpp new file mode 100644 index 000000000..69c21fb89 --- /dev/null +++ b/src/core/hle/service/ns/vulnerability_manager_interface.cpp @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ns/vulnerability_manager_interface.h" + +namespace Service::NS { + +IVulnerabilityManagerInterface::IVulnerabilityManagerInterface(Core::System& system_) + : ServiceFramework{system_, "ns:vm"} { + // clang-format off + static const FunctionInfo functions[] = { + {1200, D<&IVulnerabilityManagerInterface::NeedsUpdateVulnerability>, "NeedsUpdateVulnerability"}, + {1201, nullptr, "UpdateSafeSystemVersionForDebug"}, + {1202, nullptr, "GetSafeSystemVersion"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IVulnerabilityManagerInterface::~IVulnerabilityManagerInterface() = default; + +Result IVulnerabilityManagerInterface::NeedsUpdateVulnerability( + Out<bool> out_needs_update_vulnerability) { + LOG_WARNING(Service_NS, "(STUBBED) called"); + *out_needs_update_vulnerability = false; + R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/vulnerability_manager_interface.h b/src/core/hle/service/ns/vulnerability_manager_interface.h new file mode 100644 index 000000000..c689cf7ec --- /dev/null +++ b/src/core/hle/service/ns/vulnerability_manager_interface.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::NS { + +class IVulnerabilityManagerInterface final + : public ServiceFramework<IVulnerabilityManagerInterface> { +public: + explicit IVulnerabilityManagerInterface(Core::System& system_); + ~IVulnerabilityManagerInterface() override; + +private: + Result NeedsUpdateVulnerability(Out<bool> out_needs_update_vulnerability); +}; + +} // namespace Service::NS diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp index e89cca6f2..9edce03f6 100644 --- a/src/core/hle/service/nvdrv/core/container.cpp +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -49,6 +49,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) { continue; } if (session.process == process) { + session.ref_count++; return session.id; } } @@ -66,6 +67,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) { } auto& session = impl->sessions[new_id]; session.is_active = true; + session.ref_count = 1; // Optimization if (process->IsApplication()) { auto& page_table = process->GetPageTable().GetBasePageTable(); @@ -114,8 +116,11 @@ SessionId Container::OpenSession(Kernel::KProcess* process) { void Container::CloseSession(SessionId session_id) { std::scoped_lock lk(impl->session_guard); - impl->file.UnmapAllHandles(session_id); auto& session = impl->sessions[session_id.id]; + if (--session.ref_count > 0) { + return; + } + impl->file.UnmapAllHandles(session_id); auto& smmu = impl->host1x.MemoryManager(); if (session.has_preallocated_area) { const DAddr region_start = session.mapper->GetRegionStart(); diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h index b4d3938a8..f159ced09 100644 --- a/src/core/hle/service/nvdrv/core/container.h +++ b/src/core/hle/service/nvdrv/core/container.h @@ -46,6 +46,7 @@ struct Session { bool has_preallocated_area{}; std::unique_ptr<HeapMapper> mapper{}; bool is_active{}; + s32 ref_count{}; }; class Container { diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index bc1c033c6..453cb5831 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -333,9 +333,13 @@ void NvMap::UnmapAllHandles(NvCore::SessionId session_id) { }(); for (auto& [id, handle] : handles_copy) { - if (handle->session_id.id == session_id.id) { - FreeHandle(id, false); + { + std::scoped_lock lk{handle->mutex}; + if (handle->session_id.id != session_id.id || handle->dupes <= 0) { + continue; + } } + FreeHandle(id, false); } } diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index abe95303e..995646e25 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -15,6 +15,22 @@ namespace Service::Nvidia::Devices { +namespace { + +Tegra::BlendMode ConvertBlending(Service::Nvnflinger::LayerBlending blending) { + switch (blending) { + case Service::Nvnflinger::LayerBlending::None: + default: + return Tegra::BlendMode::Opaque; + case Service::Nvnflinger::LayerBlending::Premultiplied: + return Tegra::BlendMode::Premultiplied; + case Service::Nvnflinger::LayerBlending::Coverage: + return Tegra::BlendMode::Coverage; + } +} + +} // namespace + nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} nvdisp_disp0::~nvdisp_disp0() = default; @@ -56,6 +72,7 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers .pixel_format = layer.format, .transform_flags = layer.transform, .crop_rect = layer.crop_rect, + .blending = ConvertBlending(layer.blending), }); for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 250d01de3..0265d55f2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -92,11 +92,11 @@ NvResult nvhost_ctrl::IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_a bool must_unmark_fail = !is_allocation; const u32 event_id = params.value.raw; - SCOPE_EXIT({ + SCOPE_EXIT { if (must_unmark_fail) { events[event_id].fails = 0; } - }); + }; const u32 fence_id = static_cast<u32>(params.fence.id); diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index cb256e5b4..03eb507b9 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -42,7 +42,7 @@ void EventInterface::FreeEvent(Kernel::KEvent* event) { module.service_context.CloseEvent(event); } -void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { +void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); auto module = std::make_shared<Module>(system); const auto NvdrvInterfaceFactoryForApplication = [&, module] { @@ -62,7 +62,6 @@ void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { server_manager->RegisterNamedService("nvdrv:s", NvdrvInterfaceFactoryForSysmodules); server_manager->RegisterNamedService("nvdrv:t", NvdrvInterfaceFactoryForTesting); server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system)); - nvnflinger.SetNVDrvInstance(module); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index c594f0e5e..b76f81e59 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -10,13 +10,11 @@ #include <span> #include <string> #include <unordered_map> -#include <vector> #include "common/common_types.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvnflinger/ui/fence.h" #include "core/hle/service/service.h" namespace Core { @@ -27,10 +25,6 @@ namespace Kernel { class KEvent; } -namespace Service::Nvnflinger { -class Nvnflinger; -} - namespace Service::Nvidia { namespace NvCore { @@ -99,7 +93,6 @@ public: private: friend class EventInterface; - friend class Service::Nvnflinger::Nvnflinger; /// Manages syncpoints on the host NvCore::Container container; @@ -118,6 +111,6 @@ private: std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders; }; -void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system); +void LoopProcess(Core::System& system); } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index ffe72f281..258970fd5 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -154,10 +154,10 @@ void NVDRV::Close(HLERequestContext& ctx) { void NVDRV::Initialize(HLERequestContext& ctx) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; - SCOPE_EXIT({ + SCOPE_EXIT { rb.Push(ResultSuccess); rb.PushEnum(NvResult::Success); - }); + }; if (is_initialized) { // No need to initialize again @@ -263,8 +263,10 @@ NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* } NVDRV::~NVDRV() { - auto& container = nvdrv->GetContainer(); - container.CloseSession(session_id); + if (is_initialized) { + auto& container = nvdrv->GetContainer(); + container.CloseSession(session_id); + } } } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index f2195ae1e..c72f92597 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -16,6 +16,10 @@ public: explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name); ~NVDRV() override; + std::shared_ptr<Module> GetModule() const { + return nvdrv; + } + private: void Open(HLERequestContext& ctx); void Ioctl1(HLERequestContext& ctx); diff --git a/src/core/hle/service/nvnflinger/binder.h b/src/core/hle/service/nvnflinger/binder.h index aef1477e3..124accb94 100644 --- a/src/core/hle/service/nvnflinger/binder.h +++ b/src/core/hle/service/nvnflinger/binder.h @@ -6,6 +6,8 @@ #pragma once +#include <span> + #include "common/common_types.h" namespace Kernel { @@ -18,28 +20,12 @@ class HLERequestContext; namespace Service::android { -enum class TransactionId { - RequestBuffer = 1, - SetBufferCount = 2, - DequeueBuffer = 3, - DetachBuffer = 4, - DetachNextBuffer = 5, - AttachBuffer = 6, - QueueBuffer = 7, - CancelBuffer = 8, - Query = 9, - Connect = 10, - Disconnect = 11, - AllocateBuffers = 13, - SetPreallocatedBuffer = 14, - GetBufferHistory = 17, -}; - class IBinder { public: virtual ~IBinder() = default; - virtual void Transact(HLERequestContext& ctx, android::TransactionId code, u32 flags) = 0; - virtual Kernel::KReadableEvent& GetNativeHandle() = 0; + virtual void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply, + u32 flags) = 0; + virtual Kernel::KReadableEvent* GetNativeHandle(u32 type_id) = 0; }; } // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp index cf151ea3a..123507123 100644 --- a/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp @@ -12,7 +12,7 @@ namespace Service::android { -BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_) +BufferItemConsumer::BufferItemConsumer(std::shared_ptr<BufferQueueConsumer> consumer_) : ConsumerBase{std::move(consumer_)} {} Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, diff --git a/src/core/hle/service/nvnflinger/buffer_item_consumer.h b/src/core/hle/service/nvnflinger/buffer_item_consumer.h index e0c6b3604..9f95c9280 100644 --- a/src/core/hle/service/nvnflinger/buffer_item_consumer.h +++ b/src/core/hle/service/nvnflinger/buffer_item_consumer.h @@ -19,7 +19,7 @@ class BufferItem; class BufferItemConsumer final : public ConsumerBase { public: - explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer); + explicit BufferItemConsumer(std::shared_ptr<BufferQueueConsumer> consumer); Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, bool wait_for_fence = true); Status ReleaseBuffer(const BufferItem& item, const Fence& release_fence); diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp index bbe8e06d4..3bc23aa97 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp @@ -4,12 +4,13 @@ // Parts of this implementation were based on: // https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp +#include "common/assert.h" #include "common/logging/log.h" #include "core/hle/service/nvnflinger/buffer_item.h" #include "core/hle/service/nvnflinger/buffer_queue_consumer.h" #include "core/hle/service/nvnflinger/buffer_queue_core.h" +#include "core/hle/service/nvnflinger/parcel.h" #include "core/hle/service/nvnflinger/producer_listener.h" -#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" namespace Service::android { @@ -254,4 +255,77 @@ Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { return Status::NoError; } +void BufferQueueConsumer::Transact(u32 code, std::span<const u8> parcel_data, + std::span<u8> parcel_reply, u32 flags) { + // Values used by BnGraphicBufferConsumer onTransact + enum class TransactionId { + AcquireBuffer = 1, + DetachBuffer = 2, + AttachBuffer = 3, + ReleaseBuffer = 4, + ConsumerConnect = 5, + ConsumerDisconnect = 6, + GetReleasedBuffers = 7, + SetDefaultBufferSize = 8, + SetDefaultMaxBufferCount = 9, + DisableAsyncBuffer = 10, + SetMaxAcquiredBufferCount = 11, + SetConsumerName = 12, + SetDefaultBufferFormat = 13, + SetConsumerUsageBits = 14, + SetTransformHint = 15, + GetSidebandStream = 16, + Unknown18 = 18, + Unknown20 = 20, + }; + + Status status{Status::NoError}; + InputParcel parcel_in{parcel_data}; + OutputParcel parcel_out{}; + + switch (static_cast<TransactionId>(code)) { + case TransactionId::AcquireBuffer: { + BufferItem item; + const s64 present_when = parcel_in.Read<s64>(); + + status = AcquireBuffer(&item, std::chrono::nanoseconds{present_when}); + + // TODO: can't write this directly, needs a flattener for the sp<GraphicBuffer> + // parcel_out.WriteFlattened(item); + UNREACHABLE(); + } + case TransactionId::ReleaseBuffer: { + const s32 slot = parcel_in.Read<s32>(); + const u64 frame_number = parcel_in.Read<u64>(); + const auto release_fence = parcel_in.ReadFlattened<Fence>(); + + status = ReleaseBuffer(slot, frame_number, release_fence); + + break; + } + case TransactionId::GetReleasedBuffers: { + u64 slot_mask = 0; + + status = GetReleasedBuffers(&slot_mask); + + parcel_out.Write(slot_mask); + break; + } + default: + ASSERT_MSG(false, "called, code={} flags={}", code, flags); + break; + } + + parcel_out.Write(status); + + const auto serialized = parcel_out.Serialize(); + std::memcpy(parcel_reply.data(), serialized.data(), + std::min(parcel_reply.size(), serialized.size())); +} + +Kernel::KReadableEvent* BufferQueueConsumer::GetNativeHandle(u32 type_id) { + ASSERT_MSG(false, "called, type_id={}", type_id); + return nullptr; +} + } // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h index 0a61e8dbd..a9226f1c3 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h @@ -10,6 +10,7 @@ #include <memory> #include "common/common_types.h" +#include "core/hle/service/nvnflinger/binder.h" #include "core/hle/service/nvnflinger/buffer_queue_defs.h" #include "core/hle/service/nvnflinger/status.h" @@ -19,10 +20,10 @@ class BufferItem; class BufferQueueCore; class IConsumerListener; -class BufferQueueConsumer final { +class BufferQueueConsumer final : public IBinder { public: explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_); - ~BufferQueueConsumer(); + ~BufferQueueConsumer() override; Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); @@ -30,6 +31,11 @@ public: Status Disconnect(); Status GetReleasedBuffers(u64* out_slot_mask); + void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply, + u32 flags) override; + + Kernel::KReadableEvent* GetNativeHandle(u32 type_id) override; + private: std::shared_ptr<BufferQueueCore> core; BufferQueueDefs::SlotsType& slots; diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index 5d8762d25..9e5091eeb 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -6,12 +6,9 @@ #include "common/assert.h" #include "common/logging/log.h" -#include "common/settings.h" -#include "core/core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/service/hle_ipc.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nvnflinger/buffer_queue_core.h" #include "core/hle/service/nvnflinger/buffer_queue_producer.h" @@ -19,7 +16,6 @@ #include "core/hle/service/nvnflinger/parcel.h" #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" #include "core/hle/service/nvnflinger/window.h" -#include "core/hle/service/vi/vi.h" namespace Service::android { @@ -807,12 +803,31 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, return Status::NoError; } -void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u32 flags) { +void BufferQueueProducer::Transact(u32 code, std::span<const u8> parcel_data, + std::span<u8> parcel_reply, u32 flags) { + // Values used by BnGraphicBufferProducer onTransact + enum class TransactionId { + RequestBuffer = 1, + SetBufferCount = 2, + DequeueBuffer = 3, + DetachBuffer = 4, + DetachNextBuffer = 5, + AttachBuffer = 6, + QueueBuffer = 7, + CancelBuffer = 8, + Query = 9, + Connect = 10, + Disconnect = 11, + AllocateBuffers = 13, + SetPreallocatedBuffer = 14, + GetBufferHistory = 17, + }; + Status status{Status::NoError}; - InputParcel parcel_in{ctx.ReadBuffer()}; + InputParcel parcel_in{parcel_data}; OutputParcel parcel_out{}; - switch (code) { + switch (static_cast<TransactionId>(code)) { case TransactionId::Connect: { const auto enable_listener = parcel_in.Read<bool>(); const auto api = parcel_in.Read<NativeWindowApi>(); @@ -917,11 +932,13 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u parcel_out.Write(status); - ctx.WriteBuffer(parcel_out.Serialize()); + const auto serialized = parcel_out.Serialize(); + std::memcpy(parcel_reply.data(), serialized.data(), + std::min(parcel_reply.size(), serialized.size())); } -Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() { - return buffer_wait_event->GetReadableEvent(); +Kernel::KReadableEvent* BufferQueueProducer::GetNativeHandle(u32 type_id) { + return &buffer_wait_event->GetReadableEvent(); } } // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h index 64c17d56c..048523514 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h @@ -45,11 +45,12 @@ public: explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, std::shared_ptr<BufferQueueCore> buffer_queue_core_, Service::Nvidia::NvCore::NvMap& nvmap_); - ~BufferQueueProducer(); + ~BufferQueueProducer() override; - void Transact(HLERequestContext& ctx, android::TransactionId code, u32 flags) override; + void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply, + u32 flags) override; - Kernel::KReadableEvent& GetNativeHandle() override; + Kernel::KReadableEvent* GetNativeHandle(u32 type_id) override; public: Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf); diff --git a/src/core/hle/service/nvnflinger/consumer_base.cpp b/src/core/hle/service/nvnflinger/consumer_base.cpp index 1059e72bf..e360ebfd8 100644 --- a/src/core/hle/service/nvnflinger/consumer_base.cpp +++ b/src/core/hle/service/nvnflinger/consumer_base.cpp @@ -14,7 +14,7 @@ namespace Service::android { -ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_) +ConsumerBase::ConsumerBase(std::shared_ptr<BufferQueueConsumer> consumer_) : consumer{std::move(consumer_)} {} ConsumerBase::~ConsumerBase() { diff --git a/src/core/hle/service/nvnflinger/consumer_base.h b/src/core/hle/service/nvnflinger/consumer_base.h index ea3e9e97a..b29c16f86 100644 --- a/src/core/hle/service/nvnflinger/consumer_base.h +++ b/src/core/hle/service/nvnflinger/consumer_base.h @@ -27,7 +27,7 @@ public: void Abandon(); protected: - explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); + explicit ConsumerBase(std::shared_ptr<BufferQueueConsumer> consumer_); ~ConsumerBase() override; void OnFrameAvailable(const BufferItem& item) override; @@ -54,7 +54,7 @@ protected: bool is_abandoned{}; - std::unique_ptr<BufferQueueConsumer> consumer; + std::shared_ptr<BufferQueueConsumer> consumer; mutable std::mutex mutex; }; diff --git a/src/core/hle/service/nvnflinger/display.h b/src/core/hle/service/nvnflinger/display.h new file mode 100644 index 000000000..40aa59787 --- /dev/null +++ b/src/core/hle/service/nvnflinger/display.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/nvnflinger/buffer_item_consumer.h" +#include "core/hle/service/nvnflinger/hwc_layer.h" + +namespace Service::Nvnflinger { + +struct Layer { + explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_, + s32 consumer_id_) + : buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_), + blending(LayerBlending::None), visible(true) {} + ~Layer() { + buffer_item_consumer->Abandon(); + } + + std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer; + s32 consumer_id; + LayerBlending blending; + bool visible; +}; + +struct LayerStack { + std::vector<std::shared_ptr<Layer>> layers; + + std::shared_ptr<Layer> FindLayer(s32 consumer_id) { + for (auto& layer : layers) { + if (layer->consumer_id == consumer_id) { + return layer; + } + } + + return nullptr; + } + + bool HasLayers() { + return !layers.empty(); + } +}; + +struct Display { + explicit Display(u64 id_) { + id = id_; + } + + u64 id; + LayerStack stack; +}; + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp deleted file mode 100644 index e71652cdf..000000000 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp +++ /dev/null @@ -1,345 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <random> - -#include "core/core.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_system_resource.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" -#include "core/hle/service/nvdrv/nvdrv.h" -#include "core/hle/service/nvnflinger/buffer_queue_producer.h" -#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" -#include "core/hle/service/nvnflinger/pixel_format.h" -#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" -#include "core/hle/service/vi/layer/vi_layer.h" -#include "core/hle/service/vi/vi_results.h" - -namespace Service::Nvnflinger { - -namespace { - -Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address, - std::unique_ptr<Kernel::KPageGroup>* out_page_group, - Core::System& system, u32 size) { - using Core::Memory::YUZU_PAGESIZE; - - // Allocate memory for the system shared buffer. - // FIXME: Because the gmmu can only point to cpu addresses, we need - // to map this in the application space to allow it to be used. - // FIXME: Add proper smmu emulation. - // FIXME: This memory belongs to vi's .data section. - auto& kernel = system.Kernel(); - auto* process = system.ApplicationProcess(); - auto& page_table = process->GetPageTable(); - - // Hold a temporary page group reference while we try to map it. - auto pg = std::make_unique<Kernel::KPageGroup>( - kernel, std::addressof(kernel.GetSystemSystemResource().GetBlockInfoManager())); - - // Allocate memory from secure pool. - R_TRY(kernel.MemoryManager().AllocateAndOpen( - pg.get(), size / YUZU_PAGESIZE, - Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure, - Kernel::KMemoryManager::Direction::FromBack))); - - // Get bounds of where mapping is possible. - const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart()); - const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE; - const auto state = Kernel::KMemoryState::IoMemory; - const auto perm = Kernel::KMemoryPermission::UserReadWrite; - std::mt19937_64 rng{process->GetRandomEntropy(0)}; - - // Retry up to 64 times to map into alias code range. - Result res = ResultSuccess; - int i; - for (i = 0; i < 64; i++) { - *out_map_address = alias_code_begin + ((rng() % alias_code_size) * YUZU_PAGESIZE); - res = page_table.MapPageGroup(*out_map_address, *pg, state, perm); - if (R_SUCCEEDED(res)) { - break; - } - } - - // Return failure, if necessary - R_UNLESS(i < 64, res); - - // Return the mapped page group. - *out_page_group = std::move(pg); - - // We succeeded. - R_SUCCEED(); -} - -Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) { - // Create a handle. - Nvidia::Devices::nvmap::IocCreateParams create_params{ - .size = size, - .handle = 0, - }; - R_UNLESS(nvmap.IocCreate(create_params) == Nvidia::NvResult::Success, - VI::ResultOperationFailed); - - // Assign the output handle. - *out_nv_map_handle = create_params.handle; - - // We succeeded. - R_SUCCEED(); -} - -Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Nvidia::DeviceFD nvmap_fd) { - // Free the handle. - Nvidia::Devices::nvmap::IocFreeParams free_params{ - .handle = handle, - }; - R_UNLESS(nvmap.IocFree(free_params, nvmap_fd) == Nvidia::NvResult::Success, - VI::ResultOperationFailed); - - // We succeeded. - R_SUCCEED(); -} - -Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer, - u32 size, Nvidia::DeviceFD nvmap_fd) { - // Assign the allocated memory to the handle. - Nvidia::Devices::nvmap::IocAllocParams alloc_params{ - .handle = handle, - .heap_mask = 0, - .flags = {}, - .align = 0, - .kind = 0, - .address = GetInteger(buffer), - }; - R_UNLESS(nvmap.IocAlloc(alloc_params, nvmap_fd) == Nvidia::NvResult::Success, - VI::ResultOperationFailed); - - // We succeeded. - R_SUCCEED(); -} - -Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd, - Common::ProcessAddress buffer, u32 size) { - // Get the nvmap device. - auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd); - ASSERT(nvmap != nullptr); - - // Create a handle. - R_TRY(CreateNvMapHandle(out_handle, *nvmap, size)); - - // Ensure we maintain a clean state on failure. - ON_RESULT_FAILURE { - R_ASSERT(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd)); - }; - - // Assign the allocated memory to the handle. - R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd)); -} - -constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; -constexpr u32 SharedBufferBlockLinearBpp = 4; - -constexpr u32 SharedBufferBlockLinearWidth = 1280; -constexpr u32 SharedBufferBlockLinearHeight = 768; -constexpr u32 SharedBufferBlockLinearStride = - SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp; -constexpr u32 SharedBufferNumSlots = 7; - -constexpr u32 SharedBufferWidth = 1280; -constexpr u32 SharedBufferHeight = 720; -constexpr u32 SharedBufferAsync = false; - -constexpr u32 SharedBufferSlotSize = - SharedBufferBlockLinearWidth * SharedBufferBlockLinearHeight * SharedBufferBlockLinearBpp; -constexpr u32 SharedBufferSize = SharedBufferSlotSize * SharedBufferNumSlots; - -constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] { - SharedMemoryPoolLayout layout{}; - layout.num_slots = SharedBufferNumSlots; - - for (u32 i = 0; i < SharedBufferNumSlots; i++) { - layout.slots[i].buffer_offset = i * SharedBufferSlotSize; - layout.slots[i].size = SharedBufferSlotSize; - layout.slots[i].width = SharedBufferWidth; - layout.slots[i].height = SharedBufferHeight; - } - - return layout; -}(); - -void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { - auto buffer = std::make_shared<android::NvGraphicBuffer>(); - buffer->width = SharedBufferWidth; - buffer->height = SharedBufferHeight; - buffer->stride = SharedBufferBlockLinearStride; - buffer->format = SharedBufferBlockLinearFormat; - buffer->external_format = SharedBufferBlockLinearFormat; - buffer->buffer_id = handle; - buffer->offset = slot * SharedBufferSlotSize; - ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError); -} - -} // namespace - -FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& flinger, - std::shared_ptr<Nvidia::Module> nvdrv) - : m_system(system), m_flinger(flinger), m_nvdrv(std::move(nvdrv)) {} - -FbShareBufferManager::~FbShareBufferManager() = default; - -Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) { - std::scoped_lock lk{m_guard}; - - // Ensure we have not already created a buffer. - R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed); - - // Allocate memory and space for the shared buffer. - Common::ProcessAddress map_address; - R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address), - std::addressof(m_buffer_page_group), m_system, - SharedBufferSize)); - - auto& container = m_nvdrv->GetContainer(); - m_session_id = container.OpenSession(m_system.ApplicationProcess()); - m_nvmap_fd = m_nvdrv->Open("/dev/nvmap", m_session_id); - - // Create an nvmap handle for the buffer and assign the memory to it. - R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, m_nvmap_fd, - map_address, SharedBufferSize)); - - // Record the display id. - m_display_id = display_id; - - // Create and open a layer for the display. - m_layer_id = m_flinger.CreateLayer(m_display_id).value(); - m_flinger.OpenLayer(m_layer_id); - - // Set up the buffer. - m_buffer_id = m_next_buffer_id++; - - // Get the layer. - VI::Layer* layer = m_flinger.FindLayer(m_display_id, m_layer_id); - ASSERT(layer != nullptr); - - // Get the producer and set preallocated buffers. - auto& producer = layer->GetBufferQueue(); - MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle); - MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle); - - // Assign outputs. - *out_buffer_id = m_buffer_id; - *out_layer_id = m_layer_id; - - // We succeeded. - R_SUCCEED(); -} - -Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, - s32* out_nvmap_handle, - SharedMemoryPoolLayout* out_pool_layout, - u64 buffer_id, - u64 applet_resource_user_id) { - std::scoped_lock lk{m_guard}; - - R_UNLESS(m_buffer_id > 0, VI::ResultNotFound); - R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound); - - *out_pool_layout = SharedBufferPoolLayout; - *out_buffer_size = SharedBufferSize; - *out_nvmap_handle = m_buffer_nvmap_handle; - - R_SUCCEED(); -} - -Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) { - // Ensure the layer id is valid. - R_UNLESS(m_layer_id > 0 && layer_id == m_layer_id, VI::ResultNotFound); - - // Get the layer. - VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id); - R_UNLESS(layer != nullptr, VI::ResultNotFound); - - // We succeeded. - *out_layer = layer; - R_SUCCEED(); -} - -Result FbShareBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence, - std::array<s32, 4>& out_slot_indexes, - s64* out_target_slot, u64 layer_id) { - std::scoped_lock lk{m_guard}; - - // Get the layer. - VI::Layer* layer; - R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id)); - - // Get the producer. - auto& producer = layer->GetBufferQueue(); - - // Get the next buffer from the producer. - s32 slot; - R_UNLESS(producer.DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0, - SharedBufferWidth, SharedBufferHeight, - SharedBufferBlockLinearFormat, 0) == android::Status::NoError, - VI::ResultOperationFailed); - - // Assign remaining outputs. - *out_target_slot = slot; - out_slot_indexes = {0, 1, -1, -1}; - - // We succeeded. - R_SUCCEED(); -} - -Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence, - Common::Rectangle<s32> crop_region, - u32 transform, s32 swap_interval, - u64 layer_id, s64 slot) { - std::scoped_lock lk{m_guard}; - - // Get the layer. - VI::Layer* layer; - R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id)); - - // Get the producer. - auto& producer = layer->GetBufferQueue(); - - // Request to queue the buffer. - std::shared_ptr<android::GraphicBuffer> buffer; - R_UNLESS(producer.RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) == - android::Status::NoError, - VI::ResultOperationFailed); - - // Queue the buffer to the producer. - android::QueueBufferInput input{}; - android::QueueBufferOutput output{}; - input.crop = crop_region; - input.fence = fence; - input.transform = static_cast<android::NativeWindowTransform>(transform); - input.swap_interval = swap_interval; - R_UNLESS(producer.QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) == - android::Status::NoError, - VI::ResultOperationFailed); - - // We succeeded. - R_SUCCEED(); -} - -Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, - u64 layer_id) { - std::scoped_lock lk{m_guard}; - - // Get the layer. - VI::Layer* layer; - R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id)); - - // Get the producer. - auto& producer = layer->GetBufferQueue(); - - // Set the event. - *out_event = std::addressof(producer.GetNativeHandle()); - - // We succeeded. - R_SUCCEED(); -} - -} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h deleted file mode 100644 index 033bf4bbe..000000000 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/math_util.h" -#include "core/hle/service/nvdrv/core/container.h" -#include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvnflinger/nvnflinger.h" -#include "core/hle/service/nvnflinger/ui/fence.h" - -namespace Kernel { -class KPageGroup; -} - -namespace Service::Nvnflinger { - -struct SharedMemorySlot { - u64 buffer_offset; - u64 size; - s32 width; - s32 height; -}; -static_assert(sizeof(SharedMemorySlot) == 0x18, "SharedMemorySlot has wrong size"); - -struct SharedMemoryPoolLayout { - s32 num_slots; - std::array<SharedMemorySlot, 0x10> slots; -}; -static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); - -class FbShareBufferManager final { -public: - explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger, - std::shared_ptr<Nvidia::Module> nvdrv); - ~FbShareBufferManager(); - - Result Initialize(u64* out_buffer_id, u64* out_layer_handle, u64 display_id); - Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle, - SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id, - u64 applet_resource_user_id); - Result AcquireSharedFrameBuffer(android::Fence* out_fence, std::array<s32, 4>& out_slots, - s64* out_target_slot, u64 layer_id); - Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region, - u32 transform, s32 swap_interval, u64 layer_id, s64 slot); - Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id); - -private: - Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id); - -private: - u64 m_next_buffer_id = 1; - u64 m_display_id = 0; - u64 m_buffer_id = 0; - u64 m_layer_id = 0; - u32 m_buffer_nvmap_handle = 0; - SharedMemoryPoolLayout m_pool_layout = {}; - Nvidia::DeviceFD m_nvmap_fd = {}; - Nvidia::NvCore::SessionId m_session_id = {}; - std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; - - std::mutex m_guard; - Core::System& m_system; - Nvnflinger& m_flinger; - std::shared_ptr<Nvidia::Module> m_nvdrv; -}; - -} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp index ba2b5c28c..f2dfe85a9 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.cpp +++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp @@ -10,8 +10,6 @@ #include "core/hle/service/nvnflinger/hardware_composer.h" #include "core/hle/service/nvnflinger/hwc_layer.h" #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" -#include "core/hle/service/vi/display/vi_display.h" -#include "core/hle/service/vi/layer/vi_layer.h" namespace Service::Nvnflinger { @@ -44,7 +42,7 @@ s32 NormalizeSwapInterval(f32* out_speed_scale, s32 swap_interval) { HardwareComposer::HardwareComposer() = default; HardwareComposer::~HardwareComposer() = default; -u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, +u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display, Nvidia::Devices::nvdisp_disp0& nvdisp) { boost::container::small_vector<HwcLayer, 2> composition_stack; @@ -56,12 +54,11 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, bool has_acquired_buffer{}; // Acquire all necessary framebuffers. - for (size_t i = 0; i < display.GetNumLayers(); i++) { - auto& layer = display.GetLayer(i); - auto layer_id = layer.GetLayerId(); + for (auto& layer : display.stack.layers) { + auto consumer_id = layer->consumer_id; // Try to fetch the framebuffer (either new or stale). - const auto result = this->CacheFramebufferLocked(layer, layer_id); + const auto result = this->CacheFramebufferLocked(*layer, consumer_id); // If we failed, skip this layer. if (result == CacheStatus::NoBufferAvailable) { @@ -73,23 +70,26 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, has_acquired_buffer = true; } - const auto& buffer = m_framebuffers[layer_id]; + const auto& buffer = m_framebuffers[consumer_id]; const auto& item = buffer.item; const auto& igbp_buffer = *item.graphic_buffer; // TODO: get proper Z-index from layer - composition_stack.emplace_back(HwcLayer{ - .buffer_handle = igbp_buffer.BufferId(), - .offset = igbp_buffer.Offset(), - .format = igbp_buffer.ExternalFormat(), - .width = igbp_buffer.Width(), - .height = igbp_buffer.Height(), - .stride = igbp_buffer.Stride(), - .z_index = 0, - .transform = static_cast<android::BufferTransformFlags>(item.transform), - .crop_rect = item.crop, - .acquire_fence = item.fence, - }); + if (layer->visible) { + composition_stack.emplace_back(HwcLayer{ + .buffer_handle = igbp_buffer.BufferId(), + .offset = igbp_buffer.Offset(), + .format = igbp_buffer.ExternalFormat(), + .width = igbp_buffer.Width(), + .height = igbp_buffer.Height(), + .stride = igbp_buffer.Stride(), + .z_index = 0, + .blending = layer->blending, + .transform = static_cast<android::BufferTransformFlags>(item.transform), + .crop_rect = item.crop, + .acquire_fence = item.fence, + }); + } // We need to compose again either before this frame is supposed to // be released, or exactly on the vsync period it should be released. @@ -134,10 +134,10 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, continue; } - if (auto* layer = display.FindLayer(layer_id); layer != nullptr) { + if (const auto layer = display.stack.FindLayer(layer_id); layer != nullptr) { // TODO: support release fence // This is needed to prevent screen tearing - layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence()); + layer->buffer_item_consumer->ReleaseBuffer(framebuffer.item, android::Fence::NoFence()); framebuffer.is_acquired = false; } } @@ -145,26 +145,26 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, return frame_advance; } -void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) { - // Check if we are tracking a slot with this layer_id. - const auto it = m_framebuffers.find(layer_id); +void HardwareComposer::RemoveLayerLocked(Display& display, ConsumerId consumer_id) { + // Check if we are tracking a slot with this consumer_id. + const auto it = m_framebuffers.find(consumer_id); if (it == m_framebuffers.end()) { return; } // Try to release the buffer item. - auto* const layer = display.FindLayer(layer_id); + const auto layer = display.stack.FindLayer(consumer_id); if (layer && it->second.is_acquired) { - layer->GetConsumer().ReleaseBuffer(it->second.item, android::Fence::NoFence()); + layer->buffer_item_consumer->ReleaseBuffer(it->second.item, android::Fence::NoFence()); } // Erase the slot. m_framebuffers.erase(it); } -bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer) { +bool HardwareComposer::TryAcquireFramebufferLocked(Layer& layer, Framebuffer& framebuffer) { // Attempt the update. - const auto status = layer.GetConsumer().AcquireBuffer(&framebuffer.item, {}, false); + const auto status = layer.buffer_item_consumer->AcquireBuffer(&framebuffer.item, {}, false); if (status != android::Status::NoError) { return false; } @@ -177,10 +177,10 @@ bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer return true; } -HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer& layer, - LayerId layer_id) { +HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(Layer& layer, + ConsumerId consumer_id) { // Check if this framebuffer is already present. - const auto it = m_framebuffers.find(layer_id); + const auto it = m_framebuffers.find(consumer_id); if (it != m_framebuffers.end()) { // If it's currently still acquired, we are done. if (it->second.is_acquired) { @@ -202,7 +202,7 @@ HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer if (this->TryAcquireFramebufferLocked(layer, framebuffer)) { // Move the buffer item into a new slot. - m_framebuffers.emplace(layer_id, std::move(framebuffer)); + m_framebuffers.emplace(consumer_id, std::move(framebuffer)); // We succeeded. return CacheStatus::BufferAcquired; diff --git a/src/core/hle/service/nvnflinger/hardware_composer.h b/src/core/hle/service/nvnflinger/hardware_composer.h index 28392c512..c5b830468 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.h +++ b/src/core/hle/service/nvnflinger/hardware_composer.h @@ -3,35 +3,29 @@ #pragma once -#include <memory> #include <boost/container/flat_map.hpp> #include "core/hle/service/nvnflinger/buffer_item.h" +#include "core/hle/service/nvnflinger/display.h" namespace Service::Nvidia::Devices { class nvdisp_disp0; } -namespace Service::VI { -class Display; -class Layer; -} // namespace Service::VI - namespace Service::Nvnflinger { -using LayerId = u64; +using ConsumerId = s32; class HardwareComposer { public: explicit HardwareComposer(); ~HardwareComposer(); - u32 ComposeLocked(f32* out_speed_scale, VI::Display& display, + u32 ComposeLocked(f32* out_speed_scale, Display& display, Nvidia::Devices::nvdisp_disp0& nvdisp); - void RemoveLayerLocked(VI::Display& display, LayerId layer_id); + void RemoveLayerLocked(Display& display, ConsumerId consumer_id); private: - // TODO: do we want to track frame number in vi instead? u64 m_frame_number{0}; private: @@ -49,11 +43,11 @@ private: CachedBufferReused, }; - boost::container::flat_map<LayerId, Framebuffer> m_framebuffers{}; + boost::container::flat_map<ConsumerId, Framebuffer> m_framebuffers{}; private: - bool TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer); - CacheStatus CacheFramebufferLocked(VI::Layer& layer, LayerId layer_id); + bool TryAcquireFramebufferLocked(Layer& layer, Framebuffer& framebuffer); + CacheStatus CacheFramebufferLocked(Layer& layer, ConsumerId consumer_id); }; } // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver.cpp b/src/core/hle/service/nvnflinger/hos_binder_driver.cpp new file mode 100644 index 000000000..8629a2e89 --- /dev/null +++ b/src/core/hle/service/nvnflinger/hos_binder_driver.cpp @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/nvnflinger/binder.h" +#include "core/hle/service/nvnflinger/hos_binder_driver.h" +#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" + +namespace Service::Nvnflinger { + +IHOSBinderDriver::IHOSBinderDriver(Core::System& system_, + std::shared_ptr<HosBinderDriverServer> server, + std::shared_ptr<SurfaceFlinger> surface_flinger) + : ServiceFramework{system_, "IHOSBinderDriver"}, m_server(server), + m_surface_flinger(surface_flinger) { + static const FunctionInfo functions[] = { + {0, C<&IHOSBinderDriver::TransactParcel>, "TransactParcel"}, + {1, C<&IHOSBinderDriver::AdjustRefcount>, "AdjustRefcount"}, + {2, C<&IHOSBinderDriver::GetNativeHandle>, "GetNativeHandle"}, + {3, C<&IHOSBinderDriver::TransactParcelAuto>, "TransactParcelAuto"}, + }; + RegisterHandlers(functions); +} + +IHOSBinderDriver::~IHOSBinderDriver() = default; + +Result IHOSBinderDriver::TransactParcel(s32 binder_id, u32 transaction_id, + InBuffer<BufferAttr_HipcMapAlias> parcel_data, + OutBuffer<BufferAttr_HipcMapAlias> parcel_reply, + u32 flags) { + LOG_DEBUG(Service_VI, "called. id={} transaction={}, flags={}", binder_id, transaction_id, + flags); + + const auto binder = m_server->TryGetBinder(binder_id); + R_SUCCEED_IF(binder == nullptr); + + binder->Transact(transaction_id, parcel_data, parcel_reply, flags); + + R_SUCCEED(); +} + +Result IHOSBinderDriver::AdjustRefcount(s32 binder_id, s32 addval, s32 type) { + LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={}, type={}", binder_id, addval, type); + R_SUCCEED(); +} + +Result IHOSBinderDriver::GetNativeHandle(s32 binder_id, u32 type_id, + OutCopyHandle<Kernel::KReadableEvent> out_handle) { + LOG_WARNING(Service_VI, "(STUBBED) called id={}, type_id={}", binder_id, type_id); + + const auto binder = m_server->TryGetBinder(binder_id); + R_UNLESS(binder != nullptr, ResultUnknown); + + *out_handle = binder->GetNativeHandle(type_id); + + R_SUCCEED(); +} + +Result IHOSBinderDriver::TransactParcelAuto(s32 binder_id, u32 transaction_id, + InBuffer<BufferAttr_HipcAutoSelect> parcel_data, + OutBuffer<BufferAttr_HipcAutoSelect> parcel_reply, + u32 flags) { + R_RETURN(this->TransactParcel(binder_id, transaction_id, parcel_data, parcel_reply, flags)); +} + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver.h b/src/core/hle/service/nvnflinger/hos_binder_driver.h new file mode 100644 index 000000000..b7fb07bd2 --- /dev/null +++ b/src/core/hle/service/nvnflinger/hos_binder_driver.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KReadableEvent; +} + +namespace Service::Nvnflinger { + +class HosBinderDriverServer; +class SurfaceFlinger; + +class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { +public: + explicit IHOSBinderDriver(Core::System& system_, std::shared_ptr<HosBinderDriverServer> server, + std::shared_ptr<SurfaceFlinger> surface_flinger); + ~IHOSBinderDriver() override; + + std::shared_ptr<SurfaceFlinger> GetSurfaceFlinger() { + return m_surface_flinger; + } + + std::shared_ptr<HosBinderDriverServer> GetServer() { + return m_server; + } + +private: + Result TransactParcel(s32 binder_id, u32 transaction_id, + InBuffer<BufferAttr_HipcMapAlias> parcel_data, + OutBuffer<BufferAttr_HipcMapAlias> parcel_reply, u32 flags); + Result AdjustRefcount(s32 binder_id, s32 addval, s32 type); + Result GetNativeHandle(s32 binder_id, u32 type_id, + OutCopyHandle<Kernel::KReadableEvent> out_handle); + Result TransactParcelAuto(s32 binder_id, u32 transaction_id, + InBuffer<BufferAttr_HipcAutoSelect> parcel_data, + OutBuffer<BufferAttr_HipcAutoSelect> parcel_reply, u32 flags); + +private: + const std::shared_ptr<HosBinderDriverServer> m_server; + const std::shared_ptr<SurfaceFlinger> m_surface_flinger; +}; + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp index b86a79ec9..29addda44 100644 --- a/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp +++ b/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp @@ -8,26 +8,30 @@ namespace Service::Nvnflinger { -HosBinderDriverServer::HosBinderDriverServer(Core::System& system_) - : service_context(system_, "HosBinderDriverServer") {} +HosBinderDriverServer::HosBinderDriverServer() = default; +HosBinderDriverServer::~HosBinderDriverServer() = default; -HosBinderDriverServer::~HosBinderDriverServer() {} - -u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) { +s32 HosBinderDriverServer::RegisterBinder(std::shared_ptr<android::IBinder>&& binder) { std::scoped_lock lk{lock}; last_id++; - producers[last_id] = std::move(binder); + binders[last_id] = std::move(binder); return last_id; } -android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) { +void HosBinderDriverServer::UnregisterBinder(s32 binder_id) { + std::scoped_lock lk{lock}; + + binders.erase(binder_id); +} + +std::shared_ptr<android::IBinder> HosBinderDriverServer::TryGetBinder(s32 id) const { std::scoped_lock lk{lock}; - if (auto search = producers.find(id); search != producers.end()) { - return search->second.get(); + if (auto search = binders.find(id); search != binders.end()) { + return search->second; } return {}; diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver_server.h b/src/core/hle/service/nvnflinger/hos_binder_driver_server.h index 58bb9469a..d72b50833 100644 --- a/src/core/hle/service/nvnflinger/hos_binder_driver_server.h +++ b/src/core/hle/service/nvnflinger/hos_binder_driver_server.h @@ -8,7 +8,6 @@ #include <unordered_map> #include "common/common_types.h" -#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nvnflinger/binder.h" namespace Core { @@ -19,19 +18,18 @@ namespace Service::Nvnflinger { class HosBinderDriverServer final { public: - explicit HosBinderDriverServer(Core::System& system_); + explicit HosBinderDriverServer(); ~HosBinderDriverServer(); - u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder); + s32 RegisterBinder(std::shared_ptr<android::IBinder>&& binder); + void UnregisterBinder(s32 binder_id); - android::IBinder* TryGetProducer(u64 id); + std::shared_ptr<android::IBinder> TryGetBinder(s32 id) const; private: - KernelHelpers::ServiceContext service_context; - - std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers; - std::mutex lock; - u64 last_id{}; + std::unordered_map<s32, std::shared_ptr<android::IBinder>> binders; + mutable std::mutex lock; + s32 last_id{}; }; } // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/hwc_layer.h b/src/core/hle/service/nvnflinger/hwc_layer.h index 3af668a25..f71a5d822 100644 --- a/src/core/hle/service/nvnflinger/hwc_layer.h +++ b/src/core/hle/service/nvnflinger/hwc_layer.h @@ -11,6 +11,18 @@ namespace Service::Nvnflinger { +// hwc_layer_t::blending values +enum class LayerBlending : u32 { + // No blending + None = 0x100, + + // ONE / ONE_MINUS_SRC_ALPHA + Premultiplied = 0x105, + + // SRC_ALPHA / ONE_MINUS_SRC_ALPHA + Coverage = 0x405, +}; + struct HwcLayer { u32 buffer_handle; u32 offset; @@ -19,6 +31,7 @@ struct HwcLayer { u32 height; u32 stride; s32 z_index; + LayerBlending blending; android::BufferTransformFlags transform; Common::Rectangle<int> crop_rect; android::Fence acquire_fence; diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index d8ba89d43..9e3b68b8a 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -1,334 +1,24 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#include <algorithm> -#include <optional> - -#include "common/assert.h" -#include "common/logging/log.h" -#include "common/microprofile.h" -#include "common/scope_exit.h" -#include "common/settings.h" -#include "common/thread.h" #include "core/core.h" -#include "core/core_timing.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" -#include "core/hle/service/nvdrv/nvdrv.h" -#include "core/hle/service/nvnflinger/buffer_item_consumer.h" -#include "core/hle/service/nvnflinger/buffer_queue_core.h" -#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" -#include "core/hle/service/nvnflinger/hardware_composer.h" +#include "core/hle/service/nvnflinger/hos_binder_driver.h" #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" #include "core/hle/service/nvnflinger/nvnflinger.h" -#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" -#include "core/hle/service/vi/display/vi_display.h" -#include "core/hle/service/vi/layer/vi_layer.h" -#include "core/hle/service/vi/vi_results.h" -#include "video_core/gpu.h" -#include "video_core/host1x/host1x.h" -#include "video_core/host1x/syncpoint_manager.h" +#include "core/hle/service/nvnflinger/surface_flinger.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/sm/sm.h" namespace Service::Nvnflinger { -constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60}; - -void Nvnflinger::SplitVSync(std::stop_token stop_token) { - system.RegisterHostThread(); - std::string name = "VSyncThread"; - MicroProfileOnThreadCreate(name.c_str()); - - // Cleanup - SCOPE_EXIT({ MicroProfileOnThreadExit(); }); - - Common::SetCurrentThreadName(name.c_str()); - Common::SetCurrentThreadPriority(Common::ThreadPriority::High); - - while (!stop_token.stop_requested()) { - vsync_signal.Wait(); - - const auto lock_guard = Lock(); - - if (!is_abandoned) { - Compose(); - } - } -} - -Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_) - : system(system_), service_context(system_, "nvnflinger"), - hos_binder_driver_server(hos_binder_driver_server_) { - displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system); - displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system); - displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system); - displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system); - displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system); - guard = std::make_shared<std::mutex>(); - - // Schedule the screen composition events - multi_composition_event = Core::Timing::CreateEvent( - "ScreenComposition", - [this](s64 time, - std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - vsync_signal.Set(); - return std::chrono::nanoseconds(GetNextTicks()); - }); - - single_composition_event = Core::Timing::CreateEvent( - "ScreenComposition", - [this](s64 time, - std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto lock_guard = Lock(); - Compose(); - - return std::chrono::nanoseconds(GetNextTicks()); - }); - - if (system.IsMulticore()) { - system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, multi_composition_event); - vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); }); - } else { - system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, single_composition_event); - } -} - -Nvnflinger::~Nvnflinger() { - if (system.IsMulticore()) { - system.CoreTiming().UnscheduleEvent(multi_composition_event); - vsync_thread.request_stop(); - vsync_signal.Set(); - } else { - system.CoreTiming().UnscheduleEvent(single_composition_event); - } - - ShutdownLayers(); - - if (nvdrv) { - nvdrv->Close(disp_fd); - } -} - -void Nvnflinger::ShutdownLayers() { - // Abandon consumers. - { - const auto lock_guard = Lock(); - for (auto& display : displays) { - display.Abandon(); - } - - is_abandoned = true; - } - - // Join the vsync thread, if it exists. - vsync_thread = {}; -} - -void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { - nvdrv = std::move(instance); - disp_fd = nvdrv->Open("/dev/nvdisp_disp0", {}); -} - -std::optional<u64> Nvnflinger::OpenDisplay(std::string_view name) { - const auto lock_guard = Lock(); - - LOG_DEBUG(Service_Nvnflinger, "Opening \"{}\" display", name); - - const auto itr = - std::find_if(displays.begin(), displays.end(), - [&](const VI::Display& display) { return display.GetName() == name; }); - - if (itr == displays.end()) { - return std::nullopt; - } - - return itr->GetID(); -} - -bool Nvnflinger::CloseDisplay(u64 display_id) { - const auto lock_guard = Lock(); - auto* const display = FindDisplay(display_id); - - if (display == nullptr) { - return false; - } - - display->Reset(); - - return true; -} - -std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) { - const auto lock_guard = Lock(); - auto* const display = FindDisplay(display_id); - - if (display == nullptr) { - return std::nullopt; - } - - const u64 layer_id = next_layer_id++; - CreateLayerAtId(*display, layer_id); - return layer_id; -} - -void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { - const auto buffer_id = next_buffer_queue_id++; - display.CreateLayer(layer_id, buffer_id, nvdrv->container); -} - -bool Nvnflinger::OpenLayer(u64 layer_id) { - const auto lock_guard = Lock(); - - for (auto& display : displays) { - if (auto* layer = display.FindLayer(layer_id); layer) { - return layer->Open(); - } - } - - return false; -} - -bool Nvnflinger::CloseLayer(u64 layer_id) { - const auto lock_guard = Lock(); - - for (auto& display : displays) { - if (auto* layer = display.FindLayer(layer_id); layer) { - return layer->Close(); - } - } - - return false; -} - -void Nvnflinger::SetLayerVisibility(u64 layer_id, bool visible) { - const auto lock_guard = Lock(); - - for (auto& display : displays) { - if (auto* layer = display.FindLayer(layer_id); layer) { - layer->SetVisibility(visible); - } - } -} - -void Nvnflinger::DestroyLayer(u64 layer_id) { - const auto lock_guard = Lock(); - - for (auto& display : displays) { - display.DestroyLayer(layer_id); - } -} - -std::optional<u32> Nvnflinger::FindBufferQueueId(u64 display_id, u64 layer_id) { - const auto lock_guard = Lock(); - const auto* const layer = FindLayer(display_id, layer_id); - - if (layer == nullptr) { - return std::nullopt; - } - - return layer->GetBinderId(); -} - -Result Nvnflinger::FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64 display_id) { - const auto lock_guard = Lock(); - auto* const display = FindDisplay(display_id); - - if (display == nullptr) { - return VI::ResultNotFound; - } - - *out_vsync_event = display->GetVSyncEvent(); - return ResultSuccess; -} - -VI::Display* Nvnflinger::FindDisplay(u64 display_id) { - const auto itr = - std::find_if(displays.begin(), displays.end(), - [&](const VI::Display& display) { return display.GetID() == display_id; }); - - if (itr == displays.end()) { - return nullptr; - } - - return &*itr; -} - -const VI::Display* Nvnflinger::FindDisplay(u64 display_id) const { - const auto itr = - std::find_if(displays.begin(), displays.end(), - [&](const VI::Display& display) { return display.GetID() == display_id; }); - - if (itr == displays.end()) { - return nullptr; - } - - return &*itr; -} - -VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) { - auto* const display = FindDisplay(display_id); - - if (display == nullptr) { - return nullptr; - } - - return display->FindLayer(layer_id); -} - -void Nvnflinger::Compose() { - for (auto& display : displays) { - // Trigger vsync for this display at the end of drawing - SCOPE_EXIT({ display.SignalVSyncEvent(); }); - - // Don't do anything for displays without layers. - if (!display.HasLayers()) { - continue; - } - - if (!system.IsPoweredOn()) { - return; // We are likely shutting down - } - - auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); - ASSERT(nvdisp); - - swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp); - } -} - -s64 Nvnflinger::GetNextTicks() const { - const auto& settings = Settings::values; - auto speed_scale = 1.f; - if (settings.use_multi_core.GetValue()) { - if (settings.use_speed_limit.GetValue()) { - // Scales the speed based on speed_limit setting on MC. SC is handled by - // SpeedLimiter::DoSpeedLimiting. - speed_scale = 100.f / settings.speed_limit.GetValue(); - } else { - // Run at unlocked framerate. - speed_scale = 0.01f; - } - } - - // Adjust by speed limit determined during composition. - speed_scale /= compose_speed_scale; - - if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) { - // Run at intended presentation rate during video playback. - speed_scale = 1.f; - } - - const f32 effective_fps = 60.f / static_cast<f32>(swap_interval); - return static_cast<s64>(speed_scale * (1000000000.f / effective_fps)); -} - -FbShareBufferManager& Nvnflinger::GetSystemBufferManager() { - const auto lock_guard = Lock(); - - if (!system_buffer_manager) { - system_buffer_manager = std::make_unique<FbShareBufferManager>(system, *this, nvdrv); - } +void LoopProcess(Core::System& system) { + const auto binder_server = std::make_shared<HosBinderDriverServer>(); + const auto surface_flinger = std::make_shared<SurfaceFlinger>(system, *binder_server); - return *system_buffer_manager; + auto server_manager = std::make_unique<ServerManager>(system); + server_manager->RegisterNamedService( + "dispdrv", std::make_shared<IHOSBinderDriver>(system, binder_server, surface_flinger)); + ServerManager::RunServer(std::move(server_manager)); } } // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index c984d55a0..5c41f3013 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h @@ -3,168 +3,12 @@ #pragma once -#include <list> -#include <memory> -#include <mutex> -#include <optional> -#include <thread> -#include <vector> - -#include "common/common_types.h" -#include "common/polyfill_thread.h" -#include "common/thread.h" -#include "core/hle/result.h" -#include "core/hle/service/kernel_helpers.h" - -namespace Common { -class Event; -} // namespace Common - -namespace Core::Timing { -class CoreTiming; -struct EventType; -} // namespace Core::Timing - -namespace Kernel { -class KReadableEvent; -} // namespace Kernel - -namespace Service::Nvidia { -class Module; -} // namespace Service::Nvidia - -namespace Service::VI { -class Display; -class Layer; -} // namespace Service::VI - -namespace Service::android { -class BufferQueueCore; -class BufferQueueProducer; -} // namespace Service::android +namespace Core { +class System; +} namespace Service::Nvnflinger { -class FbShareBufferManager; -class HardwareComposer; -class HosBinderDriverServer; - -class Nvnflinger final { -public: - explicit Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); - ~Nvnflinger(); - - void ShutdownLayers(); - - /// Sets the NVDrv module instance to use to send buffers to the GPU. - void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance); - - /// Opens the specified display and returns the ID. - /// - /// If an invalid display name is provided, then an empty optional is returned. - [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name); - - /// Closes the specified display by its ID. - /// - /// Returns false if an invalid display ID is provided. - [[nodiscard]] bool CloseDisplay(u64 display_id); - - /// Creates a layer on the specified display and returns the layer ID. - /// - /// If an invalid display ID is specified, then an empty optional is returned. - [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id); - - /// Opens a layer on all displays for the given layer ID. - bool OpenLayer(u64 layer_id); - - /// Closes a layer on all displays for the given layer ID. - bool CloseLayer(u64 layer_id); - - /// Makes a layer visible on all displays for the given layer ID. - void SetLayerVisibility(u64 layer_id, bool visible); - - /// Destroys the given layer ID. - void DestroyLayer(u64 layer_id); - - /// Finds the buffer queue ID of the specified layer in the specified display. - /// - /// If an invalid display ID or layer ID is provided, then an empty optional is returned. - [[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id); - - /// Gets the vsync event for the specified display. - /// - /// If an invalid display ID is provided, then VI::ResultNotFound is returned. - /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned. - [[nodiscard]] Result FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64 display_id); - - /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when - /// finished. - void Compose(); - - [[nodiscard]] s64 GetNextTicks() const; - - FbShareBufferManager& GetSystemBufferManager(); - -private: - struct Layer { - std::unique_ptr<android::BufferQueueCore> core; - std::unique_ptr<android::BufferQueueProducer> producer; - }; - - friend class FbShareBufferManager; - -private: - [[nodiscard]] std::unique_lock<std::mutex> Lock() const { - return std::unique_lock{*guard}; - } - - /// Finds the display identified by the specified ID. - [[nodiscard]] VI::Display* FindDisplay(u64 display_id); - - /// Finds the display identified by the specified ID. - [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const; - - /// Finds the layer identified by the specified ID in the desired display. - [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); - - /// Creates a layer with the specified layer ID in the desired display. - void CreateLayerAtId(VI::Display& display, u64 layer_id); - - void SplitVSync(std::stop_token stop_token); - - std::shared_ptr<Nvidia::Module> nvdrv; - s32 disp_fd; - - std::list<VI::Display> displays; - - /// Id to use for the next layer that is created, this counter is shared among all displays. - u64 next_layer_id = 1; - /// Id to use for the next buffer queue that is created, this counter is shared among all - /// layers. - u32 next_buffer_queue_id = 1; - - s32 swap_interval = 1; - f32 compose_speed_scale = 1.0f; - - bool is_abandoned = false; - - /// Event that handles screen composition. - std::shared_ptr<Core::Timing::EventType> multi_composition_event; - std::shared_ptr<Core::Timing::EventType> single_composition_event; - - std::unique_ptr<FbShareBufferManager> system_buffer_manager; - - std::shared_ptr<std::mutex> guard; - - Core::System& system; - - Common::Event vsync_signal; - - std::jthread vsync_thread; - - KernelHelpers::ServiceContext service_context; - - HosBinderDriverServer& hos_binder_driver_server; -}; +void LoopProcess(Core::System& system); } // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/surface_flinger.cpp b/src/core/hle/service/nvnflinger/surface_flinger.cpp new file mode 100644 index 000000000..8362b65e5 --- /dev/null +++ b/src/core/hle/service/nvnflinger/surface_flinger.cpp @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" +#include "core/hle/service/nvdrv/nvdrv_interface.h" +#include "core/hle/service/nvnflinger/display.h" +#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" +#include "core/hle/service/nvnflinger/surface_flinger.h" +#include "core/hle/service/sm/sm.h" + +#include "core/hle/service/nvnflinger/buffer_queue_consumer.h" +#include "core/hle/service/nvnflinger/buffer_queue_core.h" +#include "core/hle/service/nvnflinger/buffer_queue_producer.h" + +namespace Service::Nvnflinger { + +SurfaceFlinger::SurfaceFlinger(Core::System& system, HosBinderDriverServer& server) + : m_system(system), m_server(server), m_context(m_system, "SurfaceFlinger") { + nvdrv = m_system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true)->GetModule(); + disp_fd = nvdrv->Open("/dev/nvdisp_disp0", {}); +} + +SurfaceFlinger::~SurfaceFlinger() { + nvdrv->Close(disp_fd); +} + +void SurfaceFlinger::AddDisplay(u64 display_id) { + m_displays.emplace_back(display_id); +} + +void SurfaceFlinger::RemoveDisplay(u64 display_id) { + std::erase_if(m_displays, [&](auto& display) { return display.id == display_id; }); +} + +bool SurfaceFlinger::ComposeDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, + u64 display_id) { + auto* const display = this->FindDisplay(display_id); + if (!display || !display->stack.HasLayers()) { + return false; + } + + *out_swap_interval = + m_composer.ComposeLocked(out_compose_speed_scale, *display, + *nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd)); + return true; +} + +void SurfaceFlinger::CreateLayer(s32 consumer_binder_id) { + auto binder = std::static_pointer_cast<android::BufferQueueConsumer>( + m_server.TryGetBinder(consumer_binder_id)); + if (!binder) { + return; + } + + auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(binder)); + buffer_item_consumer->Connect(false); + + m_layers.layers.emplace_back( + std::make_shared<Layer>(std::move(buffer_item_consumer), consumer_binder_id)); +} + +void SurfaceFlinger::DestroyLayer(s32 consumer_binder_id) { + std::erase_if(m_layers.layers, + [&](auto& layer) { return layer->consumer_id == consumer_binder_id; }); +} + +void SurfaceFlinger::AddLayerToDisplayStack(u64 display_id, s32 consumer_binder_id) { + auto* const display = this->FindDisplay(display_id); + auto layer = this->FindLayer(consumer_binder_id); + + if (!display || !layer) { + return; + } + + display->stack.layers.emplace_back(std::move(layer)); +} + +void SurfaceFlinger::RemoveLayerFromDisplayStack(u64 display_id, s32 consumer_binder_id) { + auto* const display = this->FindDisplay(display_id); + if (!display) { + return; + } + + m_composer.RemoveLayerLocked(*display, consumer_binder_id); + std::erase_if(display->stack.layers, + [&](auto& layer) { return layer->consumer_id == consumer_binder_id; }); +} + +void SurfaceFlinger::SetLayerVisibility(s32 consumer_binder_id, bool visible) { + if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) { + layer->visible = visible; + return; + } +} + +void SurfaceFlinger::SetLayerBlending(s32 consumer_binder_id, LayerBlending blending) { + if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) { + layer->blending = blending; + return; + } +} + +Display* SurfaceFlinger::FindDisplay(u64 display_id) { + for (auto& display : m_displays) { + if (display.id == display_id) { + return &display; + } + } + + return nullptr; +} + +std::shared_ptr<Layer> SurfaceFlinger::FindLayer(s32 consumer_binder_id) { + for (auto& layer : m_layers.layers) { + if (layer->consumer_id == consumer_binder_id) { + return layer; + } + } + + return nullptr; +} + +void SurfaceFlinger::CreateBufferQueue(s32* out_consumer_binder_id, s32* out_producer_binder_id) { + auto& nvmap = nvdrv->GetContainer().GetNvMapFile(); + auto core = std::make_shared<android::BufferQueueCore>(); + auto producer = std::make_shared<android::BufferQueueProducer>(m_context, core, nvmap); + auto consumer = std::make_shared<android::BufferQueueConsumer>(core); + + *out_consumer_binder_id = m_server.RegisterBinder(std::move(consumer)); + *out_producer_binder_id = m_server.RegisterBinder(std::move(producer)); +} + +void SurfaceFlinger::DestroyBufferQueue(s32 consumer_binder_id, s32 producer_binder_id) { + m_server.UnregisterBinder(producer_binder_id); + m_server.UnregisterBinder(consumer_binder_id); +} + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/surface_flinger.h b/src/core/hle/service/nvnflinger/surface_flinger.h new file mode 100644 index 000000000..406281c83 --- /dev/null +++ b/src/core/hle/service/nvnflinger/surface_flinger.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <vector> + +#include "common/common_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvnflinger/hardware_composer.h" + +namespace Core { +class System; +} + +namespace Service::Nvidia { +class Module; +} + +// TODO: ISurfaceComposer +// TODO: ISurfaceComposerClient + +namespace Service::Nvnflinger { + +struct Display; +class HosBinderDriverServer; +enum class LayerBlending : u32; +struct Layer; + +class SurfaceFlinger { +public: + explicit SurfaceFlinger(Core::System& system, HosBinderDriverServer& server); + ~SurfaceFlinger(); + + void AddDisplay(u64 display_id); + void RemoveDisplay(u64 display_id); + bool ComposeDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id); + + void CreateLayer(s32 consumer_binder_id); + void DestroyLayer(s32 consumer_binder_id); + + void AddLayerToDisplayStack(u64 display_id, s32 consumer_binder_id); + void RemoveLayerFromDisplayStack(u64 display_id, s32 consumer_binder_id); + + void SetLayerVisibility(s32 consumer_binder_id, bool visible); + void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending); + +private: + Display* FindDisplay(u64 display_id); + std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id); + +public: + // TODO: these don't belong here + void CreateBufferQueue(s32* out_consumer_binder_id, s32* out_producer_binder_id); + void DestroyBufferQueue(s32 consumer_binder_id, s32 producer_binder_id); + +private: + Core::System& m_system; + HosBinderDriverServer& m_server; + KernelHelpers::ServiceContext m_context; + + std::vector<Display> m_displays; + LayerStack m_layers; + std::shared_ptr<Nvidia::Module> nvdrv; + s32 disp_fd; + HardwareComposer m_composer; +}; + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/omm/omm.cpp b/src/core/hle/service/omm/omm.cpp new file mode 100644 index 000000000..b95319e26 --- /dev/null +++ b/src/core/hle/service/omm/omm.cpp @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/omm/omm.h" +#include "core/hle/service/omm/operation_mode_manager.h" +#include "core/hle/service/omm/policy_manager_system.h" +#include "core/hle/service/omm/power_state_interface.h" +#include "core/hle/service/server_manager.h" + +namespace Service::OMM { + +void LoopProcess(Core::System& system) { + auto server_manager = std::make_unique<ServerManager>(system); + + server_manager->RegisterNamedService("idle:sys", + std::make_shared<IPolicyManagerSystem>(system)); + server_manager->RegisterNamedService("omm", std::make_shared<IOperationModeManager>(system)); + server_manager->RegisterNamedService("spsm", std::make_shared<IPowerStateInterface>(system)); + ServerManager::RunServer(std::move(server_manager)); +} + +} // namespace Service::OMM diff --git a/src/core/hle/service/omm/omm.h b/src/core/hle/service/omm/omm.h new file mode 100644 index 000000000..7bf04688a --- /dev/null +++ b/src/core/hle/service/omm/omm.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Core { +class System; +} + +namespace Service::OMM { + +void LoopProcess(Core::System& system); + +} // namespace Service::OMM diff --git a/src/core/hle/service/omm/operation_mode_manager.cpp b/src/core/hle/service/omm/operation_mode_manager.cpp new file mode 100644 index 000000000..fe7ed84a7 --- /dev/null +++ b/src/core/hle/service/omm/operation_mode_manager.cpp @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/omm/operation_mode_manager.h" + +namespace Service::OMM { + +IOperationModeManager::IOperationModeManager(Core::System& system_) + : ServiceFramework{system_, "omm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetOperationMode"}, + {1, nullptr, "GetOperationModeChangeEvent"}, + {2, nullptr, "EnableAudioVisual"}, + {3, nullptr, "DisableAudioVisual"}, + {4, nullptr, "EnterSleepAndWait"}, + {5, nullptr, "GetCradleStatus"}, + {6, nullptr, "FadeInDisplay"}, + {7, nullptr, "FadeOutDisplay"}, + {8, nullptr, "GetCradleFwVersion"}, + {9, nullptr, "NotifyCecSettingsChanged"}, + {10, nullptr, "SetOperationModePolicy"}, + {11, nullptr, "GetDefaultDisplayResolution"}, + {12, nullptr, "GetDefaultDisplayResolutionChangeEvent"}, + {13, nullptr, "UpdateDefaultDisplayResolution"}, + {14, nullptr, "ShouldSleepOnBoot"}, + {15, nullptr, "NotifyHdcpApplicationExecutionStarted"}, + {16, nullptr, "NotifyHdcpApplicationExecutionFinished"}, + {17, nullptr, "NotifyHdcpApplicationDrawingStarted"}, + {18, nullptr, "NotifyHdcpApplicationDrawingFinished"}, + {19, nullptr, "GetHdcpAuthenticationFailedEvent"}, + {20, nullptr, "GetHdcpAuthenticationFailedEmulationEnabled"}, + {21, nullptr, "SetHdcpAuthenticationFailedEmulation"}, + {22, nullptr, "GetHdcpStateChangeEvent"}, + {23, nullptr, "GetHdcpState"}, + {24, nullptr, "ShowCardUpdateProcessing"}, + {25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"}, + {26, nullptr, "GetOperationModeSystemInfo"}, + {27, nullptr, "GetAppletFullAwakingSystemEvent"}, + {28, nullptr, "CreateCradleFirmwareUpdater"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IOperationModeManager::~IOperationModeManager() = default; + +} // namespace Service::OMM diff --git a/src/core/hle/service/omm/operation_mode_manager.h b/src/core/hle/service/omm/operation_mode_manager.h new file mode 100644 index 000000000..32bc7b2f9 --- /dev/null +++ b/src/core/hle/service/omm/operation_mode_manager.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::OMM { + +class IOperationModeManager final : public ServiceFramework<IOperationModeManager> { +public: + explicit IOperationModeManager(Core::System& system_); + ~IOperationModeManager() override; +}; + +} // namespace Service::OMM diff --git a/src/core/hle/service/omm/policy_manager_system.cpp b/src/core/hle/service/omm/policy_manager_system.cpp new file mode 100644 index 000000000..1cd6fd807 --- /dev/null +++ b/src/core/hle/service/omm/policy_manager_system.cpp @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/omm/policy_manager_system.h" + +namespace Service::OMM { + +IPolicyManagerSystem::IPolicyManagerSystem(Core::System& system_) + : ServiceFramework{system_, "idle:sys"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetAutoPowerDownEvent"}, + {1, nullptr, "IsAutoPowerDownRequested"}, + {2, nullptr, "Unknown2"}, + {3, nullptr, "SetHandlingContext"}, + {4, nullptr, "LoadAndApplySettings"}, + {5, nullptr, "ReportUserIsActive"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IPolicyManagerSystem::~IPolicyManagerSystem() = default; + +} // namespace Service::OMM diff --git a/src/core/hle/service/omm/policy_manager_system.h b/src/core/hle/service/omm/policy_manager_system.h new file mode 100644 index 000000000..151ca0d2e --- /dev/null +++ b/src/core/hle/service/omm/policy_manager_system.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::OMM { + +class IPolicyManagerSystem final : public ServiceFramework<IPolicyManagerSystem> { +public: + explicit IPolicyManagerSystem(Core::System& system_); + ~IPolicyManagerSystem() override; +}; + +} // namespace Service::OMM diff --git a/src/core/hle/service/omm/power_state_interface.cpp b/src/core/hle/service/omm/power_state_interface.cpp new file mode 100644 index 000000000..22cac8259 --- /dev/null +++ b/src/core/hle/service/omm/power_state_interface.cpp @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/omm/power_state_interface.h" + +namespace Service::OMM { + +IPowerStateInterface::IPowerStateInterface(Core::System& system_) + : ServiceFramework{system_, "spsm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetState"}, + {1, nullptr, "EnterSleep"}, + {2, nullptr, "GetLastWakeReason"}, + {3, nullptr, "Shutdown"}, + {4, nullptr, "GetNotificationMessageEventHandle"}, + {5, nullptr, "ReceiveNotificationMessage"}, + {6, nullptr, "AnalyzeLogForLastSleepWakeSequence"}, + {7, nullptr, "ResetEventLog"}, + {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"}, + {9, nullptr, "ChangeHomeButtonLongPressingTime"}, + {10, nullptr, "PutErrorState"}, + {11, nullptr, "InvalidateCurrentHomeButtonPressing"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IPowerStateInterface::~IPowerStateInterface() = default; + +} // namespace Service::OMM diff --git a/src/core/hle/service/omm/power_state_interface.h b/src/core/hle/service/omm/power_state_interface.h new file mode 100644 index 000000000..825a6512d --- /dev/null +++ b/src/core/hle/service/omm/power_state_interface.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::OMM { + +class IPowerStateInterface final : public ServiceFramework<IPowerStateInterface> { +public: + explicit IPowerStateInterface(Core::System& system_); + ~IPowerStateInterface() override; +}; + +} // namespace Service::OMM diff --git a/src/core/hle/service/psc/time/static.cpp b/src/core/hle/service/psc/time/static.cpp index 24b85cc61..9a0adb295 100644 --- a/src/core/hle/service/psc/time/static.cpp +++ b/src/core/hle/service/psc/time/static.cpp @@ -144,7 +144,9 @@ Result StaticService::GetStandardSteadyClockRtcValue(Out<s64> out_rtc_value) { Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled( Out<bool> out_is_enabled) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_enabled={}", *out_is_enabled); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_is_enabled={}", *out_is_enabled); + }; R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized); @@ -180,7 +182,9 @@ Result StaticService::GetStandardUserSystemClockInitialYear(Out<s32> out_year) { } Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> out_is_sufficient) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient); + }; *out_is_sufficient = m_network_system_clock.IsAccuracySufficient(); @@ -189,7 +193,9 @@ Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> o Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( Out<SteadyClockTimePoint> out_time_point) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); + }; R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized); @@ -200,7 +206,9 @@ Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( Out<s64> out_time, const SystemClockContext& context) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); + }; R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized); @@ -219,8 +227,9 @@ Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( } Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType type) { - SCOPE_EXIT( - { LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot); + }; SystemClockContext user_context{}; R_TRY(m_user_system_clock.GetContext(user_context)); @@ -234,11 +243,11 @@ Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType t Result StaticService::GetClockSnapshotFromSystemClockContext( TimeType type, OutClockSnapshot out_snapshot, const SystemClockContext& user_context, const SystemClockContext& network_context) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. type={} user_context={} network_context={} out_snapshot={}", type, user_context, network_context, *out_snapshot); - }); + }; R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type)); } @@ -246,9 +255,9 @@ Result StaticService::GetClockSnapshotFromSystemClockContext( Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference, InClockSnapshot a, InClockSnapshot b) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. a={} b={} out_difference={}", *a, *b, *out_difference); - }); + }; auto diff_s = std::chrono::seconds(b->user_context.offset) - std::chrono::seconds(a->user_context.offset); @@ -276,7 +285,9 @@ Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64> Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); + }; s64 time_s{}; auto res = diff --git a/src/core/hle/service/psc/time/steady_clock.cpp b/src/core/hle/service/psc/time/steady_clock.cpp index 948610a2b..78dcf532c 100644 --- a/src/core/hle/service/psc/time/steady_clock.cpp +++ b/src/core/hle/service/psc/time/steady_clock.cpp @@ -29,7 +29,9 @@ SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr<TimeManager> man } Result SteadyClock::GetCurrentTimePoint(Out<SteadyClockTimePoint> out_time_point) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); + }; R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), ResultClockUninitialized); @@ -38,7 +40,9 @@ Result SteadyClock::GetCurrentTimePoint(Out<SteadyClockTimePoint> out_time_point } Result SteadyClock::GetTestOffset(Out<s64> out_test_offset) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_test_offset={}", *out_test_offset); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_test_offset={}", *out_test_offset); + }; R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), ResultClockUninitialized); @@ -59,7 +63,9 @@ Result SteadyClock::SetTestOffset(s64 test_offset) { } Result SteadyClock::GetRtcValue(Out<s64> out_rtc_value) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value); + }; R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), ResultClockUninitialized); @@ -68,7 +74,9 @@ Result SteadyClock::GetRtcValue(Out<s64> out_rtc_value) { } Result SteadyClock::IsRtcResetDetected(Out<bool> out_is_detected) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_detected={}", *out_is_detected); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_is_detected={}", *out_is_detected); + }; R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), ResultClockUninitialized); @@ -78,7 +86,9 @@ Result SteadyClock::IsRtcResetDetected(Out<bool> out_is_detected) { } Result SteadyClock::GetSetupResultValue(Out<Result> out_result) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_result=0x{:X}", out_result->raw); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_result=0x{:X}", out_result->raw); + }; R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), ResultClockUninitialized); @@ -88,8 +98,9 @@ Result SteadyClock::GetSetupResultValue(Out<Result> out_result) { } Result SteadyClock::GetInternalOffset(Out<s64> out_internal_offset) { - SCOPE_EXIT( - { LOG_DEBUG(Service_Time, "called. out_internal_offset={}", *out_internal_offset); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_internal_offset={}", *out_internal_offset); + }; R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), ResultClockUninitialized); diff --git a/src/core/hle/service/psc/time/system_clock.cpp b/src/core/hle/service/psc/time/system_clock.cpp index b4e9264d8..9f841d8e0 100644 --- a/src/core/hle/service/psc/time/system_clock.cpp +++ b/src/core/hle/service/psc/time/system_clock.cpp @@ -26,7 +26,9 @@ SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, boo } Result SystemClock::GetCurrentTime(Out<s64> out_time) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time={}", *out_time); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_time={}", *out_time); + }; R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), ResultClockUninitialized); @@ -45,7 +47,9 @@ Result SystemClock::SetCurrentTime(s64 time) { } Result SystemClock::GetSystemClockContext(Out<SystemClockContext> out_context) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_context={}", *out_context); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_context={}", *out_context); + }; R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), ResultClockUninitialized); diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp index 2f80030a4..9e0674f27 100644 --- a/src/core/hle/service/psc/time/time_zone_service.cpp +++ b/src/core/hle/service/psc/time/time_zone_service.cpp @@ -37,7 +37,9 @@ TimeZoneService::TimeZoneService(Core::System& system_, StandardSteadyClockCore& } Result TimeZoneService::GetDeviceLocationName(Out<LocationName> out_location_name) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name); + }; R_RETURN(m_time_zone.GetLocationName(*out_location_name)); } @@ -50,7 +52,9 @@ Result TimeZoneService::SetDeviceLocationName(const LocationName& location_name) } Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_count={}", *out_count); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_count={}", *out_count); + }; R_RETURN(m_time_zone.GetTotalLocationCount(*out_count)); } @@ -69,17 +73,19 @@ Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, const LocationName& l } Result TimeZoneService::GetTimeZoneRuleVersion(Out<RuleVersion> out_rule_version) { - SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version); }); + SCOPE_EXIT { + LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version); + }; R_RETURN(m_time_zone.GetRuleVersion(*out_rule_version)); } Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime( Out<LocationName> out_location_name, Out<SteadyClockTimePoint> out_time_point) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. out_location_name={} out_time_point={}", *out_location_name, *out_time_point); - }); + }; R_TRY(m_time_zone.GetLocationName(*out_location_name)); R_RETURN(m_time_zone.GetTimePoint(*out_time_point)); @@ -116,10 +122,10 @@ Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle( Result TimeZoneService::ToCalendarTime(Out<CalendarTime> out_calendar_time, Out<CalendarAdditionalInfo> out_additional_info, s64 time, InRule rule) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time, *out_calendar_time, *out_additional_info); - }); + }; R_RETURN( m_time_zone.ToCalendarTime(*out_calendar_time, *out_additional_info, time, *rule.Get())); @@ -128,10 +134,10 @@ Result TimeZoneService::ToCalendarTime(Out<CalendarTime> out_calendar_time, Result TimeZoneService::ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_time, Out<CalendarAdditionalInfo> out_additional_info, s64 time) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time, *out_calendar_time, *out_additional_info); - }); + }; R_RETURN(m_time_zone.ToCalendarTimeWithMyRule(*out_calendar_time, *out_additional_info, time)); } @@ -139,11 +145,11 @@ Result TimeZoneService::ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_ Result TimeZoneService::ToPosixTime(Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times, const CalendarTime& calendar_time, InRule rule) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ", calendar_time, *out_count, out_times[0], out_times[1]); - }); + }; R_RETURN( m_time_zone.ToPosixTime(*out_count, out_times, out_times.size(), calendar_time, *rule)); @@ -152,11 +158,11 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count, Result TimeZoneService::ToPosixTimeWithMyRule(Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times, const CalendarTime& calendar_time) { - SCOPE_EXIT({ + SCOPE_EXIT { LOG_DEBUG(Service_Time, "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ", calendar_time, *out_count, out_times[0], out_times[1]); - }); + }; R_RETURN( m_time_zone.ToPosixTimeWithMyRule(*out_count, out_times, out_times.size(), calendar_time)); diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp index 8c7f94c8c..0b41bbcb9 100644 --- a/src/core/hle/service/server_manager.cpp +++ b/src/core/hle/service/server_manager.cpp @@ -177,10 +177,10 @@ Result ServerManager::ManageNamedPort(const std::string& service_name, Kernel::KPort::Register(m_system.Kernel(), port); // Ensure that our reference to the port is closed if we fail to register it. - SCOPE_EXIT({ + SCOPE_EXIT { port->GetClientPort().Close(); port->GetServerPort().Close(); - }); + }; // Register the object name with the kernel. R_TRY(Kernel::KObjectName::NewFromName(m_system.Kernel(), std::addressof(port->GetClientPort()), @@ -237,7 +237,9 @@ void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_thre } Result ServerManager::LoopProcess() { - SCOPE_EXIT({ m_stopped.Set(); }); + SCOPE_EXIT { + m_stopped.Set(); + }; R_RETURN(this->LoopProcessImpl()); } diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index f68c3c686..ce5e3b5b4 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -7,67 +7,10 @@ #include "common/settings.h" #include "core/core.h" #include "core/hle/ipc.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_server_port.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/service/acc/acc.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/aoc/aoc_u.h" -#include "core/hle/service/apm/apm.h" -#include "core/hle/service/audio/audio.h" -#include "core/hle/service/bcat/bcat.h" -#include "core/hle/service/bpc/bpc.h" -#include "core/hle/service/btdrv/btdrv.h" -#include "core/hle/service/btm/btm.h" -#include "core/hle/service/caps/caps.h" -#include "core/hle/service/erpt/erpt.h" -#include "core/hle/service/es/es.h" -#include "core/hle/service/eupld/eupld.h" -#include "core/hle/service/fatal/fatal.h" -#include "core/hle/service/fgm/fgm.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/friend/friend.h" -#include "core/hle/service/glue/glue.h" -#include "core/hle/service/grc/grc.h" -#include "core/hle/service/hid/hid.h" #include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/jit/jit.h" -#include "core/hle/service/lbl/lbl.h" -#include "core/hle/service/ldn/ldn.h" -#include "core/hle/service/ldr/ldr.h" -#include "core/hle/service/lm/lm.h" -#include "core/hle/service/mig/mig.h" -#include "core/hle/service/mii/mii.h" -#include "core/hle/service/mm/mm_u.h" -#include "core/hle/service/mnpp/mnpp_app.h" -#include "core/hle/service/ncm/ncm.h" -#include "core/hle/service/nfc/nfc.h" -#include "core/hle/service/nfp/nfp.h" -#include "core/hle/service/ngc/ngc.h" -#include "core/hle/service/nifm/nifm.h" -#include "core/hle/service/nim/nim.h" -#include "core/hle/service/npns/npns.h" -#include "core/hle/service/ns/ns.h" -#include "core/hle/service/nvdrv/nvdrv.h" -#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" -#include "core/hle/service/nvnflinger/nvnflinger.h" -#include "core/hle/service/olsc/olsc.h" -#include "core/hle/service/pcie/pcie.h" -#include "core/hle/service/pctl/pctl_module.h" -#include "core/hle/service/pcv/pcv.h" -#include "core/hle/service/pm/pm.h" -#include "core/hle/service/prepo/prepo.h" -#include "core/hle/service/psc/psc.h" -#include "core/hle/service/ptm/ptm.h" -#include "core/hle/service/ro/ro.h" #include "core/hle/service/service.h" -#include "core/hle/service/set/settings.h" #include "core/hle/service/sm/sm.h" -#include "core/hle/service/sockets/sockets.h" -#include "core/hle/service/spl/spl_module.h" -#include "core/hle/service/ssl/ssl.h" -#include "core/hle/service/usb/usb.h" -#include "core/hle/service/vi/vi.h" #include "core/reporter.h" namespace Service { @@ -208,81 +151,4 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, return result; } -/// Initialize Services -Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) - : hos_binder_driver_server{std::make_unique<Nvnflinger::HosBinderDriverServer>(system)}, - nv_flinger{std::make_unique<Nvnflinger::Nvnflinger>(system, *hos_binder_driver_server)} { - - auto& kernel = system.Kernel(); - - // Nvnflinger needs to be accessed by several services like Vi and AppletOE so we instantiate it - // here and pass it into the respective InstallInterfaces functions. - system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); - - // clang-format off - kernel.RunOnHostCoreProcess("audio", [&] { Audio::LoopProcess(system); }).detach(); - kernel.RunOnHostCoreProcess("FS", [&] { FileSystem::LoopProcess(system); }).detach(); - kernel.RunOnHostCoreProcess("jit", [&] { JIT::LoopProcess(system); }).detach(); - kernel.RunOnHostCoreProcess("ldn", [&] { LDN::LoopProcess(system); }).detach(); - kernel.RunOnHostCoreProcess("Loader", [&] { LDR::LoopProcess(system); }).detach(); - kernel.RunOnHostCoreProcess("nvservices", [&] { Nvidia::LoopProcess(*nv_flinger, system); }).detach(); - kernel.RunOnHostCoreProcess("bsdsocket", [&] { Sockets::LoopProcess(system); }).detach(); - kernel.RunOnHostCoreProcess("vi", [&] { VI::LoopProcess(system, *nv_flinger, *hos_binder_driver_server); }).detach(); - - kernel.RunOnGuestCoreProcess("sm", [&] { SM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("account", [&] { Account::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("am", [&] { AM::LoopProcess(*nv_flinger, system); }); - kernel.RunOnGuestCoreProcess("aoc", [&] { AOC::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("apm", [&] { APM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("bcat", [&] { BCAT::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("bpc", [&] { BPC::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("btdrv", [&] { BtDrv::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("btm", [&] { BTM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("capsrv", [&] { Capture::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("erpt", [&] { ERPT::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("es", [&] { ES::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("eupld", [&] { EUPLD::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); }); - // glue depends on settings and psc, so they must come first - kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("lbl", [&] { LBL::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("LogManager.Prod", [&] { LM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("mig", [&] { Migration::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("mii", [&] { Mii::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("mm", [&] { MM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("mnpp", [&] { MNPP::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("NCM", [&] { NCM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("nfc", [&] { NFC::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("nfp", [&] { NFP::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("ngc", [&] { NGC::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("nifm", [&] { NIFM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("nim", [&] { NIM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("npns", [&] { NPNS::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("ns", [&] { NS::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("olsc", [&] { OLSC::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("pcie", [&] { PCIe::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("pctl", [&] { PCTL::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); }); - // clang-format on -} - -Services::~Services() = default; - -void Services::KillNVNFlinger() { - nv_flinger->ShutdownLayers(); -} - } // namespace Service diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 22d1343d5..36aae1c79 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -28,11 +28,6 @@ namespace FileSystem { class FileSystemController; } -namespace Nvnflinger { -class HosBinderDriverServer; -class Nvnflinger; -} // namespace Nvnflinger - namespace SM { class ServiceManager; } @@ -236,20 +231,4 @@ private: } }; -/** - * The purpose of this class is to own any objects that need to be shared across the other service - * implementations. Will be torn down when the global system instance is shutdown. - */ -class Services final { -public: - explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); - ~Services(); - - void KillNVNFlinger(); - -private: - std::unique_ptr<Nvnflinger::HosBinderDriverServer> hos_binder_driver_server; - std::unique_ptr<Nvnflinger::Nvnflinger> nv_flinger; -}; - } // namespace Service diff --git a/src/core/hle/service/services.cpp b/src/core/hle/service/services.cpp new file mode 100644 index 000000000..d6c6eff50 --- /dev/null +++ b/src/core/hle/service/services.cpp @@ -0,0 +1,136 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/services.h" + +#include "core/hle/service/acc/acc.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/aoc/aoc_u.h" +#include "core/hle/service/apm/apm.h" +#include "core/hle/service/audio/audio.h" +#include "core/hle/service/bcat/bcat.h" +#include "core/hle/service/bpc/bpc.h" +#include "core/hle/service/btdrv/btdrv.h" +#include "core/hle/service/btm/btm.h" +#include "core/hle/service/caps/caps.h" +#include "core/hle/service/erpt/erpt.h" +#include "core/hle/service/es/es.h" +#include "core/hle/service/eupld/eupld.h" +#include "core/hle/service/fatal/fatal.h" +#include "core/hle/service/fgm/fgm.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/friend/friend.h" +#include "core/hle/service/glue/glue.h" +#include "core/hle/service/grc/grc.h" +#include "core/hle/service/hid/hid.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/jit/jit.h" +#include "core/hle/service/lbl/lbl.h" +#include "core/hle/service/ldn/ldn.h" +#include "core/hle/service/ldr/ldr.h" +#include "core/hle/service/lm/lm.h" +#include "core/hle/service/mig/mig.h" +#include "core/hle/service/mii/mii.h" +#include "core/hle/service/mm/mm_u.h" +#include "core/hle/service/mnpp/mnpp_app.h" +#include "core/hle/service/ncm/ncm.h" +#include "core/hle/service/nfc/nfc.h" +#include "core/hle/service/nfp/nfp.h" +#include "core/hle/service/ngc/ngc.h" +#include "core/hle/service/nifm/nifm.h" +#include "core/hle/service/nim/nim.h" +#include "core/hle/service/npns/npns.h" +#include "core/hle/service/ns/ns.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" +#include "core/hle/service/olsc/olsc.h" +#include "core/hle/service/omm/omm.h" +#include "core/hle/service/pcie/pcie.h" +#include "core/hle/service/pctl/pctl_module.h" +#include "core/hle/service/pcv/pcv.h" +#include "core/hle/service/pm/pm.h" +#include "core/hle/service/prepo/prepo.h" +#include "core/hle/service/psc/psc.h" +#include "core/hle/service/ptm/ptm.h" +#include "core/hle/service/ro/ro.h" +#include "core/hle/service/service.h" +#include "core/hle/service/set/settings.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/sockets/sockets.h" +#include "core/hle/service/spl/spl_module.h" +#include "core/hle/service/ssl/ssl.h" +#include "core/hle/service/usb/usb.h" +#include "core/hle/service/vi/vi.h" + +namespace Service { + +Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, + std::stop_token token) { + auto& kernel = system.Kernel(); + + system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); + + // clang-format off + kernel.RunOnHostCoreProcess("audio", [&] { Audio::LoopProcess(system); }).detach(); + kernel.RunOnHostCoreProcess("FS", [&] { FileSystem::LoopProcess(system); }).detach(); + kernel.RunOnHostCoreProcess("jit", [&] { JIT::LoopProcess(system); }).detach(); + kernel.RunOnHostCoreProcess("ldn", [&] { LDN::LoopProcess(system); }).detach(); + kernel.RunOnHostCoreProcess("Loader", [&] { LDR::LoopProcess(system); }).detach(); + kernel.RunOnHostCoreProcess("nvservices", [&] { Nvidia::LoopProcess(system); }).detach(); + kernel.RunOnHostCoreProcess("bsdsocket", [&] { Sockets::LoopProcess(system); }).detach(); + kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach(); + + kernel.RunOnGuestCoreProcess("sm", [&] { SM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("account", [&] { Account::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("am", [&] { AM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("aoc", [&] { AOC::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("apm", [&] { APM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("bcat", [&] { BCAT::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("bpc", [&] { BPC::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("btdrv", [&] { BtDrv::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("btm", [&] { BTM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("capsrv", [&] { Capture::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("erpt", [&] { ERPT::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("es", [&] { ES::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("eupld", [&] { EUPLD::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("lbl", [&] { LBL::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("LogManager.Prod", [&] { LM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("mig", [&] { Migration::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("mii", [&] { Mii::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("mm", [&] { MM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("mnpp", [&] { MNPP::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("nvnflinger", [&] { Nvnflinger::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("NCM", [&] { NCM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("nfc", [&] { NFC::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("nfp", [&] { NFP::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("ngc", [&] { NGC::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("nifm", [&] { NIFM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("nim", [&] { NIM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("npns", [&] { NPNS::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("ns", [&] { NS::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("olsc", [&] { OLSC::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("omm", [&] { OMM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("pcie", [&] { PCIe::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("pctl", [&] { PCTL::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); }); + // clang-format on +} + +Services::~Services() = default; + +} // namespace Service diff --git a/src/core/hle/service/services.h b/src/core/hle/service/services.h new file mode 100644 index 000000000..a99fa1e53 --- /dev/null +++ b/src/core/hle/service/services.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/polyfill_thread.h" +#include "core/hle/service/sm/sm.h" + +namespace Service { + +/** + * The purpose of this class is to own any objects that need to be shared across the other service + * implementations. Will be torn down when the global system instance is shutdown. + */ +class Services final { +public: + explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, + std::stop_token token); + ~Services(); +}; + +} // namespace Service diff --git a/src/core/hle/service/set/key_code_map.h b/src/core/hle/service/set/key_code_map.h new file mode 100644 index 000000000..c76dc5729 --- /dev/null +++ b/src/core/hle/service/set/key_code_map.h @@ -0,0 +1,973 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" + +namespace Service::Set { + +// Raw key codes map extracted from the settings sysmodule FW 16.2.0 +// This is nn::kpr::KeyCodeMap +using KeyCodeMap = std::array<u8, 0x1000>; + +constexpr KeyCodeMap KeyCodeMapChineseTraditional = { + 0x61, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x07, 0x31, 0x00, 0x00, 0xE5, 0x65, 0x00, 0x00, 0x01, 0x10, + 0x62, 0x00, 0x42, 0x00, 0x16, 0x31, 0x00, 0x00, 0x08, 0x67, 0x00, 0x00, 0x01, 0x10, 0x63, 0x00, + 0x43, 0x00, 0x0F, 0x31, 0x00, 0x00, 0xD1, 0x91, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, + 0x0E, 0x31, 0x00, 0x00, 0x28, 0x67, 0x00, 0x00, 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x0D, 0x31, + 0x00, 0x00, 0x34, 0x6C, 0x00, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x11, 0x31, 0x00, 0x00, + 0x6B, 0x70, 0x00, 0x00, 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x15, 0x31, 0x00, 0x00, 0x1F, 0x57, + 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x18, 0x31, 0x00, 0x00, 0xF9, 0x7A, 0x00, 0x00, + 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x1B, 0x31, 0x00, 0x00, 0x08, 0x62, 0x00, 0x00, 0x01, 0x10, + 0x6A, 0x00, 0x4A, 0x00, 0x28, 0x31, 0x00, 0x00, 0x41, 0x53, 0x00, 0x00, 0x01, 0x10, 0x6B, 0x00, + 0x4B, 0x00, 0x1C, 0x31, 0x00, 0x00, 0x27, 0x59, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, + 0x20, 0x31, 0x00, 0x00, 0x2D, 0x4E, 0x00, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x29, 0x31, + 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x19, 0x31, 0x00, 0x00, + 0x13, 0x5F, 0x00, 0x00, 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x1F, 0x31, 0x00, 0x00, 0xBA, 0x4E, + 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x23, 0x31, 0x00, 0x00, 0xC3, 0x5F, 0x00, 0x00, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x06, 0x31, 0x00, 0x00, 0x4B, 0x62, 0x00, 0x00, 0x01, 0x10, + 0x72, 0x00, 0x52, 0x00, 0x10, 0x31, 0x00, 0x00, 0xE3, 0x53, 0x00, 0x00, 0x01, 0x10, 0x73, 0x00, + 0x53, 0x00, 0x0B, 0x31, 0x00, 0x00, 0x38, 0x5C, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, + 0x14, 0x31, 0x00, 0x00, 0xFF, 0x5E, 0x00, 0x00, 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x27, 0x31, + 0x00, 0x00, 0x71, 0x5C, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x12, 0x31, 0x00, 0x00, + 0x73, 0x59, 0x00, 0x00, 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x0A, 0x31, 0x00, 0x00, 0x30, 0x75, + 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x0C, 0x31, 0x00, 0x00, 0xE3, 0x96, 0x00, 0x00, + 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x17, 0x31, 0x00, 0x00, 0x5C, 0x53, 0x00, 0x00, 0x01, 0x10, + 0x7A, 0x00, 0x5A, 0x00, 0x08, 0x31, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x10, 0x31, 0x00, + 0x21, 0x00, 0x05, 0x31, 0x00, 0x00, 0x31, 0x00, 0x21, 0x00, 0x00, 0x10, 0x32, 0x00, 0x40, 0x00, + 0x09, 0x31, 0x00, 0x00, 0x32, 0x00, 0x40, 0x00, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0xC7, 0x02, + 0x00, 0x00, 0x33, 0x00, 0x23, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0xCB, 0x02, 0x00, 0x00, + 0x34, 0x00, 0x24, 0x00, 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0x13, 0x31, 0x00, 0x00, 0x35, 0x00, + 0x25, 0x00, 0x00, 0x10, 0x36, 0x00, 0x5E, 0x00, 0xCA, 0x02, 0x00, 0x00, 0x36, 0x00, 0x5E, 0x00, + 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0xD9, 0x02, 0x00, 0x00, 0x37, 0x00, 0x26, 0x00, 0x00, 0x10, + 0x38, 0x00, 0x2A, 0x00, 0x1A, 0x31, 0x00, 0x00, 0x38, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x39, 0x00, + 0x28, 0x00, 0x1E, 0x31, 0x00, 0x00, 0x39, 0x00, 0x28, 0x00, 0x00, 0x10, 0x30, 0x00, 0x29, 0x00, + 0x22, 0x31, 0x00, 0x00, 0x30, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, + 0x2D, 0x00, 0x5F, 0x00, 0x26, 0x31, 0x00, 0x00, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x10, 0x3D, 0x00, + 0x2B, 0x00, 0x3D, 0x00, 0x2B, 0x00, 0x3D, 0x00, 0x2B, 0x00, 0x00, 0x10, 0x5B, 0x00, 0x7B, 0x00, + 0x5B, 0x00, 0x7B, 0x00, 0x5B, 0x00, 0x7B, 0x00, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x5D, 0x00, + 0x7D, 0x00, 0x5D, 0x00, 0x7D, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, + 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, + 0x7C, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0x24, 0x31, 0x00, 0x00, 0x3B, 0x00, 0x3A, 0x00, + 0x00, 0x10, 0x27, 0x00, 0x22, 0x00, 0x27, 0x00, 0x22, 0x00, 0x27, 0x00, 0x22, 0x00, 0x00, 0x10, + 0x60, 0x00, 0x7E, 0x00, 0x60, 0x00, 0x7E, 0x00, 0x60, 0x00, 0x7E, 0x00, 0x00, 0x10, 0x2C, 0x00, + 0x3C, 0x00, 0x1D, 0x31, 0x00, 0x00, 0x2C, 0x00, 0x3C, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x3E, 0x00, + 0x21, 0x31, 0x00, 0x00, 0x2E, 0x00, 0x3E, 0x00, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x25, 0x31, + 0x00, 0x00, 0x2F, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, + 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, + 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, + 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, + 0x20, 0x00, 0x35, 0x00, 0x20, 0x00, 0x35, 0x00, 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, + 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, + 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00, + 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, + 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapChineseSimplified = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x01, 0x10, 0x63, 0x00, + 0x43, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x01, 0x10, + 0x66, 0x00, 0x46, 0x00, 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, + 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x01, 0x10, 0x6B, 0x00, + 0x4B, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x01, 0x10, + 0x6E, 0x00, 0x4E, 0x00, 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x01, 0x10, 0x73, 0x00, + 0x53, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x01, 0x10, + 0x76, 0x00, 0x56, 0x00, 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, + 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x10, 0x31, 0x00, + 0x21, 0x00, 0x00, 0x10, 0x32, 0x00, 0x40, 0x00, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0x00, 0x10, + 0x34, 0x00, 0x24, 0x00, 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0x00, 0x10, 0x36, 0x00, 0x5E, 0x00, + 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x39, 0x00, + 0x28, 0x00, 0x00, 0x10, 0x30, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x10, 0x3D, 0x00, + 0x2B, 0x00, 0x00, 0x10, 0x5B, 0x00, 0x7B, 0x00, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x00, 0x10, + 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00, + 0x00, 0x10, 0x27, 0x00, 0x22, 0x00, 0x00, 0x10, 0x60, 0x00, 0x7E, 0x00, 0x00, 0x10, 0x2C, 0x00, + 0x3C, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x3E, 0x00, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, + 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, + 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x36, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0xFF, 0x20, + 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapKorean = { + 0x11, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x41, 0x31, 0x41, 0x31, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, + 0x60, 0x31, 0x60, 0x31, 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x4A, 0x31, 0x4A, 0x31, 0x01, 0x10, + 0x64, 0x00, 0x44, 0x00, 0x47, 0x31, 0x47, 0x31, 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x37, 0x31, + 0x38, 0x31, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x39, 0x31, 0x39, 0x31, 0x01, 0x10, 0x67, 0x00, + 0x47, 0x00, 0x4E, 0x31, 0x4E, 0x31, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x57, 0x31, 0x57, 0x31, + 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x51, 0x31, 0x51, 0x31, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, + 0x53, 0x31, 0x53, 0x31, 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x4F, 0x31, 0x4F, 0x31, 0x01, 0x10, + 0x6C, 0x00, 0x4C, 0x00, 0x63, 0x31, 0x63, 0x31, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x61, 0x31, + 0x61, 0x31, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x5C, 0x31, 0x5C, 0x31, 0x01, 0x10, 0x6F, 0x00, + 0x4F, 0x00, 0x50, 0x31, 0x52, 0x31, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x54, 0x31, 0x56, 0x31, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x42, 0x31, 0x43, 0x31, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, + 0x31, 0x31, 0x32, 0x31, 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x34, 0x31, 0x34, 0x31, 0x01, 0x10, + 0x74, 0x00, 0x54, 0x00, 0x45, 0x31, 0x46, 0x31, 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x55, 0x31, + 0x55, 0x31, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x4D, 0x31, 0x4D, 0x31, 0x01, 0x10, 0x77, 0x00, + 0x57, 0x00, 0x48, 0x31, 0x49, 0x31, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x4C, 0x31, 0x4C, 0x31, + 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x5B, 0x31, 0x5B, 0x31, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, + 0x4B, 0x31, 0x4B, 0x31, 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x31, 0x00, 0x21, 0x00, 0x00, 0x10, + 0x32, 0x00, 0x40, 0x00, 0x32, 0x00, 0x40, 0x00, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0x33, 0x00, + 0x23, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x34, 0x00, 0x24, 0x00, 0x00, 0x10, 0x35, 0x00, + 0x25, 0x00, 0x35, 0x00, 0x25, 0x00, 0x00, 0x10, 0x36, 0x00, 0x5E, 0x00, 0x36, 0x00, 0x5E, 0x00, + 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0x37, 0x00, 0x26, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00, + 0x38, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x39, 0x00, 0x28, 0x00, 0x39, 0x00, 0x28, 0x00, 0x00, 0x10, + 0x30, 0x00, 0x29, 0x00, 0x30, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, + 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0x3D, 0x00, 0x2B, 0x00, 0x00, 0x10, + 0x5B, 0x00, 0x7B, 0x00, 0x5B, 0x00, 0x7B, 0x00, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x5D, 0x00, + 0x7D, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x5C, 0x00, + 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3A, 0x00, + 0x00, 0x10, 0x27, 0x00, 0x22, 0x00, 0x27, 0x00, 0x22, 0x00, 0x00, 0x10, 0x60, 0x00, 0x7E, 0x00, + 0x60, 0x00, 0x7E, 0x00, 0x00, 0x10, 0x2C, 0x00, 0x3C, 0x00, 0x2C, 0x00, 0x3C, 0x00, 0x00, 0x10, + 0x2E, 0x00, 0x3E, 0x00, 0x2E, 0x00, 0x3E, 0x00, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x2F, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, + 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, + 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, + 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20, + 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00, + 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapRussian = { + 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x10, 0x61, 0x00, 0x41, 0x00, 0x44, 0x04, 0x24, 0x04, 0x11, 0x10, 0x62, 0x00, 0x42, 0x00, + 0x38, 0x04, 0x18, 0x04, 0x11, 0x10, 0x63, 0x00, 0x43, 0x00, 0x41, 0x04, 0x21, 0x04, 0x11, 0x10, + 0x64, 0x00, 0x44, 0x00, 0x32, 0x04, 0x12, 0x04, 0x11, 0x10, 0x65, 0x00, 0x45, 0x00, 0x43, 0x04, + 0x23, 0x04, 0x11, 0x10, 0x66, 0x00, 0x46, 0x00, 0x30, 0x04, 0x10, 0x04, 0x11, 0x10, 0x67, 0x00, + 0x47, 0x00, 0x3F, 0x04, 0x1F, 0x04, 0x11, 0x10, 0x68, 0x00, 0x48, 0x00, 0x40, 0x04, 0x20, 0x04, + 0x11, 0x10, 0x69, 0x00, 0x49, 0x00, 0x48, 0x04, 0x28, 0x04, 0x11, 0x10, 0x6A, 0x00, 0x4A, 0x00, + 0x3E, 0x04, 0x1E, 0x04, 0x11, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x3B, 0x04, 0x1B, 0x04, 0x11, 0x10, + 0x6C, 0x00, 0x4C, 0x00, 0x34, 0x04, 0x14, 0x04, 0x11, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x4C, 0x04, + 0x2C, 0x04, 0x11, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x42, 0x04, 0x22, 0x04, 0x11, 0x10, 0x6F, 0x00, + 0x4F, 0x00, 0x49, 0x04, 0x29, 0x04, 0x11, 0x10, 0x70, 0x00, 0x50, 0x00, 0x37, 0x04, 0x17, 0x04, + 0x11, 0x10, 0x71, 0x00, 0x51, 0x00, 0x39, 0x04, 0x19, 0x04, 0x11, 0x10, 0x72, 0x00, 0x52, 0x00, + 0x3A, 0x04, 0x1A, 0x04, 0x11, 0x10, 0x73, 0x00, 0x53, 0x00, 0x4B, 0x04, 0x2B, 0x04, 0x11, 0x10, + 0x74, 0x00, 0x54, 0x00, 0x35, 0x04, 0x15, 0x04, 0x11, 0x10, 0x75, 0x00, 0x55, 0x00, 0x33, 0x04, + 0x13, 0x04, 0x11, 0x10, 0x76, 0x00, 0x56, 0x00, 0x3C, 0x04, 0x1C, 0x04, 0x11, 0x10, 0x77, 0x00, + 0x57, 0x00, 0x46, 0x04, 0x26, 0x04, 0x11, 0x10, 0x78, 0x00, 0x58, 0x00, 0x47, 0x04, 0x27, 0x04, + 0x11, 0x10, 0x79, 0x00, 0x59, 0x00, 0x3D, 0x04, 0x1D, 0x04, 0x11, 0x10, 0x7A, 0x00, 0x5A, 0x00, + 0x4F, 0x04, 0x2F, 0x04, 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x31, 0x00, 0x21, 0x00, 0x00, 0x10, + 0x32, 0x00, 0x40, 0x00, 0x32, 0x00, 0x22, 0x00, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0x33, 0x00, + 0x16, 0x21, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x34, 0x00, 0x3B, 0x00, 0x00, 0x10, 0x35, 0x00, + 0x25, 0x00, 0x35, 0x00, 0x25, 0x00, 0x00, 0x10, 0x36, 0x00, 0x5E, 0x00, 0x36, 0x00, 0x3A, 0x00, + 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0x37, 0x00, 0x3F, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00, + 0x38, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x39, 0x00, 0x28, 0x00, 0x39, 0x00, 0x28, 0x00, 0x00, 0x10, + 0x30, 0x00, 0x29, 0x00, 0x30, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, + 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0x3D, 0x00, 0x2B, 0x00, 0x10, 0x10, + 0x5B, 0x00, 0x7B, 0x00, 0x45, 0x04, 0x25, 0x04, 0x10, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x4A, 0x04, + 0x2A, 0x04, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x5C, 0x00, + 0x7C, 0x00, 0x5C, 0x00, 0x2F, 0x00, 0x10, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0x36, 0x04, 0x16, 0x04, + 0x10, 0x10, 0x27, 0x00, 0x22, 0x00, 0x4D, 0x04, 0x2D, 0x04, 0x10, 0x10, 0x60, 0x00, 0x7E, 0x00, + 0x51, 0x04, 0x01, 0x04, 0x10, 0x10, 0x2C, 0x00, 0x3C, 0x00, 0x31, 0x04, 0x11, 0x04, 0x10, 0x10, + 0x2E, 0x00, 0x3E, 0x00, 0x4E, 0x04, 0x2E, 0x04, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x2E, 0x00, + 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, + 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, + 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, + 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20, + 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2C, 0x00, + 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapPortuguese = { + 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0xAC, 0x20, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10, 0x32, 0x00, 0x22, 0x00, 0x40, 0x00, + 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0xA3, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0xA7, 0x00, + 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10, 0x36, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x37, 0x00, 0x2F, 0x00, 0x7B, 0x00, 0x00, 0x10, 0x38, 0x00, 0x28, 0x00, 0x5B, 0x00, + 0x00, 0x10, 0x39, 0x00, 0x29, 0x00, 0x5D, 0x00, 0x00, 0x10, 0x30, 0x00, 0x3D, 0x00, 0x7D, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x27, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x00, 0x10, 0xAB, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2A, 0x00, 0x08, 0x03, + 0x00, 0x10, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x03, 0x03, 0x02, 0x03, 0x00, 0x00, + 0x00, 0x10, 0x03, 0x03, 0x02, 0x03, 0x00, 0x00, 0x01, 0x10, 0xE7, 0x00, 0xC7, 0x00, 0x00, 0x00, + 0x00, 0x10, 0xBA, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2C, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, + 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapItalian = { + 0x01, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, + 0x64, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0xAC, 0x20, + 0x00, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x67, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, + 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6F, 0x00, + 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, + 0x74, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x77, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x32, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x33, 0x00, 0xA3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x35, 0x00, + 0x25, 0x00, 0xAC, 0x20, 0x00, 0x00, 0x00, 0x10, 0x36, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x37, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x39, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x30, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x27, 0x00, 0x3F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xEC, 0x00, 0x5E, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x10, + 0xE8, 0x00, 0xE9, 0x00, 0x5B, 0x00, 0x7B, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2A, 0x00, 0x5D, 0x00, + 0x7D, 0x00, 0x00, 0x10, 0xF9, 0x00, 0xA7, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0xF9, 0x00, + 0xA7, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0xF2, 0x00, 0xE7, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x10, 0xE0, 0x00, 0xB0, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2C, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x2E, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, + 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, + 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, + 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20, + 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2C, 0x00, + 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapGerman = { + 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0xAC, 0x20, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0xB5, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x40, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x31, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x10, 0x32, 0x00, 0x22, 0x00, 0xB2, 0x00, + 0x01, 0x10, 0x33, 0x00, 0xA7, 0x00, 0xB3, 0x00, 0x01, 0x10, 0x34, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x35, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x10, 0x36, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x37, 0x00, 0x2F, 0x00, 0x7B, 0x00, 0x01, 0x10, 0x38, 0x00, 0x28, 0x00, 0x5B, 0x00, + 0x01, 0x10, 0x39, 0x00, 0x29, 0x00, 0x5D, 0x00, 0x01, 0x10, 0x30, 0x00, 0x3D, 0x00, 0x7D, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x01, 0x10, 0xDF, 0x00, 0x3F, 0x00, 0x5C, 0x00, + 0x00, 0x10, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x01, 0x10, 0xFC, 0x00, 0xDC, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x2B, 0x00, 0x2A, 0x00, 0x7E, 0x00, 0x01, 0x10, 0x23, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x23, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x10, 0xF6, 0x00, 0xD6, 0x00, 0x00, 0x00, + 0x01, 0x10, 0xE4, 0x00, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x03, 0xB0, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x2C, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x2E, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, + 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapSpanishLatin = { + 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x00, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x40, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10, 0x32, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10, 0x36, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x37, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x39, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x27, 0x00, 0x3F, 0x00, 0x5C, 0x00, + 0x00, 0x10, 0xBF, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x03, 0x08, 0x03, 0x00, 0x00, + 0x00, 0x10, 0x2B, 0x00, 0x2A, 0x00, 0x7E, 0x00, 0x00, 0x10, 0x7D, 0x00, 0x5D, 0x00, 0x00, 0x03, + 0x00, 0x10, 0x7D, 0x00, 0x5D, 0x00, 0x00, 0x03, 0x01, 0x10, 0xF1, 0x00, 0xD1, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x7B, 0x00, 0x5B, 0x00, 0x02, 0x03, 0x00, 0x10, 0x7C, 0x00, 0xB0, 0x00, 0xAC, 0x00, + 0x00, 0x10, 0x2C, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, + 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapSpanish = { + 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0xAC, 0x20, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x7C, 0x00, 0x00, 0x10, 0x32, 0x00, 0x22, 0x00, 0x40, 0x00, + 0x00, 0x10, 0x33, 0x00, 0xB7, 0x00, 0x23, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x03, 0x03, + 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0xAC, 0x20, 0x00, 0x10, 0x36, 0x00, 0x26, 0x00, 0xAC, 0x00, + 0x00, 0x10, 0x37, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x39, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x27, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x00, 0x10, 0xA1, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x02, 0x03, 0x5B, 0x00, + 0x00, 0x10, 0x2B, 0x00, 0x2A, 0x00, 0x5D, 0x00, 0x01, 0x10, 0xE7, 0x00, 0xC7, 0x00, 0x7D, 0x00, + 0x01, 0x10, 0xE7, 0x00, 0xC7, 0x00, 0x7D, 0x00, 0x01, 0x10, 0xF1, 0x00, 0xD1, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x01, 0x03, 0x08, 0x03, 0x7B, 0x00, 0x00, 0x10, 0xBA, 0x00, 0xAA, 0x00, 0x5C, 0x00, + 0x00, 0x10, 0x2C, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, + 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapFrenchCa = { + 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x00, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0xB5, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0xA7, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0xB6, 0x00, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0xB1, 0x00, 0x00, 0x10, 0x32, 0x00, 0x22, 0x00, 0x40, 0x00, + 0x00, 0x10, 0x33, 0x00, 0x2F, 0x00, 0xA3, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0xA2, 0x00, + 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0xA4, 0x00, 0x00, 0x10, 0x36, 0x00, 0x3F, 0x00, 0xAC, 0x00, + 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0xA6, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00, 0xB2, 0x00, + 0x00, 0x10, 0x39, 0x00, 0x28, 0x00, 0xB3, 0x00, 0x00, 0x10, 0x30, 0x00, 0x29, 0x00, 0xBC, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, 0xBD, 0x00, + 0x00, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0xBE, 0x00, 0x00, 0x10, 0x02, 0x03, 0x02, 0x03, 0x5B, 0x00, + 0x00, 0x10, 0x27, 0x03, 0x08, 0x03, 0x5D, 0x00, 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x7D, 0x00, + 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x7D, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0x7E, 0x00, + 0x00, 0x10, 0x00, 0x03, 0x00, 0x03, 0x7B, 0x00, 0x00, 0x10, 0x23, 0x00, 0x7C, 0x00, 0x5C, 0x00, + 0x00, 0x10, 0x2C, 0x00, 0x27, 0x00, 0xAF, 0x00, 0x00, 0x10, 0x2E, 0x00, 0x2E, 0x00, 0x2D, 0x00, + 0x01, 0x10, 0xE9, 0x00, 0xC9, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, + 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x00, 0x10, 0xAB, 0x00, 0xBB, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapFrench = { + 0x01, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0xAC, 0x20, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x2C, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x26, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE9, 0x00, 0x32, 0x00, 0x03, 0x03, + 0x01, 0x10, 0x22, 0x00, 0x33, 0x00, 0x23, 0x00, 0x01, 0x10, 0x27, 0x00, 0x34, 0x00, 0x7B, 0x00, + 0x01, 0x10, 0x28, 0x00, 0x35, 0x00, 0x5B, 0x00, 0x01, 0x10, 0x2D, 0x00, 0x36, 0x00, 0x7C, 0x00, + 0x01, 0x10, 0xE8, 0x00, 0x37, 0x00, 0x00, 0x03, 0x01, 0x10, 0x5F, 0x00, 0x38, 0x00, 0x5C, 0x00, + 0x01, 0x10, 0xE7, 0x00, 0x39, 0x00, 0x5E, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x30, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x01, 0x10, 0x29, 0x00, 0xB0, 0x00, 0x5D, 0x00, + 0x01, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0x7D, 0x00, 0x01, 0x10, 0x02, 0x03, 0x08, 0x03, 0x00, 0x00, + 0x01, 0x10, 0x24, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0x01, 0x10, 0x2A, 0x00, 0xB5, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x2A, 0x00, 0xB5, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00, + 0x01, 0x10, 0xF9, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x3B, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x01, 0x10, 0x3A, 0x00, 0x2F, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x21, 0x00, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, + 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x3C, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapEnglishUk = { + 0x01, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x10, 0x61, 0x00, 0x41, 0x00, 0xE1, 0x00, 0xC1, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, + 0x64, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x65, 0x00, 0x45, 0x00, 0xE9, 0x00, + 0xC9, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x67, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x10, 0x69, 0x00, 0x49, 0x00, 0xED, 0x00, 0xCD, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, + 0x6C, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x6F, 0x00, + 0x4F, 0x00, 0xF3, 0x00, 0xD3, 0x00, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, + 0x74, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x75, 0x00, 0x55, 0x00, 0xFA, 0x00, + 0xDA, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x77, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x7A, 0x00, 0x5A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x32, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x33, 0x00, 0xA3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0xAC, 0x20, 0x00, 0x00, 0x00, 0x10, 0x35, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x36, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x39, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x30, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x5B, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x23, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x23, 0x00, + 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x27, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x60, 0x00, 0xAC, 0x00, + 0xA6, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2C, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x2E, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, + 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, + 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, + 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20, + 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00, + 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapJapanese = { + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x10, 0x61, 0x00, 0x41, 0x00, 0x61, 0x30, 0x61, 0x30, 0xC1, 0x30, 0xC1, 0x30, 0x01, 0x10, + 0x62, 0x00, 0x42, 0x00, 0x53, 0x30, 0x53, 0x30, 0xB3, 0x30, 0xB3, 0x30, 0x01, 0x10, 0x63, 0x00, + 0x43, 0x00, 0x5D, 0x30, 0x5D, 0x30, 0xBD, 0x30, 0xBD, 0x30, 0x01, 0x10, 0x64, 0x00, 0x44, 0x00, + 0x57, 0x30, 0x57, 0x30, 0xB7, 0x30, 0xB7, 0x30, 0x01, 0x10, 0x65, 0x00, 0x45, 0x00, 0x44, 0x30, + 0x43, 0x30, 0xA4, 0x30, 0xA3, 0x30, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x6F, 0x30, 0x6F, 0x30, + 0xCF, 0x30, 0xCF, 0x30, 0x01, 0x10, 0x67, 0x00, 0x47, 0x00, 0x4D, 0x30, 0x4D, 0x30, 0xAD, 0x30, + 0xAD, 0x30, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x4F, 0x30, 0x4F, 0x30, 0xAF, 0x30, 0xAF, 0x30, + 0x01, 0x10, 0x69, 0x00, 0x49, 0x00, 0x6B, 0x30, 0x6B, 0x30, 0xCB, 0x30, 0xCB, 0x30, 0x01, 0x10, + 0x6A, 0x00, 0x4A, 0x00, 0x7E, 0x30, 0x7E, 0x30, 0xDE, 0x30, 0xDE, 0x30, 0x01, 0x10, 0x6B, 0x00, + 0x4B, 0x00, 0x6E, 0x30, 0x6E, 0x30, 0xCE, 0x30, 0xCE, 0x30, 0x01, 0x10, 0x6C, 0x00, 0x4C, 0x00, + 0x8A, 0x30, 0x8A, 0x30, 0xEA, 0x30, 0xEA, 0x30, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0x82, 0x30, + 0x82, 0x30, 0xE2, 0x30, 0xE2, 0x30, 0x01, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0x7F, 0x30, 0x7F, 0x30, + 0xDF, 0x30, 0xDF, 0x30, 0x01, 0x10, 0x6F, 0x00, 0x4F, 0x00, 0x89, 0x30, 0x89, 0x30, 0xE9, 0x30, + 0xE9, 0x30, 0x01, 0x10, 0x70, 0x00, 0x50, 0x00, 0x5B, 0x30, 0x5B, 0x30, 0xBB, 0x30, 0xBB, 0x30, + 0x01, 0x10, 0x71, 0x00, 0x51, 0x00, 0x5F, 0x30, 0x5F, 0x30, 0xBF, 0x30, 0xBF, 0x30, 0x01, 0x10, + 0x72, 0x00, 0x52, 0x00, 0x59, 0x30, 0x59, 0x30, 0xB9, 0x30, 0xB9, 0x30, 0x01, 0x10, 0x73, 0x00, + 0x53, 0x00, 0x68, 0x30, 0x68, 0x30, 0xC8, 0x30, 0xC8, 0x30, 0x01, 0x10, 0x74, 0x00, 0x54, 0x00, + 0x4B, 0x30, 0x4B, 0x30, 0xAB, 0x30, 0xAB, 0x30, 0x01, 0x10, 0x75, 0x00, 0x55, 0x00, 0x6A, 0x30, + 0x6A, 0x30, 0xCA, 0x30, 0xCA, 0x30, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x72, 0x30, 0x72, 0x30, + 0xD2, 0x30, 0xD2, 0x30, 0x01, 0x10, 0x77, 0x00, 0x57, 0x00, 0x66, 0x30, 0x66, 0x30, 0xC6, 0x30, + 0xC6, 0x30, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x55, 0x30, 0x55, 0x30, 0xB5, 0x30, 0xB5, 0x30, + 0x01, 0x10, 0x79, 0x00, 0x59, 0x00, 0x93, 0x30, 0x93, 0x30, 0xF3, 0x30, 0xF3, 0x30, 0x01, 0x10, + 0x7A, 0x00, 0x5A, 0x00, 0x64, 0x30, 0x63, 0x30, 0xC4, 0x30, 0xC3, 0x30, 0x00, 0x10, 0x31, 0x00, + 0x21, 0x00, 0x6C, 0x30, 0x6C, 0x30, 0xCC, 0x30, 0xCC, 0x30, 0x00, 0x10, 0x32, 0x00, 0x22, 0x00, + 0x75, 0x30, 0x75, 0x30, 0xD5, 0x30, 0xD5, 0x30, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0x42, 0x30, + 0x41, 0x30, 0xA2, 0x30, 0xA1, 0x30, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0x46, 0x30, 0x45, 0x30, + 0xA6, 0x30, 0xA5, 0x30, 0x00, 0x10, 0x35, 0x00, 0x25, 0x00, 0x48, 0x30, 0x47, 0x30, 0xA8, 0x30, + 0xA7, 0x30, 0x00, 0x10, 0x36, 0x00, 0x26, 0x00, 0x4A, 0x30, 0x49, 0x30, 0xAA, 0x30, 0xA9, 0x30, + 0x00, 0x10, 0x37, 0x00, 0x27, 0x00, 0x84, 0x30, 0x83, 0x30, 0xE4, 0x30, 0xE3, 0x30, 0x00, 0x10, + 0x38, 0x00, 0x28, 0x00, 0x86, 0x30, 0x85, 0x30, 0xE6, 0x30, 0xE5, 0x30, 0x00, 0x10, 0x39, 0x00, + 0x29, 0x00, 0x88, 0x30, 0x87, 0x30, 0xE8, 0x30, 0xE7, 0x30, 0x00, 0x10, 0x30, 0x00, 0x00, 0x00, + 0x8F, 0x30, 0x92, 0x30, 0xEF, 0x30, 0xF2, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, + 0x2D, 0x00, 0x3D, 0x00, 0x7B, 0x30, 0x7B, 0x30, 0xDB, 0x30, 0xDB, 0x30, 0x00, 0x10, 0x5E, 0x00, + 0x7E, 0x00, 0x78, 0x30, 0x78, 0x30, 0xD8, 0x30, 0xD8, 0x30, 0x00, 0x10, 0x40, 0x00, 0x60, 0x00, + 0x9E, 0xFF, 0x9E, 0xFF, 0x9E, 0xFF, 0x9E, 0xFF, 0x00, 0x10, 0x5B, 0x00, 0x7B, 0x00, 0x9F, 0xFF, + 0x62, 0xFF, 0x9F, 0xFF, 0x62, 0xFF, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x80, 0x30, 0x63, 0xFF, + 0xE0, 0x30, 0x63, 0xFF, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0x80, 0x30, 0x63, 0xFF, 0xE0, 0x30, + 0x63, 0xFF, 0x00, 0x10, 0x3B, 0x00, 0x2B, 0x00, 0x8C, 0x30, 0x8C, 0x30, 0xEC, 0x30, 0xEC, 0x30, + 0x00, 0x10, 0x3A, 0x00, 0x2A, 0x00, 0x51, 0x30, 0x51, 0x30, 0xB1, 0x30, 0xB1, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2C, 0x00, + 0x3C, 0x00, 0x6D, 0x30, 0x64, 0xFF, 0xCD, 0x30, 0x64, 0xFF, 0x00, 0x10, 0x2E, 0x00, 0x3E, 0x00, + 0x8B, 0x30, 0x61, 0xFF, 0xEB, 0x30, 0x61, 0xFF, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0x81, 0x30, + 0x65, 0xFF, 0xE1, 0x30, 0x65, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, + 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, + 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, 0x2B, 0x00, 0x2B, 0x00, + 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, + 0x20, 0x00, 0x35, 0x00, 0x20, 0x00, 0x35, 0x00, 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x37, 0x00, + 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, + 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x3D, 0x00, + 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x5F, 0x00, + 0x8D, 0x30, 0x8D, 0x30, 0xED, 0x30, 0xED, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x70, 0xFF, 0x70, 0xFF, + 0x70, 0xFF, 0x70, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +constexpr KeyCodeMap KeyCodeMapEnglishUsInternational = { + 0x01, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x10, 0x61, 0x00, 0x41, 0x00, 0xE1, 0x00, 0xC1, 0x00, 0x01, 0x10, 0x62, 0x00, 0x42, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x63, 0x00, 0x43, 0x00, 0xA9, 0x00, 0xA2, 0x00, 0x03, 0x10, + 0x64, 0x00, 0x44, 0x00, 0xF0, 0x00, 0xD0, 0x00, 0x03, 0x10, 0x65, 0x00, 0x45, 0x00, 0xE9, 0x00, + 0xC9, 0x00, 0x01, 0x10, 0x66, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x67, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x10, 0x69, 0x00, 0x49, 0x00, 0xED, 0x00, 0xCD, 0x00, 0x01, 0x10, 0x6A, 0x00, 0x4A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x6B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, + 0x6C, 0x00, 0x4C, 0x00, 0xF8, 0x00, 0xD8, 0x00, 0x01, 0x10, 0x6D, 0x00, 0x4D, 0x00, 0xB5, 0x00, + 0x00, 0x00, 0x03, 0x10, 0x6E, 0x00, 0x4E, 0x00, 0xF1, 0x00, 0xD1, 0x00, 0x03, 0x10, 0x6F, 0x00, + 0x4F, 0x00, 0xF3, 0x00, 0xD3, 0x00, 0x03, 0x10, 0x70, 0x00, 0x50, 0x00, 0xF6, 0x00, 0xD6, 0x00, + 0x03, 0x10, 0x71, 0x00, 0x51, 0x00, 0xE4, 0x00, 0xC4, 0x00, 0x01, 0x10, 0x72, 0x00, 0x52, 0x00, + 0xAE, 0x00, 0x00, 0x00, 0x01, 0x10, 0x73, 0x00, 0x53, 0x00, 0xDF, 0x00, 0xA7, 0x00, 0x03, 0x10, + 0x74, 0x00, 0x54, 0x00, 0xFE, 0x00, 0xDE, 0x00, 0x03, 0x10, 0x75, 0x00, 0x55, 0x00, 0xFA, 0x00, + 0xDA, 0x00, 0x01, 0x10, 0x76, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x77, 0x00, + 0x57, 0x00, 0xE5, 0x00, 0xC5, 0x00, 0x01, 0x10, 0x78, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x10, 0x79, 0x00, 0x59, 0x00, 0xFC, 0x00, 0xDC, 0x00, 0x03, 0x10, 0x7A, 0x00, 0x5A, 0x00, + 0xE6, 0x00, 0xC6, 0x00, 0x00, 0x10, 0x31, 0x00, 0x21, 0x00, 0xA1, 0x00, 0xB9, 0x00, 0x00, 0x10, + 0x32, 0x00, 0x40, 0x00, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x10, 0x33, 0x00, 0x23, 0x00, 0xB3, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x34, 0x00, 0x24, 0x00, 0xA4, 0x00, 0xA3, 0x00, 0x00, 0x10, 0x35, 0x00, + 0x25, 0x00, 0xAC, 0x20, 0x00, 0x00, 0x00, 0x10, 0x36, 0x00, 0x02, 0x03, 0xBC, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x37, 0x00, 0x26, 0x00, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x00, 0x2A, 0x00, + 0xBE, 0x00, 0x00, 0x00, 0x00, 0x10, 0x39, 0x00, 0x28, 0x00, 0x18, 0x20, 0x00, 0x00, 0x00, 0x10, + 0x30, 0x00, 0x29, 0x00, 0x19, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x5F, 0x00, + 0xA5, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3D, 0x00, 0x2B, 0x00, 0xD7, 0x00, 0xF7, 0x00, 0x00, 0x10, + 0x5B, 0x00, 0x7B, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5D, 0x00, 0x7D, 0x00, 0xBB, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0xAC, 0x00, 0xA6, 0x00, 0x00, 0x10, 0x5C, 0x00, + 0x7C, 0x00, 0xAC, 0x00, 0xA6, 0x00, 0x00, 0x10, 0x3B, 0x00, 0x3A, 0x00, 0xB6, 0x00, 0xB0, 0x00, + 0x00, 0x10, 0x0D, 0x03, 0x0E, 0x03, 0xB4, 0x00, 0xA8, 0x00, 0x00, 0x10, 0x00, 0x03, 0x03, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x2C, 0x00, 0x3C, 0x00, 0xE7, 0x00, 0xC7, 0x00, 0x00, 0x10, + 0x2E, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2F, 0x00, 0x3F, 0x00, 0xBF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x00, 0x10, 0x2A, 0x00, 0x2A, 0x00, + 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x10, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x2D, 0x00, 0x00, 0x10, + 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x31, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, + 0xFF, 0x20, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0xFF, 0x20, 0x20, 0x00, 0x35, 0x00, + 0x20, 0x00, 0x35, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0xFF, 0x20, + 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x37, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x38, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x39, 0x00, 0xFF, 0x20, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFF, 0x20, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x2E, 0x00, + 0x00, 0x10, 0x5C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +} // namespace Service::Set diff --git a/src/core/hle/service/set/private_settings.h b/src/core/hle/service/set/private_settings.h deleted file mode 100644 index b02291ce7..000000000 --- a/src/core/hle/service/set/private_settings.h +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> - -#include "common/bit_field.h" -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/uuid.h" -#include "core/hle/service/psc/time/common.h" - -namespace Service::Set { - -/// This is nn::settings::system::InitialLaunchFlag -struct InitialLaunchFlag { - union { - u32 raw{}; - - BitField<0, 1, u32> InitialLaunchCompletionFlag; - BitField<8, 1, u32> InitialLaunchUserAdditionFlag; - BitField<16, 1, u32> InitialLaunchTimestampFlag; - }; -}; -static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size"); - -/// This is nn::settings::system::InitialLaunchSettings -struct InitialLaunchSettings { - InitialLaunchFlag flags; - INSERT_PADDING_BYTES(0x4); - Service::PSC::Time::SteadyClockTimePoint timestamp; -}; -static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size"); - -#pragma pack(push, 4) -struct InitialLaunchSettingsPacked { - InitialLaunchFlag flags; - Service::PSC::Time::SteadyClockTimePoint timestamp; -}; -#pragma pack(pop) -static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C, - "InitialLaunchSettingsPacked is incorrect size"); - -struct PrivateSettings { - std::array<u8, 0x10> reserved_00; - - // nn::settings::system::InitialLaunchSettings - InitialLaunchSettings initial_launch_settings; - - std::array<u8, 0x20> reserved_30; - - Common::UUID external_clock_source_id; - s64 shutdown_rtc_value; - s64 external_steady_clock_internal_offset; - - std::array<u8, 0x60> reserved_70; - - // nn::settings::system::PlatformRegion - std::array<u8, 0x4> platform_region; - - std::array<u8, 0x4> reserved_D4; -}; -static_assert(offsetof(PrivateSettings, initial_launch_settings) == 0x10); -static_assert(offsetof(PrivateSettings, external_clock_source_id) == 0x50); -static_assert(offsetof(PrivateSettings, reserved_70) == 0x70); -static_assert(offsetof(PrivateSettings, platform_region) == 0xD0); -static_assert(sizeof(PrivateSettings) == 0xD8, "PrivateSettings has the wrong size!"); - -PrivateSettings DefaultPrivateSettings(); - -} // namespace Service::Set diff --git a/src/core/hle/service/set/setting_formats/system_settings.h b/src/core/hle/service/set/setting_formats/system_settings.h index 40230182a..a5b1552a5 100644 --- a/src/core/hle/service/set/setting_formats/system_settings.h +++ b/src/core/hle/service/set/setting_formats/system_settings.h @@ -244,7 +244,7 @@ struct SystemSettings { INSERT_PADDING_BYTES(0x60); // Reserved // nn::settings::system::AccountNotificationSettings - u32 account_notification_settings_count; + s32 account_notification_settings_count; INSERT_PADDING_BYTES(0xC); // Reserved std::array<AccountNotificationSettings, 8> account_notification_settings; INSERT_PADDING_BYTES(0x140); // Reserved @@ -308,7 +308,7 @@ struct SystemSettings { INSERT_PADDING_BYTES(0x34); // Reserved // nn::settings::system::EulaVersion - u32 eula_version_count; + s32 eula_version_count; INSERT_PADDING_BYTES(0xC); // Reserved std::array<EulaVersion, 32> eula_versions; INSERT_PADDING_BYTES(0x200); // Reserved diff --git a/src/core/hle/service/set/settings_server.cpp b/src/core/hle/service/set/settings_server.cpp index b2caa00ff..a9321b98d 100644 --- a/src/core/hle/service/set/settings_server.cpp +++ b/src/core/hle/service/set/settings_server.cpp @@ -6,7 +6,9 @@ #include <chrono> #include "common/logging/log.h" #include "common/settings.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/set/key_code_map.h" #include "core/hle/service/set/settings_server.h" namespace Service::Set { @@ -15,43 +17,69 @@ constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF; constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40; constexpr Result ResultInvalidLanguage{ErrorModule::Settings, 625}; - -void PushResponseLanguageCode(HLERequestContext& ctx, std::size_t num_language_codes) { - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(num_language_codes)); -} - -void GetAvailableLanguageCodesImpl(HLERequestContext& ctx, std::size_t max_entries) { - const std::size_t requested_amount = ctx.GetWriteBufferNumElements<LanguageCode>(); - const std::size_t max_amount = std::min(requested_amount, max_entries); - const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount); - const std::size_t copy_size = copy_amount * sizeof(LanguageCode); - - ctx.WriteBuffer(available_language_codes.data(), copy_size); - PushResponseLanguageCode(ctx, copy_amount); -} - -void GetKeyCodeMapImpl(HLERequestContext& ctx) { - const auto language_code = - available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())]; - const auto key_code = - std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), - [=](const auto& element) { return element.first == language_code; }); - KeyboardLayout layout = KeyboardLayout::EnglishUs; - if (key_code == language_to_layout.cend()) { - LOG_ERROR(Service_SET, - "Could not find keyboard layout for language index {}, defaulting to English us", - Settings::values.language_index.GetValue()); - } else { - layout = key_code->second; +constexpr Result ResultNullPointer{ErrorModule::Settings, 1261}; + +Result GetKeyCodeMapImpl(KeyCodeMap& out_key_code_map, KeyboardLayout keyboard_layout, + LanguageCode language_code) { + switch (keyboard_layout) { + case KeyboardLayout::Japanese: + out_key_code_map = KeyCodeMapJapanese; + R_SUCCEED(); + case KeyboardLayout::EnglishUs: + out_key_code_map = KeyCodeMapEnglishUsInternational; + if (language_code == LanguageCode::KO) { + out_key_code_map = KeyCodeMapKorean; + } + if (language_code == LanguageCode::ZH_HANS) { + out_key_code_map = KeyCodeMapChineseSimplified; + } + if (language_code == LanguageCode::ZH_HANT) { + out_key_code_map = KeyCodeMapChineseTraditional; + } + R_SUCCEED(); + case KeyboardLayout::EnglishUk: + out_key_code_map = KeyCodeMapEnglishUk; + R_SUCCEED(); + case KeyboardLayout::French: + out_key_code_map = KeyCodeMapFrench; + R_SUCCEED(); + case KeyboardLayout::FrenchCa: + out_key_code_map = KeyCodeMapFrenchCa; + R_SUCCEED(); + case KeyboardLayout::Spanish: + out_key_code_map = KeyCodeMapSpanish; + R_SUCCEED(); + case KeyboardLayout::SpanishLatin: + out_key_code_map = KeyCodeMapSpanishLatin; + R_SUCCEED(); + case KeyboardLayout::German: + out_key_code_map = KeyCodeMapGerman; + R_SUCCEED(); + case KeyboardLayout::Italian: + out_key_code_map = KeyCodeMapItalian; + R_SUCCEED(); + case KeyboardLayout::Portuguese: + out_key_code_map = KeyCodeMapPortuguese; + R_SUCCEED(); + case KeyboardLayout::Russian: + out_key_code_map = KeyCodeMapRussian; + R_SUCCEED(); + case KeyboardLayout::Korean: + out_key_code_map = KeyCodeMapKorean; + R_SUCCEED(); + case KeyboardLayout::ChineseSimplified: + out_key_code_map = KeyCodeMapChineseSimplified; + R_SUCCEED(); + case KeyboardLayout::ChineseTraditional: + out_key_code_map = KeyCodeMapChineseTraditional; + R_SUCCEED(); + default: + case KeyboardLayout::EnglishUsInternational: + out_key_code_map = KeyCodeMapEnglishUsInternational; + R_SUCCEED(); } - - ctx.WriteBuffer(layout); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); } + } // Anonymous namespace LanguageCode GetLanguageCodeFromIndex(std::size_t index) { @@ -61,18 +89,18 @@ LanguageCode GetLanguageCodeFromIndex(std::size_t index) { ISettingsServer::ISettingsServer(Core::System& system_) : ServiceFramework{system_, "set"} { // clang-format off static const FunctionInfo functions[] = { - {0, &ISettingsServer::GetLanguageCode, "GetLanguageCode"}, - {1, &ISettingsServer::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, - {2, &ISettingsServer::MakeLanguageCode, "MakeLanguageCode"}, - {3, &ISettingsServer::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"}, - {4, &ISettingsServer::GetRegionCode, "GetRegionCode"}, - {5, &ISettingsServer::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, - {6, &ISettingsServer::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, - {7, &ISettingsServer::GetKeyCodeMap, "GetKeyCodeMap"}, - {8, &ISettingsServer::GetQuestFlag, "GetQuestFlag"}, - {9, &ISettingsServer::GetKeyCodeMap2, "GetKeyCodeMap2"}, + {0, C<&ISettingsServer::GetLanguageCode>, "GetLanguageCode"}, + {1, C<&ISettingsServer::GetAvailableLanguageCodes>, "GetAvailableLanguageCodes"}, + {2, C<&ISettingsServer::MakeLanguageCode>, "MakeLanguageCode"}, + {3, C<&ISettingsServer::GetAvailableLanguageCodeCount>, "GetAvailableLanguageCodeCount"}, + {4, C<&ISettingsServer::GetRegionCode>, "GetRegionCode"}, + {5, C<&ISettingsServer::GetAvailableLanguageCodes2>, "GetAvailableLanguageCodes2"}, + {6, C<&ISettingsServer::GetAvailableLanguageCodeCount2>, "GetAvailableLanguageCodeCount2"}, + {7, C<&ISettingsServer::GetKeyCodeMap>, "GetKeyCodeMap"}, + {8, C<&ISettingsServer::GetQuestFlag>, "GetQuestFlag"}, + {9, C<&ISettingsServer::GetKeyCodeMap2>, "GetKeyCodeMap2"}, {10, nullptr, "GetFirmwareVersionForDebug"}, - {11, &ISettingsServer::GetDeviceNickName, "GetDeviceNickName"}, + {11, C<&ISettingsServer::GetDeviceNickName>, "GetDeviceNickName"}, }; // clang-format on @@ -81,86 +109,134 @@ ISettingsServer::ISettingsServer(Core::System& system_) : ServiceFramework{syste ISettingsServer::~ISettingsServer() = default; -void ISettingsServer::GetAvailableLanguageCodes(HLERequestContext& ctx) { +Result ISettingsServer::GetLanguageCode(Out<LanguageCode> out_language_code) { + LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue()); + + *out_language_code = available_language_codes[static_cast<std::size_t>( + Settings::values.language_index.GetValue())]; + R_SUCCEED(); +} + +Result ISettingsServer::GetAvailableLanguageCodes( + Out<s32> out_count, OutArray<LanguageCode, BufferAttr_HipcPointer> out_language_codes) { LOG_DEBUG(Service_SET, "called"); - GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES); + const std::size_t max_amount = std::min(PRE_4_0_0_MAX_ENTRIES, out_language_codes.size()); + *out_count = static_cast<s32>(std::min(available_language_codes.size(), max_amount)); + + memcpy(out_language_codes.data(), available_language_codes.data(), + static_cast<std::size_t>(*out_count) * sizeof(LanguageCode)); + + R_SUCCEED(); } -void ISettingsServer::MakeLanguageCode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto index = rp.Pop<u32>(); +Result ISettingsServer::MakeLanguageCode(Out<LanguageCode> out_language_code, Language language) { + LOG_DEBUG(Service_SET, "called, language={}", language); - if (index >= available_language_codes.size()) { - LOG_ERROR(Service_SET, "Invalid language code index! index={}", index); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Set::ResultInvalidLanguage); - return; - } + const auto index = static_cast<std::size_t>(language); + R_UNLESS(index < available_language_codes.size(), Set::ResultInvalidLanguage); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(available_language_codes[index]); + *out_language_code = available_language_codes[index]; + R_SUCCEED(); } -void ISettingsServer::GetAvailableLanguageCodes2(HLERequestContext& ctx) { +Result ISettingsServer::GetAvailableLanguageCodeCount(Out<s32> out_count) { LOG_DEBUG(Service_SET, "called"); - GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES); + *out_count = PRE_4_0_0_MAX_ENTRIES; + R_SUCCEED(); } -void ISettingsServer::GetAvailableLanguageCodeCount(HLERequestContext& ctx) { +Result ISettingsServer::GetRegionCode(Out<SystemRegionCode> out_region_code) { LOG_DEBUG(Service_SET, "called"); - PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES); + *out_region_code = static_cast<SystemRegionCode>(Settings::values.region_index.GetValue()); + R_SUCCEED(); } -void ISettingsServer::GetAvailableLanguageCodeCount2(HLERequestContext& ctx) { +Result ISettingsServer::GetAvailableLanguageCodes2( + Out<s32> out_count, OutArray<LanguageCode, BufferAttr_HipcMapAlias> language_codes) { LOG_DEBUG(Service_SET, "called"); - PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES); + const std::size_t max_amount = std::min(POST_4_0_0_MAX_ENTRIES, language_codes.size()); + *out_count = static_cast<s32>(std::min(available_language_codes.size(), max_amount)); + + memcpy(language_codes.data(), available_language_codes.data(), + static_cast<std::size_t>(*out_count) * sizeof(LanguageCode)); + + R_SUCCEED(); } -void ISettingsServer::GetQuestFlag(HLERequestContext& ctx) { +Result ISettingsServer::GetAvailableLanguageCodeCount2(Out<s32> out_count) { LOG_DEBUG(Service_SET, "called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<s32>(Settings::values.quest_flag.GetValue())); + *out_count = POST_4_0_0_MAX_ENTRIES; + R_SUCCEED(); } -void ISettingsServer::GetLanguageCode(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue()); +Result ISettingsServer::GetKeyCodeMap( + OutLargeData<KeyCodeMap, BufferAttr_HipcMapAlias> out_key_code_map) { + LOG_DEBUG(Service_SET, "called"); + + R_UNLESS(out_key_code_map != nullptr, ResultNullPointer); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum( - available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())]); + const auto language_code = + available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())]; + const auto key_code = + std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), + [=](const auto& element) { return element.first == language_code; }); + + if (key_code == language_to_layout.cend()) { + LOG_ERROR(Service_SET, + "Could not find keyboard layout for language index {}, defaulting to English us", + Settings::values.language_index.GetValue()); + *out_key_code_map = KeyCodeMapEnglishUsInternational; + R_SUCCEED(); + } + + R_RETURN(GetKeyCodeMapImpl(*out_key_code_map, key_code->second, key_code->first)); } -void ISettingsServer::GetRegionCode(HLERequestContext& ctx) { +Result ISettingsServer::GetQuestFlag(Out<bool> out_quest_flag) { LOG_DEBUG(Service_SET, "called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(Settings::values.region_index.GetValue())); + *out_quest_flag = Settings::values.quest_flag.GetValue(); + R_SUCCEED(); } -void ISettingsServer::GetKeyCodeMap(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); - GetKeyCodeMapImpl(ctx); -} +Result ISettingsServer::GetKeyCodeMap2( + OutLargeData<KeyCodeMap, BufferAttr_HipcMapAlias> out_key_code_map) { + LOG_DEBUG(Service_SET, "called"); + + R_UNLESS(out_key_code_map != nullptr, ResultNullPointer); -void ISettingsServer::GetKeyCodeMap2(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); - GetKeyCodeMapImpl(ctx); + const auto language_code = + available_language_codes[static_cast<s32>(Settings::values.language_index.GetValue())]; + const auto key_code = + std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), + [=](const auto& element) { return element.first == language_code; }); + + if (key_code == language_to_layout.cend()) { + LOG_ERROR(Service_SET, + "Could not find keyboard layout for language index {}, defaulting to English us", + Settings::values.language_index.GetValue()); + *out_key_code_map = KeyCodeMapEnglishUsInternational; + R_SUCCEED(); + } + + R_RETURN(GetKeyCodeMapImpl(*out_key_code_map, key_code->second, key_code->first)); } -void ISettingsServer::GetDeviceNickName(HLERequestContext& ctx) { +Result ISettingsServer::GetDeviceNickName( + OutLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> out_device_name) { LOG_DEBUG(Service_SET, "called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - ctx.WriteBuffer(Settings::values.device_name.GetValue()); + + const std::size_t string_size = + std::min(Settings::values.device_name.GetValue().size(), out_device_name->size()); + + *out_device_name = {}; + memcpy(out_device_name->data(), Settings::values.device_name.GetValue().data(), string_size); + R_SUCCEED(); } } // namespace Service::Set diff --git a/src/core/hle/service/set/settings_server.h b/src/core/hle/service/set/settings_server.h index 8304e8424..a39971fe9 100644 --- a/src/core/hle/service/set/settings_server.h +++ b/src/core/hle/service/set/settings_server.h @@ -3,6 +3,7 @@ #pragma once +#include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" #include "core/hle/service/set/settings_types.h" @@ -11,6 +12,7 @@ class System; } namespace Service::Set { +using KeyCodeMap = std::array<u8, 0x1000>; LanguageCode GetLanguageCodeFromIndex(std::size_t idx); @@ -20,17 +22,30 @@ public: ~ISettingsServer() override; private: - void GetLanguageCode(HLERequestContext& ctx); - void GetAvailableLanguageCodes(HLERequestContext& ctx); - void MakeLanguageCode(HLERequestContext& ctx); - void GetAvailableLanguageCodes2(HLERequestContext& ctx); - void GetAvailableLanguageCodeCount(HLERequestContext& ctx); - void GetAvailableLanguageCodeCount2(HLERequestContext& ctx); - void GetQuestFlag(HLERequestContext& ctx); - void GetRegionCode(HLERequestContext& ctx); - void GetKeyCodeMap(HLERequestContext& ctx); - void GetKeyCodeMap2(HLERequestContext& ctx); - void GetDeviceNickName(HLERequestContext& ctx); + Result GetLanguageCode(Out<LanguageCode> out_language_code); + + Result GetAvailableLanguageCodes(Out<s32> out_count, + OutArray<LanguageCode, BufferAttr_HipcPointer> language_codes); + + Result MakeLanguageCode(Out<LanguageCode> out_language_code, Language language); + + Result GetAvailableLanguageCodeCount(Out<s32> out_count); + + Result GetRegionCode(Out<SystemRegionCode> out_region_code); + + Result GetAvailableLanguageCodes2( + Out<s32> out_count, OutArray<LanguageCode, BufferAttr_HipcMapAlias> language_codes); + + Result GetAvailableLanguageCodeCount2(Out<s32> out_count); + + Result GetKeyCodeMap(OutLargeData<KeyCodeMap, BufferAttr_HipcMapAlias> out_key_code_map); + + Result GetQuestFlag(Out<bool> out_quest_flag); + + Result GetKeyCodeMap2(OutLargeData<KeyCodeMap, BufferAttr_HipcMapAlias> out_key_code_map); + + Result GetDeviceNickName( + OutLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> out_device_name); }; } // namespace Service::Set diff --git a/src/core/hle/service/set/settings_types.h b/src/core/hle/service/set/settings_types.h index ceb85b82a..29664e88c 100644 --- a/src/core/hle/service/set/settings_types.h +++ b/src/core/hle/service/set/settings_types.h @@ -12,6 +12,7 @@ #include "core/hle/service/psc/time/common.h" namespace Service::Set { +using SettingItemName = std::array<u8, 0x48>; /// This is nn::settings::system::AudioOutputMode enum class AudioOutputMode : u32 { @@ -148,6 +149,28 @@ enum class KeyboardLayout : u32 { ChineseTraditional = 14, }; +// This is nn::settings::Language +enum class Language : u32 { + Japanese, + AmericanEnglish, + French, + German, + Italian, + Spanish, + Chinese, + Korean, + Dutch, + Portiguesue, + Russian, + Taiwanese, + BritishEnglish, + CanadianFrench, + LatinAmericanSpanish, + SimplifiedCHhinese, + TraditionalChinese, + BrazilianPortuguese, +}; + /// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64. enum class LanguageCode : u64 { JA = 0x000000000000616A, @@ -391,16 +414,18 @@ struct FirmwareVersionFormat { u8 major; u8 minor; u8 micro; - INSERT_PADDING_BYTES(1); + INSERT_PADDING_BYTES_NOINIT(1); u8 revision_major; u8 revision_minor; - INSERT_PADDING_BYTES(2); + INSERT_PADDING_BYTES_NOINIT(2); std::array<char, 0x20> platform; std::array<u8, 0x40> version_hash; std::array<char, 0x18> display_version; std::array<char, 0x80> display_title; }; static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size"); +static_assert(std::is_trivial_v<FirmwareVersionFormat>, + "FirmwareVersionFormat type must be trivially copyable."); /// This is nn::settings::system::HomeMenuScheme struct HomeMenuScheme { diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp index 7ef4a0ded..93925f783 100644 --- a/src/core/hle/service/set/system_settings_server.cpp +++ b/src/core/hle/service/set/system_settings_server.cpp @@ -17,6 +17,7 @@ #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs.h" #include "core/file_sys/system_archive/system_archive.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/set/settings_server.h" @@ -91,83 +92,83 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) : ServiceFramework{system_, "set:sys"}, m_system{system} { // clang-format off static const FunctionInfo functions[] = { - {0, &ISystemSettingsServer::SetLanguageCode, "SetLanguageCode"}, + {0, C<&ISystemSettingsServer::SetLanguageCode>, "SetLanguageCode"}, {1, nullptr, "SetNetworkSettings"}, {2, nullptr, "GetNetworkSettings"}, - {3, &ISystemSettingsServer::GetFirmwareVersion, "GetFirmwareVersion"}, - {4, &ISystemSettingsServer::GetFirmwareVersion2, "GetFirmwareVersion2"}, + {3, C<&ISystemSettingsServer::GetFirmwareVersion>, "GetFirmwareVersion"}, + {4, C<&ISystemSettingsServer::GetFirmwareVersion2>, "GetFirmwareVersion2"}, {5, nullptr, "GetFirmwareVersionDigest"}, - {7, &ISystemSettingsServer::GetLockScreenFlag, "GetLockScreenFlag"}, - {8, &ISystemSettingsServer::SetLockScreenFlag, "SetLockScreenFlag"}, + {7, C<&ISystemSettingsServer::GetLockScreenFlag>, "GetLockScreenFlag"}, + {8, C<&ISystemSettingsServer::SetLockScreenFlag>, "SetLockScreenFlag"}, {9, nullptr, "GetBacklightSettings"}, {10, nullptr, "SetBacklightSettings"}, {11, nullptr, "SetBluetoothDevicesSettings"}, {12, nullptr, "GetBluetoothDevicesSettings"}, - {13, &ISystemSettingsServer::GetExternalSteadyClockSourceId, "GetExternalSteadyClockSourceId"}, - {14, &ISystemSettingsServer::SetExternalSteadyClockSourceId, "SetExternalSteadyClockSourceId"}, - {15, &ISystemSettingsServer::GetUserSystemClockContext, "GetUserSystemClockContext"}, - {16, &ISystemSettingsServer::SetUserSystemClockContext, "SetUserSystemClockContext"}, - {17, &ISystemSettingsServer::GetAccountSettings, "GetAccountSettings"}, - {18, &ISystemSettingsServer::SetAccountSettings, "SetAccountSettings"}, + {13, C<&ISystemSettingsServer::GetExternalSteadyClockSourceId>, "GetExternalSteadyClockSourceId"}, + {14, C<&ISystemSettingsServer::SetExternalSteadyClockSourceId>, "SetExternalSteadyClockSourceId"}, + {15, C<&ISystemSettingsServer::GetUserSystemClockContext>, "GetUserSystemClockContext"}, + {16, C<&ISystemSettingsServer::SetUserSystemClockContext>, "SetUserSystemClockContext"}, + {17, C<&ISystemSettingsServer::GetAccountSettings>, "GetAccountSettings"}, + {18, C<&ISystemSettingsServer::SetAccountSettings>, "SetAccountSettings"}, {19, nullptr, "GetAudioVolume"}, {20, nullptr, "SetAudioVolume"}, - {21, &ISystemSettingsServer::GetEulaVersions, "GetEulaVersions"}, - {22, &ISystemSettingsServer::SetEulaVersions, "SetEulaVersions"}, - {23, &ISystemSettingsServer::GetColorSetId, "GetColorSetId"}, - {24, &ISystemSettingsServer::SetColorSetId, "SetColorSetId"}, + {21, C<&ISystemSettingsServer::GetEulaVersions>, "GetEulaVersions"}, + {22, C<&ISystemSettingsServer::SetEulaVersions>, "SetEulaVersions"}, + {23, C<&ISystemSettingsServer::GetColorSetId>, "GetColorSetId"}, + {24, C<&ISystemSettingsServer::SetColorSetId>, "SetColorSetId"}, {25, nullptr, "GetConsoleInformationUploadFlag"}, {26, nullptr, "SetConsoleInformationUploadFlag"}, {27, nullptr, "GetAutomaticApplicationDownloadFlag"}, {28, nullptr, "SetAutomaticApplicationDownloadFlag"}, - {29, &ISystemSettingsServer::GetNotificationSettings, "GetNotificationSettings"}, - {30, &ISystemSettingsServer::SetNotificationSettings, "SetNotificationSettings"}, - {31, &ISystemSettingsServer::GetAccountNotificationSettings, "GetAccountNotificationSettings"}, - {32, &ISystemSettingsServer::SetAccountNotificationSettings, "SetAccountNotificationSettings"}, - {35, &ISystemSettingsServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"}, - {36, &ISystemSettingsServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"}, - {37, &ISystemSettingsServer::GetSettingsItemValueSize, "GetSettingsItemValueSize"}, - {38, &ISystemSettingsServer::GetSettingsItemValue, "GetSettingsItemValue"}, - {39, &ISystemSettingsServer::GetTvSettings, "GetTvSettings"}, - {40, &ISystemSettingsServer::SetTvSettings, "SetTvSettings"}, + {29, C<&ISystemSettingsServer::GetNotificationSettings>, "GetNotificationSettings"}, + {30, C<&ISystemSettingsServer::SetNotificationSettings>, "SetNotificationSettings"}, + {31, C<&ISystemSettingsServer::GetAccountNotificationSettings>, "GetAccountNotificationSettings"}, + {32, C<&ISystemSettingsServer::SetAccountNotificationSettings>, "SetAccountNotificationSettings"}, + {35, C<&ISystemSettingsServer::GetVibrationMasterVolume>, "GetVibrationMasterVolume"}, + {36, C<&ISystemSettingsServer::SetVibrationMasterVolume>, "SetVibrationMasterVolume"}, + {37, C<&ISystemSettingsServer::GetSettingsItemValueSize>, "GetSettingsItemValueSize"}, + {38, C<&ISystemSettingsServer::GetSettingsItemValue>, "GetSettingsItemValue"}, + {39, C<&ISystemSettingsServer::GetTvSettings>, "GetTvSettings"}, + {40, C<&ISystemSettingsServer::SetTvSettings>, "SetTvSettings"}, {41, nullptr, "GetEdid"}, {42, nullptr, "SetEdid"}, - {43, &ISystemSettingsServer::GetAudioOutputMode, "GetAudioOutputMode"}, - {44, &ISystemSettingsServer::SetAudioOutputMode, "SetAudioOutputMode"}, - {45, &ISystemSettingsServer::GetSpeakerAutoMuteFlag , "GetSpeakerAutoMuteFlag"}, - {46, &ISystemSettingsServer::SetSpeakerAutoMuteFlag , "SetSpeakerAutoMuteFlag"}, - {47, &ISystemSettingsServer::GetQuestFlag, "GetQuestFlag"}, - {48, &ISystemSettingsServer::SetQuestFlag, "SetQuestFlag"}, + {43, C<&ISystemSettingsServer::GetAudioOutputMode>, "GetAudioOutputMode"}, + {44, C<&ISystemSettingsServer::SetAudioOutputMode>, "SetAudioOutputMode"}, + {45, C<&ISystemSettingsServer::GetSpeakerAutoMuteFlag> , "GetSpeakerAutoMuteFlag"}, + {46, C<&ISystemSettingsServer::SetSpeakerAutoMuteFlag> , "SetSpeakerAutoMuteFlag"}, + {47, C<&ISystemSettingsServer::GetQuestFlag>, "GetQuestFlag"}, + {48, C<&ISystemSettingsServer::SetQuestFlag>, "SetQuestFlag"}, {49, nullptr, "GetDataDeletionSettings"}, {50, nullptr, "SetDataDeletionSettings"}, {51, nullptr, "GetInitialSystemAppletProgramId"}, {52, nullptr, "GetOverlayDispProgramId"}, - {53, &ISystemSettingsServer::GetDeviceTimeZoneLocationName, "GetDeviceTimeZoneLocationName"}, - {54, &ISystemSettingsServer::SetDeviceTimeZoneLocationName, "SetDeviceTimeZoneLocationName"}, + {53, C<&ISystemSettingsServer::GetDeviceTimeZoneLocationName>, "GetDeviceTimeZoneLocationName"}, + {54, C<&ISystemSettingsServer::SetDeviceTimeZoneLocationName>, "SetDeviceTimeZoneLocationName"}, {55, nullptr, "GetWirelessCertificationFileSize"}, {56, nullptr, "GetWirelessCertificationFile"}, - {57, &ISystemSettingsServer::SetRegionCode, "SetRegionCode"}, - {58, &ISystemSettingsServer::GetNetworkSystemClockContext, "GetNetworkSystemClockContext"}, - {59, &ISystemSettingsServer::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"}, - {60, &ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"}, - {61, &ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"}, - {62, &ISystemSettingsServer::GetDebugModeFlag, "GetDebugModeFlag"}, - {63, &ISystemSettingsServer::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"}, - {64, &ISystemSettingsServer::SetPrimaryAlbumStorage, "SetPrimaryAlbumStorage"}, + {57, C<&ISystemSettingsServer::SetRegionCode>, "SetRegionCode"}, + {58, C<&ISystemSettingsServer::GetNetworkSystemClockContext>, "GetNetworkSystemClockContext"}, + {59, C<&ISystemSettingsServer::SetNetworkSystemClockContext>, "SetNetworkSystemClockContext"}, + {60, C<&ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled>, "IsUserSystemClockAutomaticCorrectionEnabled"}, + {61, C<&ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled>, "SetUserSystemClockAutomaticCorrectionEnabled"}, + {62, C<&ISystemSettingsServer::GetDebugModeFlag>, "GetDebugModeFlag"}, + {63, C<&ISystemSettingsServer::GetPrimaryAlbumStorage>, "GetPrimaryAlbumStorage"}, + {64, C<&ISystemSettingsServer::SetPrimaryAlbumStorage>, "SetPrimaryAlbumStorage"}, {65, nullptr, "GetUsb30EnableFlag"}, {66, nullptr, "SetUsb30EnableFlag"}, - {67, &ISystemSettingsServer::GetBatteryLot, "GetBatteryLot"}, - {68, &ISystemSettingsServer::GetSerialNumber, "GetSerialNumber"}, - {69, &ISystemSettingsServer::GetNfcEnableFlag, "GetNfcEnableFlag"}, - {70, &ISystemSettingsServer::SetNfcEnableFlag, "SetNfcEnableFlag"}, - {71, &ISystemSettingsServer::GetSleepSettings, "GetSleepSettings"}, - {72, &ISystemSettingsServer::SetSleepSettings, "SetSleepSettings"}, - {73, &ISystemSettingsServer::GetWirelessLanEnableFlag, "GetWirelessLanEnableFlag"}, - {74, &ISystemSettingsServer::SetWirelessLanEnableFlag, "SetWirelessLanEnableFlag"}, - {75, &ISystemSettingsServer::GetInitialLaunchSettings, "GetInitialLaunchSettings"}, - {76, &ISystemSettingsServer::SetInitialLaunchSettings, "SetInitialLaunchSettings"}, - {77, &ISystemSettingsServer::GetDeviceNickName, "GetDeviceNickName"}, - {78, &ISystemSettingsServer::SetDeviceNickName, "SetDeviceNickName"}, - {79, &ISystemSettingsServer::GetProductModel, "GetProductModel"}, + {67, C<&ISystemSettingsServer::GetBatteryLot>, "GetBatteryLot"}, + {68, C<&ISystemSettingsServer::GetSerialNumber>, "GetSerialNumber"}, + {69, C<&ISystemSettingsServer::GetNfcEnableFlag>, "GetNfcEnableFlag"}, + {70, C<&ISystemSettingsServer::SetNfcEnableFlag>, "SetNfcEnableFlag"}, + {71, C<&ISystemSettingsServer::GetSleepSettings>, "GetSleepSettings"}, + {72, C<&ISystemSettingsServer::SetSleepSettings>, "SetSleepSettings"}, + {73, C<&ISystemSettingsServer::GetWirelessLanEnableFlag>, "GetWirelessLanEnableFlag"}, + {74, C<&ISystemSettingsServer::SetWirelessLanEnableFlag>, "SetWirelessLanEnableFlag"}, + {75, C<&ISystemSettingsServer::GetInitialLaunchSettings>, "GetInitialLaunchSettings"}, + {76, C<&ISystemSettingsServer::SetInitialLaunchSettings>, "SetInitialLaunchSettings"}, + {77, C<&ISystemSettingsServer::GetDeviceNickName>, "GetDeviceNickName"}, + {78, C<&ISystemSettingsServer::SetDeviceNickName>, "SetDeviceNickName"}, + {79, C<&ISystemSettingsServer::GetProductModel>, "GetProductModel"}, {80, nullptr, "GetLdnChannel"}, {81, nullptr, "SetLdnChannel"}, {82, nullptr, "AcquireTelemetryDirtyFlagEventHandle"}, @@ -176,25 +177,25 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {85, nullptr, "SetPtmBatteryLot"}, {86, nullptr, "GetPtmFuelGaugeParameter"}, {87, nullptr, "SetPtmFuelGaugeParameter"}, - {88, &ISystemSettingsServer::GetBluetoothEnableFlag, "GetBluetoothEnableFlag"}, - {89, &ISystemSettingsServer::SetBluetoothEnableFlag, "SetBluetoothEnableFlag"}, - {90, &ISystemSettingsServer::GetMiiAuthorId, "GetMiiAuthorId"}, + {88, C<&ISystemSettingsServer::GetBluetoothEnableFlag>, "GetBluetoothEnableFlag"}, + {89, C<&ISystemSettingsServer::SetBluetoothEnableFlag>, "SetBluetoothEnableFlag"}, + {90, C<&ISystemSettingsServer::GetMiiAuthorId>, "GetMiiAuthorId"}, {91, nullptr, "SetShutdownRtcValue"}, {92, nullptr, "GetShutdownRtcValue"}, {93, nullptr, "AcquireFatalDirtyFlagEventHandle"}, {94, nullptr, "GetFatalDirtyFlags"}, - {95, &ISystemSettingsServer::GetAutoUpdateEnableFlag, "GetAutoUpdateEnableFlag"}, - {96, &ISystemSettingsServer::SetAutoUpdateEnableFlag, "SetAutoUpdateEnableFlag"}, + {95, C<&ISystemSettingsServer::GetAutoUpdateEnableFlag>, "GetAutoUpdateEnableFlag"}, + {96, C<&ISystemSettingsServer::SetAutoUpdateEnableFlag>, "SetAutoUpdateEnableFlag"}, {97, nullptr, "GetNxControllerSettings"}, {98, nullptr, "SetNxControllerSettings"}, - {99, &ISystemSettingsServer::GetBatteryPercentageFlag, "GetBatteryPercentageFlag"}, - {100, &ISystemSettingsServer::SetBatteryPercentageFlag, "SetBatteryPercentageFlag"}, + {99, C<&ISystemSettingsServer::GetBatteryPercentageFlag>, "GetBatteryPercentageFlag"}, + {100, C<&ISystemSettingsServer::SetBatteryPercentageFlag>, "SetBatteryPercentageFlag"}, {101, nullptr, "GetExternalRtcResetFlag"}, {102, nullptr, "SetExternalRtcResetFlag"}, {103, nullptr, "GetUsbFullKeyEnableFlag"}, {104, nullptr, "SetUsbFullKeyEnableFlag"}, - {105, &ISystemSettingsServer::SetExternalSteadyClockInternalOffset, "SetExternalSteadyClockInternalOffset"}, - {106, &ISystemSettingsServer::GetExternalSteadyClockInternalOffset, "GetExternalSteadyClockInternalOffset"}, + {105, C<&ISystemSettingsServer::SetExternalSteadyClockInternalOffset>, "SetExternalSteadyClockInternalOffset"}, + {106, C<&ISystemSettingsServer::GetExternalSteadyClockInternalOffset>, "GetExternalSteadyClockInternalOffset"}, {107, nullptr, "GetBacklightSettingsEx"}, {108, nullptr, "SetBacklightSettingsEx"}, {109, nullptr, "GetHeadphoneVolumeWarningCount"}, @@ -208,14 +209,14 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {117, nullptr, "GetHeadphoneVolumeUpdateFlag"}, {118, nullptr, "SetHeadphoneVolumeUpdateFlag"}, {119, nullptr, "NeedsToUpdateHeadphoneVolume"}, - {120, &ISystemSettingsServer::GetPushNotificationActivityModeOnSleep, "GetPushNotificationActivityModeOnSleep"}, - {121, &ISystemSettingsServer::SetPushNotificationActivityModeOnSleep, "SetPushNotificationActivityModeOnSleep"}, + {120, C<&ISystemSettingsServer::GetPushNotificationActivityModeOnSleep>, "GetPushNotificationActivityModeOnSleep"}, + {121, C<&ISystemSettingsServer::SetPushNotificationActivityModeOnSleep>, "SetPushNotificationActivityModeOnSleep"}, {122, nullptr, "GetServiceDiscoveryControlSettings"}, {123, nullptr, "SetServiceDiscoveryControlSettings"}, - {124, &ISystemSettingsServer::GetErrorReportSharePermission, "GetErrorReportSharePermission"}, - {125, &ISystemSettingsServer::SetErrorReportSharePermission, "SetErrorReportSharePermission"}, - {126, &ISystemSettingsServer::GetAppletLaunchFlags, "GetAppletLaunchFlags"}, - {127, &ISystemSettingsServer::SetAppletLaunchFlags, "SetAppletLaunchFlags"}, + {124, C<&ISystemSettingsServer::GetErrorReportSharePermission>, "GetErrorReportSharePermission"}, + {125, C<&ISystemSettingsServer::SetErrorReportSharePermission>, "SetErrorReportSharePermission"}, + {126, C<&ISystemSettingsServer::GetAppletLaunchFlags>, "GetAppletLaunchFlags"}, + {127, C<&ISystemSettingsServer::SetAppletLaunchFlags>, "SetAppletLaunchFlags"}, {128, nullptr, "GetConsoleSixAxisSensorAccelerationBias"}, {129, nullptr, "SetConsoleSixAxisSensorAccelerationBias"}, {130, nullptr, "GetConsoleSixAxisSensorAngularVelocityBias"}, @@ -224,8 +225,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {133, nullptr, "SetConsoleSixAxisSensorAccelerationGain"}, {134, nullptr, "GetConsoleSixAxisSensorAngularVelocityGain"}, {135, nullptr, "SetConsoleSixAxisSensorAngularVelocityGain"}, - {136, &ISystemSettingsServer::GetKeyboardLayout, "GetKeyboardLayout"}, - {137, &ISystemSettingsServer::SetKeyboardLayout, "SetKeyboardLayout"}, + {136, C<&ISystemSettingsServer::GetKeyboardLayout>, "GetKeyboardLayout"}, + {137, C<&ISystemSettingsServer::SetKeyboardLayout>, "SetKeyboardLayout"}, {138, nullptr, "GetWebInspectorFlag"}, {139, nullptr, "GetAllowedSslHosts"}, {140, nullptr, "GetHostFsMountPoint"}, @@ -238,10 +239,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"}, {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"}, {149, nullptr, "GetRebootlessSystemUpdateVersion"}, - {150, &ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime, "GetDeviceTimeZoneLocationUpdatedTime"}, - {151, &ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime, "SetDeviceTimeZoneLocationUpdatedTime"}, - {152, &ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime, "GetUserSystemClockAutomaticCorrectionUpdatedTime"}, - {153, &ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime, "SetUserSystemClockAutomaticCorrectionUpdatedTime"}, + {150, C<&ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime>, "GetDeviceTimeZoneLocationUpdatedTime"}, + {151, C<&ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime>, "SetDeviceTimeZoneLocationUpdatedTime"}, + {152, C<&ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime>, "GetUserSystemClockAutomaticCorrectionUpdatedTime"}, + {153, C<&ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime>, "SetUserSystemClockAutomaticCorrectionUpdatedTime"}, {154, nullptr, "GetAccountOnlineStorageSettings"}, {155, nullptr, "SetAccountOnlineStorageSettings"}, {156, nullptr, "GetPctlReadyFlag"}, @@ -258,11 +259,11 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {167, nullptr, "SetUsb30DeviceEnableFlag"}, {168, nullptr, "GetThemeId"}, {169, nullptr, "SetThemeId"}, - {170, &ISystemSettingsServer::GetChineseTraditionalInputMethod, "GetChineseTraditionalInputMethod"}, + {170, C<&ISystemSettingsServer::GetChineseTraditionalInputMethod>, "GetChineseTraditionalInputMethod"}, {171, nullptr, "SetChineseTraditionalInputMethod"}, {172, nullptr, "GetPtmCycleCountReliability"}, {173, nullptr, "SetPtmCycleCountReliability"}, - {174, &ISystemSettingsServer::GetHomeMenuScheme, "GetHomeMenuScheme"}, + {174, C<&ISystemSettingsServer::GetHomeMenuScheme>, "GetHomeMenuScheme"}, {175, nullptr, "GetThemeSettings"}, {176, nullptr, "SetThemeSettings"}, {177, nullptr, "GetThemeKey"}, @@ -273,10 +274,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {182, nullptr, "SetT"}, {183, nullptr, "GetPlatformRegion"}, {184, nullptr, "SetPlatformRegion"}, - {185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, + {185, C<&ISystemSettingsServer::GetHomeMenuSchemeModel>, "GetHomeMenuSchemeModel"}, {186, nullptr, "GetMemoryUsageRateFlag"}, - {187, &ISystemSettingsServer::GetTouchScreenMode, "GetTouchScreenMode"}, - {188, &ISystemSettingsServer::SetTouchScreenMode, "SetTouchScreenMode"}, + {187, C<&ISystemSettingsServer::GetTouchScreenMode>, "GetTouchScreenMode"}, + {188, C<&ISystemSettingsServer::SetTouchScreenMode>, "SetTouchScreenMode"}, {189, nullptr, "GetButtonConfigSettingsFull"}, {190, nullptr, "SetButtonConfigSettingsFull"}, {191, nullptr, "GetButtonConfigSettingsEmbedded"}, @@ -289,10 +290,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"}, {199, nullptr, "GetButtonConfigRegisteredSettings"}, {200, nullptr, "SetButtonConfigRegisteredSettings"}, - {201, &ISystemSettingsServer::GetFieldTestingFlag, "GetFieldTestingFlag"}, + {201, C<&ISystemSettingsServer::GetFieldTestingFlag>, "GetFieldTestingFlag"}, {202, nullptr, "SetFieldTestingFlag"}, - {203, &ISystemSettingsServer::GetPanelCrcMode, "GetPanelCrcMode"}, - {204, &ISystemSettingsServer::SetPanelCrcMode, "SetPanelCrcMode"}, + {203, C<&ISystemSettingsServer::GetPanelCrcMode>, "GetPanelCrcMode"}, + {204, C<&ISystemSettingsServer::SetPanelCrcMode>, "SetPanelCrcMode"}, {205, nullptr, "GetNxControllerSettingsEx"}, {206, nullptr, "SetNxControllerSettingsEx"}, {207, nullptr, "GetHearingProtectionSafeguardFlag"}, @@ -422,178 +423,134 @@ bool ISystemSettingsServer::StoreSettingsFile(std::filesystem::path& path, auto& return true; } -void ISystemSettingsServer::SetLanguageCode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.language_code = rp.PopEnum<LanguageCode>(); - SetSaveNeeded(); - - LOG_INFO(Service_SET, "called, language_code={}", m_system_settings.language_code); +Result ISystemSettingsServer::SetLanguageCode(LanguageCode language_code) { + LOG_INFO(Service_SET, "called, language_code={}", language_code); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.language_code = language_code; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetFirmwareVersion(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetFirmwareVersion( + OutLargeData<FirmwareVersionFormat, BufferAttr_HipcPointer> out_firmware_data) { LOG_DEBUG(Service_SET, "called"); - FirmwareVersionFormat firmware_data{}; - const auto result = - GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1); - - if (result.IsSuccess()) { - ctx.WriteBuffer(firmware_data); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_RETURN(GetFirmwareVersionImpl(*out_firmware_data, system, GetFirmwareVersionType::Version1)); } -void ISystemSettingsServer::GetFirmwareVersion2(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetFirmwareVersion2( + OutLargeData<FirmwareVersionFormat, BufferAttr_HipcPointer> out_firmware_data) { LOG_DEBUG(Service_SET, "called"); - FirmwareVersionFormat firmware_data{}; - const auto result = - GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2); - - if (result.IsSuccess()) { - ctx.WriteBuffer(firmware_data); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_RETURN(GetFirmwareVersionImpl(*out_firmware_data, system, GetFirmwareVersionType::Version2)); } -void ISystemSettingsServer::GetExternalSteadyClockSourceId(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - Common::UUID id{}; - const auto res = GetExternalSteadyClockSourceId(id); +Result ISystemSettingsServer::GetLockScreenFlag(Out<bool> out_lock_screen_flag) { + LOG_INFO(Service_SET, "called, lock_screen_flag={}", m_system_settings.lock_screen_flag); - IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)}; - rb.Push(res); - rb.PushRaw(id); + *out_lock_screen_flag = m_system_settings.lock_screen_flag; + R_SUCCEED(); } -void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); +Result ISystemSettingsServer::SetLockScreenFlag(bool lock_screen_flag) { + LOG_INFO(Service_SET, "called, lock_screen_flag={}", lock_screen_flag); - IPC::RequestParser rp{ctx}; - const auto id{rp.PopRaw<Common::UUID>()}; + m_system_settings.lock_screen_flag = lock_screen_flag; + SetSaveNeeded(); + R_SUCCEED(); +} - const auto res = SetExternalSteadyClockSourceId(id); +Result ISystemSettingsServer::GetExternalSteadyClockSourceId( + Out<Common::UUID> out_clock_source_id) { + LOG_INFO(Service_SET, "called, clock_source_id={}", + m_private_settings.external_clock_source_id.FormattedString()); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + *out_clock_source_id = m_private_settings.external_clock_source_id; + R_SUCCEED(); } -void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); - - Service::PSC::Time::SystemClockContext context{}; - const auto res = GetUserSystemClockContext(context); +Result ISystemSettingsServer::SetExternalSteadyClockSourceId(const Common::UUID& clock_source_id) { + LOG_INFO(Service_SET, "called, clock_source_id={}", clock_source_id.FormattedString()); - IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; - rb.Push(res); - rb.PushRaw(context); + m_private_settings.external_clock_source_id = clock_source_id; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetUserSystemClockContext( + Out<Service::PSC::Time::SystemClockContext> out_clock_context) { LOG_INFO(Service_SET, "called"); - IPC::RequestParser rp{ctx}; - const auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; - - const auto res = SetUserSystemClockContext(context); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + *out_clock_context = m_system_settings.user_system_clock_context; + R_SUCCEED(); } -void ISystemSettingsServer::GetLockScreenFlag(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called, lock_screen_flag={}", m_system_settings.lock_screen_flag); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.lock_screen_flag); -} +Result ISystemSettingsServer::SetUserSystemClockContext( + const Service::PSC::Time::SystemClockContext& clock_context) { + LOG_INFO(Service_SET, "called"); -void ISystemSettingsServer::SetLockScreenFlag(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.lock_screen_flag = rp.Pop<bool>(); + m_system_settings.user_system_clock_context = clock_context; SetSaveNeeded(); - - LOG_INFO(Service_SET, "called, lock_screen_flag={}", m_system_settings.lock_screen_flag); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } -void ISystemSettingsServer::GetAccountSettings(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); +Result ISystemSettingsServer::GetAccountSettings(Out<AccountSettings> out_account_settings) { + LOG_INFO(Service_SET, "called, account_settings_flags={}", + m_system_settings.account_settings.flags); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.account_settings); + *out_account_settings = m_system_settings.account_settings; + R_SUCCEED(); } -void ISystemSettingsServer::SetAccountSettings(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.account_settings = rp.PopRaw<AccountSettings>(); - SetSaveNeeded(); - - LOG_INFO(Service_SET, "called, account_settings_flags={}", - m_system_settings.account_settings.flags); +Result ISystemSettingsServer::SetAccountSettings(AccountSettings account_settings) { + LOG_INFO(Service_SET, "called, account_settings_flags={}", account_settings.flags); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.account_settings = account_settings; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetEulaVersions(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetEulaVersions( + Out<s32> out_count, OutArray<EulaVersion, BufferAttr_HipcMapAlias> out_eula_versions) { LOG_INFO(Service_SET, "called, elements={}", m_system_settings.eula_version_count); - ctx.WriteBuffer(m_system_settings.eula_versions); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.eula_version_count); + *out_count = + std::min(m_system_settings.eula_version_count, static_cast<s32>(out_eula_versions.size())); + memcpy(out_eula_versions.data(), m_system_settings.eula_versions.data(), + static_cast<std::size_t>(*out_count) * sizeof(EulaVersion)); + R_SUCCEED(); } -void ISystemSettingsServer::SetEulaVersions(HLERequestContext& ctx) { - const auto elements = ctx.GetReadBufferNumElements<EulaVersion>(); - const auto buffer_data = ctx.ReadBuffer(); +Result ISystemSettingsServer::SetEulaVersions( + InArray<EulaVersion, BufferAttr_HipcMapAlias> eula_versions) { + LOG_INFO(Service_SET, "called, elements={}", eula_versions.size()); - LOG_INFO(Service_SET, "called, elements={}", elements); - ASSERT(elements <= m_system_settings.eula_versions.size()); + ASSERT(eula_versions.size() <= m_system_settings.eula_versions.size()); - m_system_settings.eula_version_count = static_cast<u32>(elements); - std::memcpy(&m_system_settings.eula_versions, buffer_data.data(), - sizeof(EulaVersion) * elements); + m_system_settings.eula_version_count = static_cast<s32>(eula_versions.size()); + std::memcpy(m_system_settings.eula_versions.data(), eula_versions.data(), + eula_versions.size() * sizeof(EulaVersion)); SetSaveNeeded(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } -void ISystemSettingsServer::GetColorSetId(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetColorSetId(Out<ColorSet> out_color_set_id) { LOG_DEBUG(Service_SET, "called, color_set=", m_system_settings.color_set_id); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(m_system_settings.color_set_id); + *out_color_set_id = m_system_settings.color_set_id; + R_SUCCEED(); } -void ISystemSettingsServer::SetColorSetId(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.color_set_id = rp.PopEnum<ColorSet>(); - SetSaveNeeded(); - - LOG_DEBUG(Service_SET, "called, color_set={}", m_system_settings.color_set_id); +Result ISystemSettingsServer::SetColorSetId(ColorSet color_set_id) { + LOG_DEBUG(Service_SET, "called, color_set={}", color_set_id); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.color_set_id = color_set_id; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetNotificationSettings(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetNotificationSettings( + Out<NotificationSettings> out_notification_settings) { LOG_INFO(Service_SET, "called, flags={}, volume={}, head_time={}:{}, tailt_time={}:{}", m_system_settings.notification_settings.flags.raw, m_system_settings.notification_settings.volume, @@ -602,77 +559,67 @@ void ISystemSettingsServer::GetNotificationSettings(HLERequestContext& ctx) { m_system_settings.notification_settings.stop_time.hour, m_system_settings.notification_settings.stop_time.minute); - IPC::ResponseBuilder rb{ctx, 8}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.notification_settings); + *out_notification_settings = m_system_settings.notification_settings; + R_SUCCEED(); } -void ISystemSettingsServer::SetNotificationSettings(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.notification_settings = rp.PopRaw<NotificationSettings>(); - SetSaveNeeded(); - +Result ISystemSettingsServer::SetNotificationSettings( + const NotificationSettings& notification_settings) { LOG_INFO(Service_SET, "called, flags={}, volume={}, head_time={}:{}, tailt_time={}:{}", - m_system_settings.notification_settings.flags.raw, - m_system_settings.notification_settings.volume, - m_system_settings.notification_settings.start_time.hour, - m_system_settings.notification_settings.start_time.minute, - m_system_settings.notification_settings.stop_time.hour, - m_system_settings.notification_settings.stop_time.minute); + notification_settings.flags.raw, notification_settings.volume, + notification_settings.start_time.hour, notification_settings.start_time.minute, + notification_settings.stop_time.hour, notification_settings.stop_time.minute); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.notification_settings = notification_settings; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetAccountNotificationSettings(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetAccountNotificationSettings( + Out<s32> out_count, OutArray<AccountNotificationSettings, BufferAttr_HipcMapAlias> + out_account_notification_settings) { LOG_INFO(Service_SET, "called, elements={}", m_system_settings.account_notification_settings_count); - ctx.WriteBuffer(m_system_settings.account_notification_settings); + *out_count = std::min(m_system_settings.account_notification_settings_count, + static_cast<s32>(out_account_notification_settings.size())); + memcpy(out_account_notification_settings.data(), + m_system_settings.account_notification_settings.data(), + static_cast<std::size_t>(*out_count) * sizeof(AccountNotificationSettings)); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.account_notification_settings_count); + R_SUCCEED(); } -void ISystemSettingsServer::SetAccountNotificationSettings(HLERequestContext& ctx) { - const auto elements = ctx.GetReadBufferNumElements<AccountNotificationSettings>(); - const auto buffer_data = ctx.ReadBuffer(); - - LOG_INFO(Service_SET, "called, elements={}", elements); +Result ISystemSettingsServer::SetAccountNotificationSettings( + InArray<AccountNotificationSettings, BufferAttr_HipcMapAlias> account_notification_settings) { + LOG_INFO(Service_SET, "called, elements={}", account_notification_settings.size()); - ASSERT(elements <= m_system_settings.account_notification_settings.size()); + ASSERT(account_notification_settings.size() <= + m_system_settings.account_notification_settings.size()); - m_system_settings.account_notification_settings_count = static_cast<u32>(elements); - std::memcpy(&m_system_settings.account_notification_settings, buffer_data.data(), - elements * sizeof(AccountNotificationSettings)); + m_system_settings.account_notification_settings_count = + static_cast<s32>(account_notification_settings.size()); + std::memcpy(m_system_settings.account_notification_settings.data(), + account_notification_settings.data(), + account_notification_settings.size() * sizeof(AccountNotificationSettings)); SetSaveNeeded(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } -void ISystemSettingsServer::GetVibrationMasterVolume(HLERequestContext& ctx) { - f32 vibration_master_volume = {}; - const auto result = GetVibrationMasterVolume(vibration_master_volume); +Result ISystemSettingsServer::GetVibrationMasterVolume(Out<f32> vibration_master_volume) { + LOG_INFO(Service_SET, "called, vibration_master_volume={}", + m_system_settings.vibration_master_volume); - LOG_INFO(Service_SET, "called, master_volume={}", vibration_master_volume); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(vibration_master_volume); + *vibration_master_volume = m_system_settings.vibration_master_volume; + R_SUCCEED(); } -void ISystemSettingsServer::SetVibrationMasterVolume(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto vibration_master_volume = rp.PopRaw<f32>(); - - LOG_INFO(Service_SET, "called, elements={}", m_system_settings.vibration_master_volume); +Result ISystemSettingsServer::SetVibrationMasterVolume(f32 vibration_master_volume) { + LOG_INFO(Service_SET, "called, vibration_master_volume={}", vibration_master_volume); - const auto result = SetVibrationMasterVolume(vibration_master_volume); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + m_system_settings.vibration_master_volume = vibration_master_volume; + SetSaveNeeded(); + R_SUCCEED(); } // FIXME: implement support for the real system_settings.ini @@ -734,55 +681,38 @@ static Settings GetSettings() { return ret; } -void ISystemSettingsServer::GetSettingsItemValueSize(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called"); +Result ISystemSettingsServer::GetSettingsItemValueSize( + Out<u64> out_size, InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_category_buffer, + InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_name_buffer) { + const std::string setting_category{Common::StringFromBuffer(*setting_category_buffer)}; + const std::string setting_name{Common::StringFromBuffer(*setting_name_buffer)}; - // The category of the setting. This corresponds to the top-level keys of - // system_settings.ini. - const auto setting_category_buf{ctx.ReadBuffer(0)}; - const std::string setting_category{Common::StringFromBuffer(setting_category_buf)}; + LOG_DEBUG(Service_SET, "called, category={}, name={}", setting_category, setting_name); - // The name of the setting. This corresponds to the second-level keys of - // system_settings.ini. - const auto setting_name_buf{ctx.ReadBuffer(1)}; - const std::string setting_name{Common::StringFromBuffer(setting_name_buf)}; + *out_size = 0; auto settings{GetSettings()}; - u64 response_size{0}; - if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) { - response_size = settings[setting_category][setting_name].size(); + *out_size = settings[setting_category][setting_name].size(); } - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(response_size == 0 ? ResultUnknown : ResultSuccess); - rb.Push(response_size); + R_UNLESS(*out_size != 0, ResultUnknown); + R_SUCCEED(); } -void ISystemSettingsServer::GetSettingsItemValue(HLERequestContext& ctx) { - // The category of the setting. This corresponds to the top-level keys of - // system_settings.ini. - const auto setting_category_buf{ctx.ReadBuffer(0)}; - const std::string setting_category{Common::StringFromBuffer(setting_category_buf)}; - - // The name of the setting. This corresponds to the second-level keys of - // system_settings.ini. - const auto setting_name_buf{ctx.ReadBuffer(1)}; - const std::string setting_name{Common::StringFromBuffer(setting_name_buf)}; +Result ISystemSettingsServer::GetSettingsItemValue( + OutBuffer<BufferAttr_HipcMapAlias> out_data, + InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_category_buffer, + InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_name_buffer) { + const std::string setting_category{Common::StringFromBuffer(*setting_category_buffer)}; + const std::string setting_name{Common::StringFromBuffer(*setting_name_buffer)}; - std::vector<u8> value; - auto response = GetSettingsItemValue(value, setting_category, setting_name); + LOG_INFO(Service_SET, "called, category={}, name={}", setting_category, setting_name); - LOG_INFO(Service_SET, "called. category={}, name={} -- res=0x{:X}", setting_category, - setting_name, response.raw); - - ctx.WriteBuffer(value.data(), value.size()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(response); + R_RETURN(GetSettingsItemValueImpl(out_data, setting_category, setting_name)); } -void ISystemSettingsServer::GetTvSettings(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetTvSettings(Out<TvSettings> out_tv_settings) { LOG_INFO(Service_SET, "called, flags={}, cmu_mode={}, contrast_ratio={}, hdmi_content_type={}, " "rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}", @@ -793,371 +723,335 @@ void ISystemSettingsServer::GetTvSettings(HLERequestContext& ctx) { m_system_settings.tv_settings.tv_resolution, m_system_settings.tv_settings.tv_underscan); - IPC::ResponseBuilder rb{ctx, 10}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.tv_settings); + *out_tv_settings = m_system_settings.tv_settings; + R_SUCCEED(); } -void ISystemSettingsServer::SetTvSettings(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.tv_settings = rp.PopRaw<TvSettings>(); - SetSaveNeeded(); +Result ISystemSettingsServer::SetTvSettings(TvSettings tv_settings) { LOG_INFO(Service_SET, "called, flags={}, cmu_mode={}, contrast_ratio={}, hdmi_content_type={}, " "rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}", - m_system_settings.tv_settings.flags.raw, m_system_settings.tv_settings.cmu_mode, - m_system_settings.tv_settings.contrast_ratio, - m_system_settings.tv_settings.hdmi_content_type, - m_system_settings.tv_settings.rgb_range, m_system_settings.tv_settings.tv_gama, - m_system_settings.tv_settings.tv_resolution, - m_system_settings.tv_settings.tv_underscan); + tv_settings.flags.raw, tv_settings.cmu_mode, tv_settings.contrast_ratio, + tv_settings.hdmi_content_type, tv_settings.rgb_range, tv_settings.tv_gama, + tv_settings.tv_resolution, tv_settings.tv_underscan); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.tv_settings = tv_settings; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetAudioOutputMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto target{rp.PopEnum<AudioOutputModeTarget>()}; - - AudioOutputMode output_mode{}; - const auto result = GetAudioOutputMode(output_mode, target); - - LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode); +Result ISystemSettingsServer::GetAudioOutputMode(Out<AudioOutputMode> out_output_mode, + AudioOutputModeTarget target) { + switch (target) { + case AudioOutputModeTarget::Hdmi: + *out_output_mode = m_system_settings.audio_output_mode_hdmi; + break; + case AudioOutputModeTarget::Speaker: + *out_output_mode = m_system_settings.audio_output_mode_speaker; + break; + case AudioOutputModeTarget::Headphone: + *out_output_mode = m_system_settings.audio_output_mode_headphone; + break; + case AudioOutputModeTarget::Type3: + *out_output_mode = m_system_settings.audio_output_mode_type3; + break; + case AudioOutputModeTarget::Type4: + *out_output_mode = m_system_settings.audio_output_mode_type4; + break; + default: + LOG_ERROR(Service_SET, "Invalid audio output mode target {}", target); + } - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.PushEnum(output_mode); + LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, *out_output_mode); + R_SUCCEED(); } -void ISystemSettingsServer::SetAudioOutputMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto target{rp.PopEnum<AudioOutputModeTarget>()}; - const auto output_mode{rp.PopEnum<AudioOutputMode>()}; - - const auto result = SetAudioOutputMode(target, output_mode); - +Result ISystemSettingsServer::SetAudioOutputMode(AudioOutputModeTarget target, + AudioOutputMode output_mode) { LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + switch (target) { + case AudioOutputModeTarget::Hdmi: + m_system_settings.audio_output_mode_hdmi = output_mode; + break; + case AudioOutputModeTarget::Speaker: + m_system_settings.audio_output_mode_speaker = output_mode; + break; + case AudioOutputModeTarget::Headphone: + m_system_settings.audio_output_mode_headphone = output_mode; + break; + case AudioOutputModeTarget::Type3: + m_system_settings.audio_output_mode_type3 = output_mode; + break; + case AudioOutputModeTarget::Type4: + m_system_settings.audio_output_mode_type4 = output_mode; + break; + default: + LOG_ERROR(Service_SET, "Invalid audio output mode target {}", target); + } + + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetSpeakerAutoMuteFlag(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetSpeakerAutoMuteFlag( + Out<bool> out_force_mute_on_headphone_removed) { LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}", m_system_settings.force_mute_on_headphone_removed); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.force_mute_on_headphone_removed); + *out_force_mute_on_headphone_removed = m_system_settings.force_mute_on_headphone_removed; + R_SUCCEED(); } -void ISystemSettingsServer::SetSpeakerAutoMuteFlag(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.force_mute_on_headphone_removed = rp.PopRaw<bool>(); - SetSaveNeeded(); - +Result ISystemSettingsServer::SetSpeakerAutoMuteFlag(bool force_mute_on_headphone_removed) { LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}", - m_system_settings.force_mute_on_headphone_removed); + force_mute_on_headphone_removed); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.force_mute_on_headphone_removed = force_mute_on_headphone_removed; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetQuestFlag(Out<QuestFlag> out_quest_flag) { LOG_INFO(Service_SET, "called, quest_flag={}", m_system_settings.quest_flag); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(m_system_settings.quest_flag); + *out_quest_flag = m_system_settings.quest_flag; + R_SUCCEED(); } -void ISystemSettingsServer::SetQuestFlag(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.quest_flag = rp.PopEnum<QuestFlag>(); - SetSaveNeeded(); +Result ISystemSettingsServer::SetQuestFlag(QuestFlag quest_flag) { + LOG_INFO(Service_SET, "called, quest_flag={}", quest_flag); - LOG_INFO(Service_SET, "called, quest_flag={}", m_system_settings.quest_flag); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.quest_flag = quest_flag; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetDeviceTimeZoneLocationName( + Out<Service::PSC::Time::LocationName> out_name) { LOG_INFO(Service_SET, "called"); - Service::PSC::Time::LocationName name{}; - const auto res = GetDeviceTimeZoneLocationName(name); - - IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)}; - rb.Push(res); - rb.PushRaw<Service::PSC::Time::LocationName>(name); + *out_name = m_system_settings.device_time_zone_location_name; + R_SUCCEED(); } -void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) { +Result ISystemSettingsServer::SetDeviceTimeZoneLocationName( + const Service::PSC::Time::LocationName& name) { LOG_INFO(Service_SET, "called"); - IPC::RequestParser rp{ctx}; - auto name{rp.PopRaw<Service::PSC::Time::LocationName>()}; - - const auto res = SetDeviceTimeZoneLocationName(name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); -} - -void ISystemSettingsServer::SetRegionCode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.region_code = rp.PopEnum<SystemRegionCode>(); + m_system_settings.device_time_zone_location_name = name; SetSaveNeeded(); - - LOG_INFO(Service_SET, "called, region_code={}", m_system_settings.region_code); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } -void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); +Result ISystemSettingsServer::SetRegionCode(SystemRegionCode region_code) { + LOG_INFO(Service_SET, "called, region_code={}", region_code); - Service::PSC::Time::SystemClockContext context{}; - const auto res = GetNetworkSystemClockContext(context); - - IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; - rb.Push(res); - rb.PushRaw(context); + m_system_settings.region_code = region_code; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::SetNetworkSystemClockContext(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetNetworkSystemClockContext( + Out<Service::PSC::Time::SystemClockContext> out_context) { LOG_INFO(Service_SET, "called"); - IPC::RequestParser rp{ctx}; - const auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; - - const auto res = SetNetworkSystemClockContext(context); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + *out_context = m_system_settings.network_system_clock_context; + R_SUCCEED(); } -void ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) { +Result ISystemSettingsServer::SetNetworkSystemClockContext( + const Service::PSC::Time::SystemClockContext& context) { LOG_INFO(Service_SET, "called"); - bool enabled{}; - const auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(res); - rb.PushRaw(enabled); + m_system_settings.network_system_clock_context = context; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) { - LOG_INFO(Service_SET, "called"); +Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled( + Out<bool> out_automatic_correction_enabled) { + LOG_INFO(Service_SET, "called, out_automatic_correction_enabled={}", + m_system_settings.user_system_clock_automatic_correction_enabled); - IPC::RequestParser rp{ctx}; - auto enabled{rp.Pop<bool>()}; + *out_automatic_correction_enabled = + m_system_settings.user_system_clock_automatic_correction_enabled; + R_SUCCEED(); +} - const auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled); +Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled( + bool automatic_correction_enabled) { + LOG_INFO(Service_SET, "called, out_automatic_correction_enabled={}", + automatic_correction_enabled); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + m_system_settings.user_system_clock_automatic_correction_enabled = automatic_correction_enabled; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) { - bool is_debug_mode_enabled = false; - GetSettingsItemValue<bool>(is_debug_mode_enabled, "settings_debug", "is_debug_mode_enabled"); +Result ISystemSettingsServer::GetDebugModeFlag(Out<bool> is_debug_mode_enabled) { + const auto result = GetSettingsItemValueImpl<bool>(*is_debug_mode_enabled, "settings_debug", + "is_debug_mode_enabled"); - LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", is_debug_mode_enabled); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_debug_mode_enabled); + LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", *is_debug_mode_enabled); + R_RETURN(result); } -void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetPrimaryAlbumStorage( + Out<PrimaryAlbumStorage> out_primary_album_storage) { LOG_INFO(Service_SET, "called, primary_album_storage={}", m_system_settings.primary_album_storage); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(m_system_settings.primary_album_storage); + *out_primary_album_storage = m_system_settings.primary_album_storage; + R_SUCCEED(); } -void ISystemSettingsServer::SetPrimaryAlbumStorage(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.primary_album_storage = rp.PopEnum<PrimaryAlbumStorage>(); - SetSaveNeeded(); +Result ISystemSettingsServer::SetPrimaryAlbumStorage(PrimaryAlbumStorage primary_album_storage) { + LOG_INFO(Service_SET, "called, primary_album_storage={}", primary_album_storage); - LOG_INFO(Service_SET, "called, primary_album_storage={}", - m_system_settings.primary_album_storage); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.primary_album_storage = primary_album_storage; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetBatteryLot(HLERequestContext& ctx) { - BatteryLot battery_lot = {"YUZUEMULATOR123456789"}; - +Result ISystemSettingsServer::GetBatteryLot(Out<BatteryLot> out_battery_lot) { LOG_INFO(Service_SET, "called"); - IPC::ResponseBuilder rb{ctx, 8}; - rb.Push(ResultSuccess); - rb.PushRaw(battery_lot); + *out_battery_lot = {"YUZU0EMULATOR14022024"}; + R_SUCCEED(); } -void ISystemSettingsServer::GetSerialNumber(HLERequestContext& ctx) { - SerialNumber console_serial = {"YUZ10012345678"}; - +Result ISystemSettingsServer::GetSerialNumber(Out<SerialNumber> out_console_serial) { LOG_INFO(Service_SET, "called"); - IPC::ResponseBuilder rb{ctx, 8}; - rb.Push(ResultSuccess); - rb.PushRaw(console_serial); + *out_console_serial = {"YUZ10000000001"}; + R_SUCCEED(); } -void ISystemSettingsServer::GetNfcEnableFlag(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetNfcEnableFlag(Out<bool> out_nfc_enable_flag) { LOG_INFO(Service_SET, "called, nfc_enable_flag={}", m_system_settings.nfc_enable_flag); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(m_system_settings.nfc_enable_flag); + *out_nfc_enable_flag = m_system_settings.nfc_enable_flag; + R_SUCCEED(); } -void ISystemSettingsServer::SetNfcEnableFlag(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.nfc_enable_flag = rp.Pop<bool>(); - SetSaveNeeded(); - - LOG_INFO(Service_SET, "called, nfc_enable_flag={}", m_system_settings.nfc_enable_flag); +Result ISystemSettingsServer::SetNfcEnableFlag(bool nfc_enable_flag) { + LOG_INFO(Service_SET, "called, nfc_enable_flag={}", nfc_enable_flag); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.nfc_enable_flag = nfc_enable_flag; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetSleepSettings(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetSleepSettings(Out<SleepSettings> out_sleep_settings) { LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}", m_system_settings.sleep_settings.flags.raw, m_system_settings.sleep_settings.handheld_sleep_plan, m_system_settings.sleep_settings.console_sleep_plan); - IPC::ResponseBuilder rb{ctx, 5}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.sleep_settings); + *out_sleep_settings = m_system_settings.sleep_settings; + R_SUCCEED(); } -void ISystemSettingsServer::SetSleepSettings(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.sleep_settings = rp.PopRaw<SleepSettings>(); - SetSaveNeeded(); - +Result ISystemSettingsServer::SetSleepSettings(SleepSettings sleep_settings) { LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}", - m_system_settings.sleep_settings.flags.raw, - m_system_settings.sleep_settings.handheld_sleep_plan, - m_system_settings.sleep_settings.console_sleep_plan); + sleep_settings.flags.raw, sleep_settings.handheld_sleep_plan, + sleep_settings.console_sleep_plan); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.sleep_settings = sleep_settings; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetWirelessLanEnableFlag(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetWirelessLanEnableFlag(Out<bool> out_wireless_lan_enable_flag) { LOG_INFO(Service_SET, "called, wireless_lan_enable_flag={}", m_system_settings.wireless_lan_enable_flag); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.wireless_lan_enable_flag); + *out_wireless_lan_enable_flag = m_system_settings.wireless_lan_enable_flag; + R_SUCCEED(); } -void ISystemSettingsServer::SetWirelessLanEnableFlag(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.wireless_lan_enable_flag = rp.Pop<bool>(); - SetSaveNeeded(); - - LOG_INFO(Service_SET, "called, wireless_lan_enable_flag={}", - m_system_settings.wireless_lan_enable_flag); +Result ISystemSettingsServer::SetWirelessLanEnableFlag(bool wireless_lan_enable_flag) { + LOG_INFO(Service_SET, "called, wireless_lan_enable_flag={}", wireless_lan_enable_flag); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.wireless_lan_enable_flag = wireless_lan_enable_flag; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetInitialLaunchSettings(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetInitialLaunchSettings( + Out<InitialLaunchSettings> out_initial_launch_settings) { LOG_INFO(Service_SET, "called, flags={}, timestamp={}", m_system_settings.initial_launch_settings_packed.flags.raw, m_system_settings.initial_launch_settings_packed.timestamp.time_point); - IPC::ResponseBuilder rb{ctx, 10}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.initial_launch_settings_packed); + *out_initial_launch_settings = { + .flags = m_system_settings.initial_launch_settings_packed.flags, + .timestamp = m_system_settings.initial_launch_settings_packed.timestamp, + }; + R_SUCCEED(); } -void ISystemSettingsServer::SetInitialLaunchSettings(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - auto initial_launch_settings = rp.PopRaw<InitialLaunchSettings>(); +Result ISystemSettingsServer::SetInitialLaunchSettings( + InitialLaunchSettings initial_launch_settings) { + LOG_INFO(Service_SET, "called, flags={}, timestamp={}", initial_launch_settings.flags.raw, + initial_launch_settings.timestamp.time_point); m_system_settings.initial_launch_settings_packed.flags = initial_launch_settings.flags; m_system_settings.initial_launch_settings_packed.timestamp = initial_launch_settings.timestamp; SetSaveNeeded(); - - LOG_INFO(Service_SET, "called, flags={}, timestamp={}", - m_system_settings.initial_launch_settings_packed.flags.raw, - m_system_settings.initial_launch_settings_packed.timestamp.time_point); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } -void ISystemSettingsServer::GetDeviceNickName(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetDeviceNickName( + OutLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> out_device_name) { LOG_DEBUG(Service_SET, "called"); - ctx.WriteBuffer(::Settings::values.device_name.GetValue()); + *out_device_name = {}; + const auto device_name_buffer = ::Settings::values.device_name.GetValue().c_str(); + memcpy(out_device_name->data(), device_name_buffer, + ::Settings::values.device_name.GetValue().size()); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } -void ISystemSettingsServer::SetDeviceNickName(HLERequestContext& ctx) { - const std::string device_name = Common::StringFromBuffer(ctx.ReadBuffer()); +Result ISystemSettingsServer::SetDeviceNickName( + InLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> device_name_buffer) { + const std::string device_name = Common::StringFromBuffer(*device_name_buffer); LOG_INFO(Service_SET, "called, device_name={}", device_name); ::Settings::values.device_name = device_name; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } -void ISystemSettingsServer::GetProductModel(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetProductModel(Out<u32> out_product_model) { const u32 product_model = 1; LOG_WARNING(Service_SET, "(STUBBED) called, product_model={}", product_model); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(product_model); + + *out_product_model = product_model; + R_SUCCEED(); } -void ISystemSettingsServer::GetBluetoothEnableFlag(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetBluetoothEnableFlag(Out<bool> out_bluetooth_enable_flag) { LOG_INFO(Service_SET, "called, bluetooth_enable_flag={}", m_system_settings.bluetooth_enable_flag); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(m_system_settings.bluetooth_enable_flag); + *out_bluetooth_enable_flag = m_system_settings.bluetooth_enable_flag; + R_SUCCEED(); } -void ISystemSettingsServer::SetBluetoothEnableFlag(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.bluetooth_enable_flag = rp.Pop<bool>(); - SetSaveNeeded(); +Result ISystemSettingsServer::SetBluetoothEnableFlag(bool bluetooth_enable_flag) { + LOG_INFO(Service_SET, "called, bluetooth_enable_flag={}", bluetooth_enable_flag); - LOG_INFO(Service_SET, "called, bluetooth_enable_flag={}", - m_system_settings.bluetooth_enable_flag); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.bluetooth_enable_flag = bluetooth_enable_flag; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetMiiAuthorId(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetMiiAuthorId(Out<Common::UUID> out_mii_author_id) { if (m_system_settings.mii_author_id.IsInvalid()) { m_system_settings.mii_author_id = Common::UUID::MakeDefault(); SetSaveNeeded(); @@ -1166,282 +1060,224 @@ void ISystemSettingsServer::GetMiiAuthorId(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called, author_id={}", m_system_settings.mii_author_id.FormattedString()); - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(m_system_settings.mii_author_id); + *out_mii_author_id = m_system_settings.mii_author_id; + R_SUCCEED(); } -void ISystemSettingsServer::GetAutoUpdateEnableFlag(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetAutoUpdateEnableFlag(Out<bool> out_auto_update_enable_flag) { LOG_INFO(Service_SET, "called, auto_update_flag={}", m_system_settings.auto_update_enable_flag); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.auto_update_enable_flag); + *out_auto_update_enable_flag = m_system_settings.auto_update_enable_flag; + R_SUCCEED(); } -void ISystemSettingsServer::SetAutoUpdateEnableFlag(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.auto_update_enable_flag = rp.Pop<bool>(); - SetSaveNeeded(); +Result ISystemSettingsServer::SetAutoUpdateEnableFlag(bool auto_update_enable_flag) { + LOG_INFO(Service_SET, "called, auto_update_flag={}", auto_update_enable_flag); - LOG_INFO(Service_SET, "called, auto_update_flag={}", m_system_settings.auto_update_enable_flag); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.auto_update_enable_flag = auto_update_enable_flag; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetBatteryPercentageFlag(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetBatteryPercentageFlag(Out<bool> out_battery_percentage_flag) { LOG_DEBUG(Service_SET, "called, battery_percentage_flag={}", m_system_settings.battery_percentage_flag); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.battery_percentage_flag); + *out_battery_percentage_flag = m_system_settings.battery_percentage_flag; + R_SUCCEED(); } -void ISystemSettingsServer::SetBatteryPercentageFlag(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.battery_percentage_flag = rp.Pop<bool>(); - SetSaveNeeded(); - - LOG_INFO(Service_SET, "called, battery_percentage_flag={}", - m_system_settings.battery_percentage_flag); +Result ISystemSettingsServer::SetBatteryPercentageFlag(bool battery_percentage_flag) { + LOG_INFO(Service_SET, "called, battery_percentage_flag={}", battery_percentage_flag); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.battery_percentage_flag = battery_percentage_flag; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::SetExternalSteadyClockInternalOffset(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called."); - - IPC::RequestParser rp{ctx}; - auto offset{rp.Pop<s64>()}; - - const auto res = SetExternalSteadyClockInternalOffset(offset); +Result ISystemSettingsServer::SetExternalSteadyClockInternalOffset(s64 offset) { + LOG_DEBUG(Service_SET, "called, external_steady_clock_internal_offset={}", offset); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + m_private_settings.external_steady_clock_internal_offset = offset; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetExternalSteadyClockInternalOffset(HLERequestContext& ctx) { - LOG_DEBUG(Service_SET, "called."); +Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(Out<s64> out_offset) { + LOG_DEBUG(Service_SET, "called, external_steady_clock_internal_offset={}", + m_private_settings.external_steady_clock_internal_offset); - s64 offset{}; - const auto res = GetExternalSteadyClockInternalOffset(offset); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.Push(offset); + *out_offset = m_private_settings.external_steady_clock_internal_offset; + R_SUCCEED(); } -void ISystemSettingsServer::GetPushNotificationActivityModeOnSleep(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetPushNotificationActivityModeOnSleep( + Out<s32> out_push_notification_activity_mode_on_sleep) { LOG_INFO(Service_SET, "called, push_notification_activity_mode_on_sleep={}", m_system_settings.push_notification_activity_mode_on_sleep); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.push_notification_activity_mode_on_sleep); + *out_push_notification_activity_mode_on_sleep = + m_system_settings.push_notification_activity_mode_on_sleep; + R_SUCCEED(); } -void ISystemSettingsServer::SetPushNotificationActivityModeOnSleep(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.push_notification_activity_mode_on_sleep = rp.Pop<s32>(); - SetSaveNeeded(); - +Result ISystemSettingsServer::SetPushNotificationActivityModeOnSleep( + s32 push_notification_activity_mode_on_sleep) { LOG_INFO(Service_SET, "called, push_notification_activity_mode_on_sleep={}", - m_system_settings.push_notification_activity_mode_on_sleep); + push_notification_activity_mode_on_sleep); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.push_notification_activity_mode_on_sleep = + push_notification_activity_mode_on_sleep; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetErrorReportSharePermission(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetErrorReportSharePermission( + Out<ErrorReportSharePermission> out_error_report_share_permission) { LOG_INFO(Service_SET, "called, error_report_share_permission={}", m_system_settings.error_report_share_permission); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(m_system_settings.error_report_share_permission); + *out_error_report_share_permission = m_system_settings.error_report_share_permission; + R_SUCCEED(); } -void ISystemSettingsServer::SetErrorReportSharePermission(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.error_report_share_permission = rp.PopEnum<ErrorReportSharePermission>(); - SetSaveNeeded(); - +Result ISystemSettingsServer::SetErrorReportSharePermission( + ErrorReportSharePermission error_report_share_permission) { LOG_INFO(Service_SET, "called, error_report_share_permission={}", - m_system_settings.error_report_share_permission); + error_report_share_permission); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.error_report_share_permission = error_report_share_permission; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetAppletLaunchFlags(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetAppletLaunchFlags(Out<u32> out_applet_launch_flag) { LOG_INFO(Service_SET, "called, applet_launch_flag={}", m_system_settings.applet_launch_flag); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.applet_launch_flag); + *out_applet_launch_flag = m_system_settings.applet_launch_flag; + R_SUCCEED(); } -void ISystemSettingsServer::SetAppletLaunchFlags(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.applet_launch_flag = rp.Pop<u32>(); - SetSaveNeeded(); - - LOG_INFO(Service_SET, "called, applet_launch_flag={}", m_system_settings.applet_launch_flag); +Result ISystemSettingsServer::SetAppletLaunchFlags(u32 applet_launch_flag) { + LOG_INFO(Service_SET, "called, applet_launch_flag={}", applet_launch_flag); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.applet_launch_flag = applet_launch_flag; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetKeyboardLayout(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetKeyboardLayout(Out<KeyboardLayout> out_keyboard_layout) { LOG_INFO(Service_SET, "called, keyboard_layout={}", m_system_settings.keyboard_layout); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(m_system_settings.keyboard_layout)); + *out_keyboard_layout = m_system_settings.keyboard_layout; + R_SUCCEED(); } -void ISystemSettingsServer::SetKeyboardLayout(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.keyboard_layout = rp.PopRaw<KeyboardLayout>(); - SetSaveNeeded(); - - LOG_INFO(Service_SET, "called, keyboard_layout={}", m_system_settings.keyboard_layout); +Result ISystemSettingsServer::SetKeyboardLayout(KeyboardLayout keyboard_layout) { + LOG_INFO(Service_SET, "called, keyboard_layout={}", keyboard_layout); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.keyboard_layout = keyboard_layout; + R_SUCCEED(); } -void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime( + Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) { LOG_INFO(Service_SET, "called"); - Service::PSC::Time::SteadyClockTimePoint time_point{}; - const auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point); + *out_time_point = m_system_settings.device_time_zone_location_updated_time; + R_SUCCEED(); } -void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { +Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime( + const Service::PSC::Time::SteadyClockTimePoint& time_point) { LOG_INFO(Service_SET, "called"); - IPC::RequestParser rp{ctx}; - auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()}; - - const auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + m_system_settings.device_time_zone_location_updated_time = time_point; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( - HLERequestContext& ctx) { +Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( + Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) { LOG_INFO(Service_SET, "called"); - Service::PSC::Time::SteadyClockTimePoint time_point{}; - const auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point); + *out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point; + R_SUCCEED(); } -void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( - HLERequestContext& ctx) { +Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( + const Service::PSC::Time::SteadyClockTimePoint& out_time_point) { LOG_INFO(Service_SET, "called"); - IPC::RequestParser rp{ctx}; - const auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()}; - - const auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetChineseTraditionalInputMethod(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetChineseTraditionalInputMethod( + Out<ChineseTraditionalInputMethod> out_chinese_traditional_input_method) { LOG_INFO(Service_SET, "called, chinese_traditional_input_method={}", m_system_settings.chinese_traditional_input_method); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(m_system_settings.chinese_traditional_input_method); + *out_chinese_traditional_input_method = m_system_settings.chinese_traditional_input_method; + R_SUCCEED(); } -void ISystemSettingsServer::GetHomeMenuScheme(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetHomeMenuScheme(Out<HomeMenuScheme> out_home_menu_scheme) { LOG_DEBUG(Service_SET, "(STUBBED) called"); - const HomeMenuScheme default_color = { + *out_home_menu_scheme = { .main = 0xFF323232, .back = 0xFF323232, .sub = 0xFFFFFFFF, .bezel = 0xFFFFFFFF, .extra = 0xFF000000, }; - - IPC::ResponseBuilder rb{ctx, 2 + sizeof(HomeMenuScheme) / sizeof(u32)}; - rb.Push(ResultSuccess); - rb.PushRaw(default_color); + R_SUCCEED(); } -void ISystemSettingsServer::GetHomeMenuSchemeModel(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetHomeMenuSchemeModel(Out<u32> out_home_menu_scheme_model) { LOG_WARNING(Service_SET, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); + *out_home_menu_scheme_model = 0; + R_SUCCEED(); } -void ISystemSettingsServer::GetTouchScreenMode(HLERequestContext& ctx) { - TouchScreenMode touch_screen_mode{}; - auto res = GetTouchScreenMode(touch_screen_mode); +Result ISystemSettingsServer::GetTouchScreenMode(Out<TouchScreenMode> out_touch_screen_mode) { + LOG_INFO(Service_SET, "called, touch_screen_mode={}", m_system_settings.touch_screen_mode); - LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(res); - rb.PushEnum(touch_screen_mode); + *out_touch_screen_mode = m_system_settings.touch_screen_mode; + R_SUCCEED(); } -void ISystemSettingsServer::SetTouchScreenMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto touch_screen_mode = rp.PopEnum<TouchScreenMode>(); - auto res = SetTouchScreenMode(touch_screen_mode); - +Result ISystemSettingsServer::SetTouchScreenMode(TouchScreenMode touch_screen_mode) { LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); + m_system_settings.touch_screen_mode = touch_screen_mode; + SetSaveNeeded(); + R_SUCCEED(); } -void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetFieldTestingFlag(Out<bool> out_field_testing_flag) { LOG_INFO(Service_SET, "called, field_testing_flag={}", m_system_settings.field_testing_flag); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.field_testing_flag); + *out_field_testing_flag = m_system_settings.field_testing_flag; + R_SUCCEED(); } -void ISystemSettingsServer::GetPanelCrcMode(HLERequestContext& ctx) { +Result ISystemSettingsServer::GetPanelCrcMode(Out<s32> out_panel_crc_mode) { LOG_INFO(Service_SET, "called, panel_crc_mode={}", m_system_settings.panel_crc_mode); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(m_system_settings.panel_crc_mode); + *out_panel_crc_mode = m_system_settings.panel_crc_mode; + R_SUCCEED(); } -void ISystemSettingsServer::SetPanelCrcMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - m_system_settings.panel_crc_mode = rp.PopRaw<s32>(); - SetSaveNeeded(); +Result ISystemSettingsServer::SetPanelCrcMode(s32 panel_crc_mode) { + LOG_INFO(Service_SET, "called, panel_crc_mode={}", panel_crc_mode); - LOG_INFO(Service_SET, "called, panel_crc_mode={}", m_system_settings.panel_crc_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + m_system_settings.panel_crc_mode = panel_crc_mode; + SetSaveNeeded(); + R_SUCCEED(); } void ISystemSettingsServer::SetupSettings() { @@ -1513,9 +1349,9 @@ void ISystemSettingsServer::SetSaveNeeded() { m_save_needed = true; } -Result ISystemSettingsServer::GetSettingsItemValue(std::vector<u8>& out_value, - const std::string& category, - const std::string& name) { +Result ISystemSettingsServer::GetSettingsItemValueImpl(std::vector<u8>& out_value, + const std::string& category, + const std::string& name) { auto settings{GetSettings()}; R_UNLESS(settings.contains(category) && settings[category].contains(name), ResultUnknown); @@ -1523,184 +1359,4 @@ Result ISystemSettingsServer::GetSettingsItemValue(std::vector<u8>& out_value, R_SUCCEED(); } -Result ISystemSettingsServer::GetVibrationMasterVolume(f32& out_volume) const { - out_volume = m_system_settings.vibration_master_volume; - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetVibrationMasterVolume(f32 volume) { - m_system_settings.vibration_master_volume = volume; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result ISystemSettingsServer::GetAudioOutputMode(AudioOutputMode& out_output_mode, - AudioOutputModeTarget target) const { - switch (target) { - case AudioOutputModeTarget::Hdmi: - out_output_mode = m_system_settings.audio_output_mode_hdmi; - break; - case AudioOutputModeTarget::Speaker: - out_output_mode = m_system_settings.audio_output_mode_speaker; - break; - case AudioOutputModeTarget::Headphone: - out_output_mode = m_system_settings.audio_output_mode_headphone; - break; - case AudioOutputModeTarget::Type3: - out_output_mode = m_system_settings.audio_output_mode_type3; - break; - case AudioOutputModeTarget::Type4: - out_output_mode = m_system_settings.audio_output_mode_type4; - break; - default: - LOG_ERROR(Service_SET, "Invalid audio output mode target {}", target); - } - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetAudioOutputMode(AudioOutputModeTarget target, - AudioOutputMode output_mode) { - switch (target) { - case AudioOutputModeTarget::Hdmi: - m_system_settings.audio_output_mode_hdmi = output_mode; - break; - case AudioOutputModeTarget::Speaker: - m_system_settings.audio_output_mode_speaker = output_mode; - break; - case AudioOutputModeTarget::Headphone: - m_system_settings.audio_output_mode_headphone = output_mode; - break; - case AudioOutputModeTarget::Type3: - m_system_settings.audio_output_mode_type3 = output_mode; - break; - case AudioOutputModeTarget::Type4: - m_system_settings.audio_output_mode_type4 = output_mode; - break; - default: - LOG_ERROR(Service_SET, "Invalid audio output mode target {}", target); - } - SetSaveNeeded(); - R_SUCCEED(); -} - -Result ISystemSettingsServer::GetSpeakerAutoMuteFlag(bool& is_auto_mute) const { - is_auto_mute = m_system_settings.force_mute_on_headphone_removed; - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetSpeakerAutoMuteFlag(bool is_auto_mute) { - m_system_settings.force_mute_on_headphone_removed = is_auto_mute; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result ISystemSettingsServer::GetExternalSteadyClockSourceId(Common::UUID& out_id) const { - out_id = m_private_settings.external_clock_source_id; - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetExternalSteadyClockSourceId(const Common::UUID& id) { - m_private_settings.external_clock_source_id = id; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result ISystemSettingsServer::GetUserSystemClockContext( - Service::PSC::Time::SystemClockContext& out_context) const { - out_context = m_system_settings.user_system_clock_context; - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetUserSystemClockContext( - const Service::PSC::Time::SystemClockContext& context) { - m_system_settings.user_system_clock_context = context; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result ISystemSettingsServer::GetDeviceTimeZoneLocationName( - Service::PSC::Time::LocationName& out_name) const { - out_name = m_system_settings.device_time_zone_location_name; - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetDeviceTimeZoneLocationName( - const Service::PSC::Time::LocationName& name) { - m_system_settings.device_time_zone_location_name = name; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result ISystemSettingsServer::GetNetworkSystemClockContext( - Service::PSC::Time::SystemClockContext& out_context) const { - out_context = m_system_settings.network_system_clock_context; - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetNetworkSystemClockContext( - const Service::PSC::Time::SystemClockContext& context) { - m_system_settings.network_system_clock_context = context; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const { - out_enabled = m_system_settings.user_system_clock_automatic_correction_enabled; - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(bool enabled) { - m_system_settings.user_system_clock_automatic_correction_enabled = enabled; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetExternalSteadyClockInternalOffset(s64 offset) { - m_private_settings.external_steady_clock_internal_offset = offset; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offset) const { - out_offset = m_private_settings.external_steady_clock_internal_offset; - R_SUCCEED(); -} - -Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint& out_time_point) const { - out_time_point = m_system_settings.device_time_zone_location_updated_time; - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime( - const Service::PSC::Time::SteadyClockTimePoint& time_point) { - m_system_settings.device_time_zone_location_updated_time = time_point; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint& out_time_point) const { - out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point; - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( - const Service::PSC::Time::SteadyClockTimePoint& out_time_point) { - m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point; - SetSaveNeeded(); - R_SUCCEED(); -} - -Result ISystemSettingsServer::GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const { - touch_screen_mode = m_system_settings.touch_screen_mode; - R_SUCCEED(); -} - -Result ISystemSettingsServer::SetTouchScreenMode(TouchScreenMode touch_screen_mode) { - m_system_settings.touch_screen_mode = touch_screen_mode; - SetSaveNeeded(); - R_SUCCEED(); -} - } // namespace Service::Set diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h index 9a3b36f0c..46e06c8ea 100644 --- a/src/core/hle/service/set/system_settings_server.h +++ b/src/core/hle/service/set/system_settings_server.h @@ -11,6 +11,7 @@ #include "common/polyfill_thread.h" #include "common/uuid.h" #include "core/hle/result.h" +#include "core/hle/service/cmif_types.h" #include "core/hle/service/psc/time/common.h" #include "core/hle/service/service.h" #include "core/hle/service/set/setting_formats/appln_settings.h" @@ -33,13 +34,14 @@ public: explicit ISystemSettingsServer(Core::System& system_); ~ISystemSettingsServer() override; - Result GetSettingsItemValue(std::vector<u8>& out_value, const std::string& category, - const std::string& name); + Result GetSettingsItemValueImpl(std::vector<u8>& out_value, const std::string& category, + const std::string& name); template <typename T> - Result GetSettingsItemValue(T& value, const std::string& category, const std::string& name) { + Result GetSettingsItemValueImpl(T& value, const std::string& category, + const std::string& name) { std::vector<u8> data; - const auto result = GetSettingsItemValue(data, category, name); + const auto result = GetSettingsItemValueImpl(data, category, name); if (result.IsError()) { return result; } @@ -48,120 +50,114 @@ public: return result; } - Result GetVibrationMasterVolume(f32& out_volume) const; - Result SetVibrationMasterVolume(f32 volume); - Result GetAudioOutputMode(AudioOutputMode& out_output_mode, AudioOutputModeTarget target) const; +public: + Result SetLanguageCode(LanguageCode language_code); + Result GetFirmwareVersion( + OutLargeData<FirmwareVersionFormat, BufferAttr_HipcPointer> out_firmware_data); + Result GetFirmwareVersion2( + OutLargeData<FirmwareVersionFormat, BufferAttr_HipcPointer> out_firmware_data); + Result GetLockScreenFlag(Out<bool> out_lock_screen_flag); + Result SetLockScreenFlag(bool lock_screen_flag); + Result GetExternalSteadyClockSourceId(Out<Common::UUID> out_clock_source_id); + Result SetExternalSteadyClockSourceId(const Common::UUID& clock_source_id); + Result GetUserSystemClockContext(Out<Service::PSC::Time::SystemClockContext> out_clock_context); + Result SetUserSystemClockContext(const Service::PSC::Time::SystemClockContext& clock_context); + Result GetAccountSettings(Out<AccountSettings> out_account_settings); + Result SetAccountSettings(AccountSettings account_settings); + Result GetEulaVersions(Out<s32> out_count, + OutArray<EulaVersion, BufferAttr_HipcMapAlias> out_eula_versions); + Result SetEulaVersions(InArray<EulaVersion, BufferAttr_HipcMapAlias> eula_versions); + Result GetColorSetId(Out<ColorSet> out_color_set_id); + Result SetColorSetId(ColorSet color_set_id); + Result GetNotificationSettings(Out<NotificationSettings> out_notification_settings); + Result SetNotificationSettings(const NotificationSettings& notification_settings); + Result GetAccountNotificationSettings( + Out<s32> out_count, OutArray<AccountNotificationSettings, BufferAttr_HipcMapAlias> + out_account_notification_settings); + Result SetAccountNotificationSettings( + InArray<AccountNotificationSettings, BufferAttr_HipcMapAlias> + account_notification_settings); + Result GetVibrationMasterVolume(Out<f32> vibration_master_volume); + Result SetVibrationMasterVolume(f32 vibration_master_volume); + Result GetSettingsItemValueSize( + Out<u64> out_size, + InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_category_buffer, + InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_name_buf); + Result GetSettingsItemValue( + OutBuffer<BufferAttr_HipcMapAlias> out_data, + InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_category_buffer, + InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_name_buffer); + Result GetTvSettings(Out<TvSettings> out_tv_settings); + Result SetTvSettings(TvSettings tv_settings); + Result GetAudioOutputMode(Out<AudioOutputMode> out_output_mode, AudioOutputModeTarget target); Result SetAudioOutputMode(AudioOutputModeTarget target, AudioOutputMode output_mode); - Result GetSpeakerAutoMuteFlag(bool& is_auto_mute) const; - Result SetSpeakerAutoMuteFlag(bool auto_mute); - Result GetExternalSteadyClockSourceId(Common::UUID& out_id) const; - Result SetExternalSteadyClockSourceId(const Common::UUID& id); - Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const; - Result SetUserSystemClockContext(const Service::PSC::Time::SystemClockContext& context); - Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name) const; + Result GetSpeakerAutoMuteFlag(Out<bool> out_force_mute_on_headphone_removed); + Result SetSpeakerAutoMuteFlag(bool force_mute_on_headphone_removed); + Result GetQuestFlag(Out<QuestFlag> out_quest_flag); + Result SetQuestFlag(QuestFlag quest_flag); + Result GetDeviceTimeZoneLocationName(Out<Service::PSC::Time::LocationName> out_name); Result SetDeviceTimeZoneLocationName(const Service::PSC::Time::LocationName& name); - Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const; + Result SetRegionCode(SystemRegionCode region_code); + Result GetNetworkSystemClockContext(Out<Service::PSC::Time::SystemClockContext> out_context); Result SetNetworkSystemClockContext(const Service::PSC::Time::SystemClockContext& context); - Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const; - Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled); + Result IsUserSystemClockAutomaticCorrectionEnabled(Out<bool> out_automatic_correction_enabled); + Result SetUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction_enabled); + Result GetDebugModeFlag(Out<bool> is_debug_mode_enabled); + Result GetPrimaryAlbumStorage(Out<PrimaryAlbumStorage> out_primary_album_storage); + Result SetPrimaryAlbumStorage(PrimaryAlbumStorage primary_album_storage); + Result GetBatteryLot(Out<BatteryLot> out_battery_lot); + Result GetSerialNumber(Out<SerialNumber> out_console_serial); + Result GetNfcEnableFlag(Out<bool> out_nfc_enable_flag); + Result SetNfcEnableFlag(bool nfc_enable_flag); + Result GetSleepSettings(Out<SleepSettings> out_sleep_settings); + Result SetSleepSettings(SleepSettings sleep_settings); + Result GetWirelessLanEnableFlag(Out<bool> out_wireless_lan_enable_flag); + Result SetWirelessLanEnableFlag(bool wireless_lan_enable_flag); + Result GetInitialLaunchSettings(Out<InitialLaunchSettings> out_initial_launch_settings); + Result SetInitialLaunchSettings(InitialLaunchSettings initial_launch_settings); + Result GetDeviceNickName( + OutLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> out_device_name); + Result SetDeviceNickName( + InLargeData<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> device_name_buffer); + Result GetProductModel(Out<u32> out_product_model); + Result GetBluetoothEnableFlag(Out<bool> out_bluetooth_enable_flag); + Result SetBluetoothEnableFlag(bool bluetooth_enable_flag); + Result GetMiiAuthorId(Out<Common::UUID> out_mii_author_id); + Result GetAutoUpdateEnableFlag(Out<bool> out_auto_update_enable_flag); + Result SetAutoUpdateEnableFlag(bool auto_update_enable_flag); + Result GetBatteryPercentageFlag(Out<bool> out_battery_percentage_flag); + Result SetBatteryPercentageFlag(bool battery_percentage_flag); Result SetExternalSteadyClockInternalOffset(s64 offset); - Result GetExternalSteadyClockInternalOffset(s64& out_offset) const; + Result GetExternalSteadyClockInternalOffset(Out<s64> out_offset); + Result GetPushNotificationActivityModeOnSleep( + Out<s32> out_push_notification_activity_mode_on_sleep); + Result SetPushNotificationActivityModeOnSleep(s32 push_notification_activity_mode_on_sleep); + Result GetErrorReportSharePermission( + Out<ErrorReportSharePermission> out_error_report_share_permission); + Result SetErrorReportSharePermission(ErrorReportSharePermission error_report_share_permission); + Result GetAppletLaunchFlags(Out<u32> out_applet_launch_flag); + Result SetAppletLaunchFlags(u32 applet_launch_flag); + Result GetKeyboardLayout(Out<KeyboardLayout> out_keyboard_layout); + Result SetKeyboardLayout(KeyboardLayout keyboard_layout); Result GetDeviceTimeZoneLocationUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; + Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point); Result SetDeviceTimeZoneLocationUpdatedTime( const Service::PSC::Time::SteadyClockTimePoint& time_point); Result GetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; + Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point); Result SetUserSystemClockAutomaticCorrectionUpdatedTime( - const Service::PSC::Time::SteadyClockTimePoint& time_point); - Result GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const; + const Service::PSC::Time::SteadyClockTimePoint& out_time_point); + Result GetChineseTraditionalInputMethod( + Out<ChineseTraditionalInputMethod> out_chinese_traditional_input_method); + Result GetHomeMenuScheme(Out<HomeMenuScheme> out_home_menu_scheme); + Result GetHomeMenuSchemeModel(Out<u32> out_home_menu_scheme_model); + Result GetTouchScreenMode(Out<TouchScreenMode> out_touch_screen_mode); Result SetTouchScreenMode(TouchScreenMode touch_screen_mode); + Result GetFieldTestingFlag(Out<bool> out_field_testing_flag); + Result GetPanelCrcMode(Out<s32> out_panel_crc_mode); + Result SetPanelCrcMode(s32 panel_crc_mode); private: - void SetLanguageCode(HLERequestContext& ctx); - void GetFirmwareVersion(HLERequestContext& ctx); - void GetFirmwareVersion2(HLERequestContext& ctx); - void GetLockScreenFlag(HLERequestContext& ctx); - void SetLockScreenFlag(HLERequestContext& ctx); - void GetExternalSteadyClockSourceId(HLERequestContext& ctx); - void SetExternalSteadyClockSourceId(HLERequestContext& ctx); - void GetUserSystemClockContext(HLERequestContext& ctx); - void SetUserSystemClockContext(HLERequestContext& ctx); - void GetAccountSettings(HLERequestContext& ctx); - void SetAccountSettings(HLERequestContext& ctx); - void GetEulaVersions(HLERequestContext& ctx); - void SetEulaVersions(HLERequestContext& ctx); - void GetColorSetId(HLERequestContext& ctx); - void SetColorSetId(HLERequestContext& ctx); - void GetNotificationSettings(HLERequestContext& ctx); - void SetNotificationSettings(HLERequestContext& ctx); - void GetAccountNotificationSettings(HLERequestContext& ctx); - void SetAccountNotificationSettings(HLERequestContext& ctx); - void GetVibrationMasterVolume(HLERequestContext& ctx); - void SetVibrationMasterVolume(HLERequestContext& ctx); - void GetSettingsItemValueSize(HLERequestContext& ctx); - void GetSettingsItemValue(HLERequestContext& ctx); - void GetTvSettings(HLERequestContext& ctx); - void SetTvSettings(HLERequestContext& ctx); - void GetAudioOutputMode(HLERequestContext& ctx); - void SetAudioOutputMode(HLERequestContext& ctx); - void GetSpeakerAutoMuteFlag(HLERequestContext& ctx); - void SetSpeakerAutoMuteFlag(HLERequestContext& ctx); - void GetDebugModeFlag(HLERequestContext& ctx); - void GetQuestFlag(HLERequestContext& ctx); - void SetQuestFlag(HLERequestContext& ctx); - void GetDeviceTimeZoneLocationName(HLERequestContext& ctx); - void SetDeviceTimeZoneLocationName(HLERequestContext& ctx); - void SetRegionCode(HLERequestContext& ctx); - void GetNetworkSystemClockContext(HLERequestContext& ctx); - void SetNetworkSystemClockContext(HLERequestContext& ctx); - void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); - void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); - void GetPrimaryAlbumStorage(HLERequestContext& ctx); - void SetPrimaryAlbumStorage(HLERequestContext& ctx); - void GetBatteryLot(HLERequestContext& ctx); - void GetSerialNumber(HLERequestContext& ctx); - void GetNfcEnableFlag(HLERequestContext& ctx); - void SetNfcEnableFlag(HLERequestContext& ctx); - void GetSleepSettings(HLERequestContext& ctx); - void SetSleepSettings(HLERequestContext& ctx); - void GetWirelessLanEnableFlag(HLERequestContext& ctx); - void SetWirelessLanEnableFlag(HLERequestContext& ctx); - void GetInitialLaunchSettings(HLERequestContext& ctx); - void SetInitialLaunchSettings(HLERequestContext& ctx); - void GetDeviceNickName(HLERequestContext& ctx); - void SetDeviceNickName(HLERequestContext& ctx); - void GetProductModel(HLERequestContext& ctx); - void GetBluetoothEnableFlag(HLERequestContext& ctx); - void SetBluetoothEnableFlag(HLERequestContext& ctx); - void GetMiiAuthorId(HLERequestContext& ctx); - void GetAutoUpdateEnableFlag(HLERequestContext& ctx); - void SetAutoUpdateEnableFlag(HLERequestContext& ctx); - void GetBatteryPercentageFlag(HLERequestContext& ctx); - void SetBatteryPercentageFlag(HLERequestContext& ctx); - void SetExternalSteadyClockInternalOffset(HLERequestContext& ctx); - void GetExternalSteadyClockInternalOffset(HLERequestContext& ctx); - void GetPushNotificationActivityModeOnSleep(HLERequestContext& ctx); - void SetPushNotificationActivityModeOnSleep(HLERequestContext& ctx); - void GetErrorReportSharePermission(HLERequestContext& ctx); - void SetErrorReportSharePermission(HLERequestContext& ctx); - void GetAppletLaunchFlags(HLERequestContext& ctx); - void SetAppletLaunchFlags(HLERequestContext& ctx); - void GetKeyboardLayout(HLERequestContext& ctx); - void SetKeyboardLayout(HLERequestContext& ctx); - void GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx); - void SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx); - void GetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); - void SetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); - void GetChineseTraditionalInputMethod(HLERequestContext& ctx); - void GetHomeMenuScheme(HLERequestContext& ctx); - void GetHomeMenuSchemeModel(HLERequestContext& ctx); - void GetTouchScreenMode(HLERequestContext& ctx); - void SetTouchScreenMode(HLERequestContext& ctx); - void GetFieldTestingFlag(HLERequestContext& ctx); - void GetPanelCrcMode(HLERequestContext& ctx); - void SetPanelCrcMode(HLERequestContext& ctx); - bool LoadSettingsFile(std::filesystem::path& path, auto&& default_func); bool StoreSettingsFile(std::filesystem::path& path, auto& settings); void SetupSettings(); diff --git a/src/core/hle/service/vi/application_display_service.cpp b/src/core/hle/service/vi/application_display_service.cpp new file mode 100644 index 000000000..6b0bcb536 --- /dev/null +++ b/src/core/hle/service/vi/application_display_service.cpp @@ -0,0 +1,302 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/nvnflinger/hos_binder_driver.h" +#include "core/hle/service/nvnflinger/parcel.h" +#include "core/hle/service/os/event.h" +#include "core/hle/service/vi/application_display_service.h" +#include "core/hle/service/vi/container.h" +#include "core/hle/service/vi/manager_display_service.h" +#include "core/hle/service/vi/system_display_service.h" +#include "core/hle/service/vi/vi_results.h" + +namespace Service::VI { + +IApplicationDisplayService::IApplicationDisplayService(Core::System& system_, + std::shared_ptr<Container> container) + : ServiceFramework{system_, "IApplicationDisplayService"}, + m_container{std::move(container)}, m_context{system, "IApplicationDisplayService"} { + // clang-format off + static const FunctionInfo functions[] = { + {100, C<&IApplicationDisplayService::GetRelayService>, "GetRelayService"}, + {101, C<&IApplicationDisplayService::GetSystemDisplayService>, "GetSystemDisplayService"}, + {102, C<&IApplicationDisplayService::GetManagerDisplayService>, "GetManagerDisplayService"}, + {103, C<&IApplicationDisplayService::GetIndirectDisplayTransactionService>, "GetIndirectDisplayTransactionService"}, + {1000, C<&IApplicationDisplayService::ListDisplays>, "ListDisplays"}, + {1010, C<&IApplicationDisplayService::OpenDisplay>, "OpenDisplay"}, + {1011, C<&IApplicationDisplayService::OpenDefaultDisplay>, "OpenDefaultDisplay"}, + {1020, C<&IApplicationDisplayService::CloseDisplay>, "CloseDisplay"}, + {1101, C<&IApplicationDisplayService::SetDisplayEnabled>, "SetDisplayEnabled"}, + {1102, C<&IApplicationDisplayService::GetDisplayResolution>, "GetDisplayResolution"}, + {2020, C<&IApplicationDisplayService::OpenLayer>, "OpenLayer"}, + {2021, C<&IApplicationDisplayService::CloseLayer>, "CloseLayer"}, + {2030, C<&IApplicationDisplayService::CreateStrayLayer>, "CreateStrayLayer"}, + {2031, C<&IApplicationDisplayService::DestroyStrayLayer>, "DestroyStrayLayer"}, + {2101, C<&IApplicationDisplayService::SetLayerScalingMode>, "SetLayerScalingMode"}, + {2102, C<&IApplicationDisplayService::ConvertScalingMode>, "ConvertScalingMode"}, + {2450, C<&IApplicationDisplayService::GetIndirectLayerImageMap>, "GetIndirectLayerImageMap"}, + {2451, nullptr, "GetIndirectLayerImageCropMap"}, + {2460, C<&IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo>, "GetIndirectLayerImageRequiredMemoryInfo"}, + {5202, C<&IApplicationDisplayService::GetDisplayVsyncEvent>, "GetDisplayVsyncEvent"}, + {5203, nullptr, "GetDisplayVsyncEventForDebug"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IApplicationDisplayService::~IApplicationDisplayService() { + for (auto& [display_id, event] : m_display_vsync_events) { + m_container->UnlinkVsyncEvent(display_id, &event); + } + for (const auto layer_id : m_open_layer_ids) { + m_container->CloseLayer(layer_id); + } + for (const auto layer_id : m_stray_layer_ids) { + m_container->DestroyStrayLayer(layer_id); + } +} + +Result IApplicationDisplayService::GetRelayService( + Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_relay_service) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + R_RETURN(m_container->GetBinderDriver(out_relay_service)); +} + +Result IApplicationDisplayService::GetSystemDisplayService( + Out<SharedPointer<ISystemDisplayService>> out_system_display_service) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + *out_system_display_service = std::make_shared<ISystemDisplayService>(system, m_container); + R_SUCCEED(); +} + +Result IApplicationDisplayService::GetManagerDisplayService( + Out<SharedPointer<IManagerDisplayService>> out_manager_display_service) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + *out_manager_display_service = std::make_shared<IManagerDisplayService>(system, m_container); + R_SUCCEED(); +} + +Result IApplicationDisplayService::GetIndirectDisplayTransactionService( + Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_indirect_display_transaction_service) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + R_RETURN(m_container->GetBinderDriver(out_indirect_display_transaction_service)); +} + +Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayName display_name) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + + display_name[display_name.size() - 1] = '\0'; + ASSERT_MSG(strcmp(display_name.data(), "Default") == 0, + "Non-default displays aren't supported yet"); + + R_RETURN(m_container->OpenDisplay(out_display_id, display_name)); +} + +Result IApplicationDisplayService::OpenDefaultDisplay(Out<u64> out_display_id) { + LOG_DEBUG(Service_VI, "called"); + R_RETURN(this->OpenDisplay(out_display_id, DisplayName{"Default"})); +} + +Result IApplicationDisplayService::CloseDisplay(u64 display_id) { + LOG_DEBUG(Service_VI, "called"); + R_RETURN(m_container->CloseDisplay(display_id)); +} + +Result IApplicationDisplayService::SetDisplayEnabled(u32 state, u64 display_id) { + LOG_DEBUG(Service_VI, "called"); + + // This literally does nothing internally in the actual service itself, + // and just returns a successful result code regardless of the input. + R_SUCCEED(); +} + +Result IApplicationDisplayService::GetDisplayResolution(Out<s64> out_width, Out<s64> out_height, + u64 display_id) { + LOG_DEBUG(Service_VI, "called. display_id={}", display_id); + + // This only returns the fixed values of 1280x720 and makes no distinguishing + // between docked and undocked dimensions. + *out_width = static_cast<s64>(DisplayResolution::UndockedWidth); + *out_height = static_cast<s64>(DisplayResolution::UndockedHeight); + R_SUCCEED(); +} + +Result IApplicationDisplayService::SetLayerScalingMode(NintendoScaleMode scale_mode, u64 layer_id) { + LOG_DEBUG(Service_VI, "called. scale_mode={}, unknown=0x{:016X}", scale_mode, layer_id); + + if (scale_mode > NintendoScaleMode::PreserveAspectRatio) { + LOG_ERROR(Service_VI, "Invalid scaling mode provided."); + R_THROW(VI::ResultOperationFailed); + } + + if (scale_mode != NintendoScaleMode::ScaleToWindow && + scale_mode != NintendoScaleMode::PreserveAspectRatio) { + LOG_ERROR(Service_VI, "Unsupported scaling mode supplied."); + R_THROW(VI::ResultNotSupported); + } + + R_SUCCEED(); +} + +Result IApplicationDisplayService::ListDisplays( + Out<u64> out_count, OutArray<DisplayInfo, BufferAttr_HipcMapAlias> out_displays) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + + if (out_displays.size() > 0) { + out_displays[0] = DisplayInfo{}; + *out_count = 1; + } else { + *out_count = 0; + } + + R_SUCCEED(); +} + +Result IApplicationDisplayService::OpenLayer(Out<u64> out_size, + OutBuffer<BufferAttr_HipcMapAlias> out_native_window, + DisplayName display_name, u64 layer_id, + ClientAppletResourceUserId aruid) { + display_name[display_name.size() - 1] = '\0'; + + LOG_DEBUG(Service_VI, "called. layer_id={}, aruid={:#x}", layer_id, aruid.pid); + + u64 display_id; + R_TRY(m_container->OpenDisplay(&display_id, display_name)); + + s32 producer_binder_id; + R_TRY(m_container->OpenLayer(&producer_binder_id, layer_id, aruid.pid)); + + { + std::scoped_lock lk{m_lock}; + m_open_layer_ids.insert(layer_id); + } + + android::OutputParcel parcel; + parcel.WriteInterface(NativeWindow{producer_binder_id}); + + const auto buffer = parcel.Serialize(); + std::memcpy(out_native_window.data(), buffer.data(), + std::min(out_native_window.size(), buffer.size())); + *out_size = buffer.size(); + + R_SUCCEED(); +} + +Result IApplicationDisplayService::CloseLayer(u64 layer_id) { + LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id); + + { + std::scoped_lock lk{m_lock}; + R_UNLESS(m_open_layer_ids.contains(layer_id), VI::ResultNotFound); + m_open_layer_ids.erase(layer_id); + } + + R_RETURN(m_container->CloseLayer(layer_id)); +} + +Result IApplicationDisplayService::CreateStrayLayer( + Out<u64> out_layer_id, Out<u64> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_native_window, + u32 flags, u64 display_id) { + LOG_DEBUG(Service_VI, "called. flags={}, display_id={}", flags, display_id); + + s32 producer_binder_id; + R_TRY(m_container->CreateStrayLayer(&producer_binder_id, out_layer_id, display_id)); + + std::scoped_lock lk{m_lock}; + m_stray_layer_ids.insert(*out_layer_id); + + android::OutputParcel parcel; + parcel.WriteInterface(NativeWindow{producer_binder_id}); + + const auto buffer = parcel.Serialize(); + std::memcpy(out_native_window.data(), buffer.data(), + std::min(out_native_window.size(), buffer.size())); + + *out_size = buffer.size(); + + R_SUCCEED(); +} + +Result IApplicationDisplayService::DestroyStrayLayer(u64 layer_id) { + LOG_WARNING(Service_VI, "(STUBBED) called. layer_id={}", layer_id); + + { + std::scoped_lock lk{m_lock}; + R_UNLESS(m_stray_layer_ids.contains(layer_id), VI::ResultNotFound); + m_stray_layer_ids.erase(layer_id); + } + + R_RETURN(m_container->DestroyStrayLayer(layer_id)); +} + +Result IApplicationDisplayService::GetDisplayVsyncEvent( + OutCopyHandle<Kernel::KReadableEvent> out_vsync_event, u64 display_id) { + LOG_DEBUG(Service_VI, "called. display_id={}", display_id); + + std::scoped_lock lk{m_lock}; + + auto [it, created] = m_display_vsync_events.emplace(display_id, m_context); + R_UNLESS(created, VI::ResultPermissionDenied); + + m_container->LinkVsyncEvent(display_id, &it->second); + *out_vsync_event = it->second.GetHandle(); + + R_SUCCEED(); +} + +Result IApplicationDisplayService::ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, + NintendoScaleMode mode) { + LOG_DEBUG(Service_VI, "called mode={}", mode); + + switch (mode) { + case NintendoScaleMode::None: + *out_scaling_mode = ConvertedScaleMode::None; + R_SUCCEED(); + case NintendoScaleMode::Freeze: + *out_scaling_mode = ConvertedScaleMode::Freeze; + R_SUCCEED(); + case NintendoScaleMode::ScaleToWindow: + *out_scaling_mode = ConvertedScaleMode::ScaleToWindow; + R_SUCCEED(); + case NintendoScaleMode::ScaleAndCrop: + *out_scaling_mode = ConvertedScaleMode::ScaleAndCrop; + R_SUCCEED(); + case NintendoScaleMode::PreserveAspectRatio: + *out_scaling_mode = ConvertedScaleMode::PreserveAspectRatio; + R_SUCCEED(); + default: + LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); + R_THROW(VI::ResultOperationFailed); + } +} + +Result IApplicationDisplayService::GetIndirectLayerImageMap( + Out<u64> out_size, Out<u64> out_stride, + OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer, + s64 width, s64 height, u64 indirect_layer_consumer_handle, ClientAppletResourceUserId aruid) { + LOG_WARNING( + Service_VI, + "(STUBBED) called, width={}, height={}, indirect_layer_consumer_handle={}, aruid={:#x}", + width, height, indirect_layer_consumer_handle, aruid.pid); + *out_size = 0; + *out_stride = 0; + R_SUCCEED(); +} + +Result IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo(Out<s64> out_size, + Out<s64> out_alignment, + s64 width, s64 height) { + LOG_DEBUG(Service_VI, "called width={}, height={}", width, height); + + constexpr u64 base_size = 0x20000; + const auto texture_size = width * height * 4; + + *out_alignment = 0x1000; + *out_size = (texture_size + base_size - 1) / base_size * base_size; + + R_SUCCEED(); +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/application_display_service.h b/src/core/hle/service/vi/application_display_service.h new file mode 100644 index 000000000..1bdeb8f84 --- /dev/null +++ b/src/core/hle/service/vi/application_display_service.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <map> +#include <set> + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/os/event.h" +#include "core/hle/service/service.h" +#include "core/hle/service/vi/vi_types.h" + +namespace Kernel { +class KReadableEvent; +} + +namespace Service::Nvnflinger { +class IHOSBinderDriver; +} + +namespace Service::VI { + +class Container; +class IManagerDisplayService; +class ISystemDisplayService; + +class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { +public: + IApplicationDisplayService(Core::System& system_, std::shared_ptr<Container> container); + ~IApplicationDisplayService() override; + + std::shared_ptr<Container> GetContainer() const { + return m_container; + } + +public: + Result GetRelayService(Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_relay_service); + Result GetSystemDisplayService( + Out<SharedPointer<ISystemDisplayService>> out_system_display_service); + Result GetManagerDisplayService( + Out<SharedPointer<IManagerDisplayService>> out_manager_display_service); + Result GetIndirectDisplayTransactionService( + Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_indirect_display_transaction_service); + Result OpenDisplay(Out<u64> out_display_id, DisplayName display_name); + Result OpenDefaultDisplay(Out<u64> out_display_id); + Result CloseDisplay(u64 display_id); + Result SetDisplayEnabled(u32 state, u64 display_id); + Result GetDisplayResolution(Out<s64> out_width, Out<s64> out_height, u64 display_id); + Result SetLayerScalingMode(NintendoScaleMode scale_mode, u64 layer_id); + Result ListDisplays(Out<u64> out_count, + OutArray<DisplayInfo, BufferAttr_HipcMapAlias> out_displays); + Result OpenLayer(Out<u64> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_native_window, + DisplayName display_name, u64 layer_id, ClientAppletResourceUserId aruid); + Result CloseLayer(u64 layer_id); + Result CreateStrayLayer(Out<u64> out_layer_id, Out<u64> out_size, + OutBuffer<BufferAttr_HipcMapAlias> out_native_window, u32 flags, + u64 display_id); + Result DestroyStrayLayer(u64 layer_id); + Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event, + u64 display_id); + Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode); + Result GetIndirectLayerImageMap( + Out<u64> out_size, Out<u64> out_stride, + OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer, + s64 width, s64 height, u64 indirect_layer_consumer_handle, + ClientAppletResourceUserId aruid); + Result GetIndirectLayerImageRequiredMemoryInfo(Out<s64> out_size, Out<s64> out_alignment, + s64 width, s64 height); + +private: + const std::shared_ptr<Container> m_container; + + KernelHelpers::ServiceContext m_context; + std::mutex m_lock{}; + std::set<u64> m_open_layer_ids{}; + std::set<u64> m_stray_layer_ids{}; + std::map<u64, Event> m_display_vsync_events{}; + bool m_vsync_event_fetched{false}; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/application_root_service.cpp b/src/core/hle/service/vi/application_root_service.cpp new file mode 100644 index 000000000..7f35a048d --- /dev/null +++ b/src/core/hle/service/vi/application_root_service.cpp @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/vi/application_display_service.h" +#include "core/hle/service/vi/application_root_service.h" +#include "core/hle/service/vi/container.h" +#include "core/hle/service/vi/service_creator.h" +#include "core/hle/service/vi/vi.h" +#include "core/hle/service/vi/vi_types.h" + +namespace Service::VI { + +IApplicationRootService::IApplicationRootService(Core::System& system_, + std::shared_ptr<Container> container) + : ServiceFramework{system_, "vi:u"}, m_container{std::move(container)} { + static const FunctionInfo functions[] = { + {0, C<&IApplicationRootService::GetDisplayService>, "GetDisplayService"}, + {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, + }; + RegisterHandlers(functions); +} + +IApplicationRootService::~IApplicationRootService() = default; + +Result IApplicationRootService::GetDisplayService( + Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) { + LOG_DEBUG(Service_VI, "called"); + R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container, + Permission::User, policy)); +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/application_root_service.h b/src/core/hle/service/vi/application_root_service.h new file mode 100644 index 000000000..15aa4483d --- /dev/null +++ b/src/core/hle/service/vi/application_root_service.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::VI { + +class Container; +class IApplicationDisplayService; +enum class Policy : u32; + +class IApplicationRootService final : public ServiceFramework<IApplicationRootService> { +public: + explicit IApplicationRootService(Core::System& system_, std::shared_ptr<Container> container); + ~IApplicationRootService() override; + +private: + Result GetDisplayService( + Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, + Policy policy); + +private: + const std::shared_ptr<Container> m_container; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/conductor.cpp b/src/core/hle/service/vi/conductor.cpp new file mode 100644 index 000000000..c8ce4fca0 --- /dev/null +++ b/src/core/hle/service/vi/conductor.cpp @@ -0,0 +1,114 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/vi/conductor.h" +#include "core/hle/service/vi/container.h" +#include "core/hle/service/vi/display_list.h" +#include "core/hle/service/vi/vsync_manager.h" + +constexpr auto FrameNs = std::chrono::nanoseconds{1000000000 / 60}; + +namespace Service::VI { + +Conductor::Conductor(Core::System& system, Container& container, DisplayList& displays) + : m_system(system), m_container(container) { + displays.ForEachDisplay([&](Display& display) { + m_vsync_managers.insert({display.GetId(), VsyncManager{}}); + }); + + if (system.IsMulticore()) { + m_event = Core::Timing::CreateEvent( + "ScreenComposition", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + m_signal.Set(); + return std::chrono::nanoseconds(this->GetNextTicks()); + }); + + system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event); + m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); }); + } else { + m_event = Core::Timing::CreateEvent( + "ScreenComposition", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + this->ProcessVsync(); + return std::chrono::nanoseconds(this->GetNextTicks()); + }); + + system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event); + } +} + +Conductor::~Conductor() { + m_system.CoreTiming().UnscheduleEvent(m_event); + + if (m_system.IsMulticore()) { + m_thread.request_stop(); + m_signal.Set(); + } +} + +void Conductor::LinkVsyncEvent(u64 display_id, Event* event) { + if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) { + it->second.LinkVsyncEvent(event); + } +} + +void Conductor::UnlinkVsyncEvent(u64 display_id, Event* event) { + if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) { + it->second.UnlinkVsyncEvent(event); + } +} + +void Conductor::ProcessVsync() { + for (auto& [display_id, manager] : m_vsync_managers) { + m_container.ComposeOnDisplay(&m_swap_interval, &m_compose_speed_scale, display_id); + manager.SignalVsync(); + } +} + +void Conductor::VsyncThread(std::stop_token token) { + Common::SetCurrentThreadName("VSyncThread"); + + while (!token.stop_requested()) { + m_signal.Wait(); + + if (m_system.IsShuttingDown()) { + return; + } + + this->ProcessVsync(); + } +} + +s64 Conductor::GetNextTicks() const { + const auto& settings = Settings::values; + auto speed_scale = 1.f; + if (settings.use_multi_core.GetValue()) { + if (settings.use_speed_limit.GetValue()) { + // Scales the speed based on speed_limit setting on MC. SC is handled by + // SpeedLimiter::DoSpeedLimiting. + speed_scale = 100.f / settings.speed_limit.GetValue(); + } else { + // Run at unlocked framerate. + speed_scale = 0.01f; + } + } + + // Adjust by speed limit determined during composition. + speed_scale /= m_compose_speed_scale; + + if (m_system.GetNVDECActive() && settings.use_video_framerate.GetValue()) { + // Run at intended presentation rate during video playback. + speed_scale = 1.f; + } + + const f32 effective_fps = 60.f / static_cast<f32>(m_swap_interval); + return static_cast<s64>(speed_scale * (1000000000.f / effective_fps)); +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/conductor.h b/src/core/hle/service/vi/conductor.h new file mode 100644 index 000000000..52e3595d2 --- /dev/null +++ b/src/core/hle/service/vi/conductor.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <unordered_map> + +#include "common/common_types.h" +#include "common/polyfill_thread.h" +#include "common/thread.h" + +namespace Core { +class System; +} + +namespace Core::Timing { +struct EventType; +} + +namespace Service { +class Event; +} + +namespace Service::VI { + +class Container; +class DisplayList; +class VsyncManager; + +class Conductor { +public: + explicit Conductor(Core::System& system, Container& container, DisplayList& displays); + ~Conductor(); + + void LinkVsyncEvent(u64 display_id, Event* event); + void UnlinkVsyncEvent(u64 display_id, Event* event); + +private: + void ProcessVsync(); + void VsyncThread(std::stop_token token); + s64 GetNextTicks() const; + +private: + Core::System& m_system; + Container& m_container; + std::unordered_map<u64, VsyncManager> m_vsync_managers; + std::shared_ptr<Core::Timing::EventType> m_event; + Common::Event m_signal; + std::jthread m_thread; + +private: + s32 m_swap_interval = 1; + f32 m_compose_speed_scale = 1.0f; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/container.cpp b/src/core/hle/service/vi/container.cpp new file mode 100644 index 000000000..9074f4ae0 --- /dev/null +++ b/src/core/hle/service/vi/container.cpp @@ -0,0 +1,226 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/nvdrv/nvdrv_interface.h" +#include "core/hle/service/nvnflinger/buffer_queue_producer.h" +#include "core/hle/service/nvnflinger/hos_binder_driver.h" +#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" +#include "core/hle/service/nvnflinger/surface_flinger.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/vi/container.h" +#include "core/hle/service/vi/vi_results.h" + +namespace Service::VI { + +Container::Container(Core::System& system) { + m_displays.CreateDisplay(DisplayName{"Default"}); + m_displays.CreateDisplay(DisplayName{"External"}); + m_displays.CreateDisplay(DisplayName{"Edid"}); + m_displays.CreateDisplay(DisplayName{"Internal"}); + m_displays.CreateDisplay(DisplayName{"Null"}); + + m_binder_driver = + system.ServiceManager().GetService<Nvnflinger::IHOSBinderDriver>("dispdrv", true); + m_surface_flinger = m_binder_driver->GetSurfaceFlinger(); + + const auto nvdrv = + system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true)->GetModule(); + m_shared_buffer_manager.emplace(system, *this, nvdrv); + + m_displays.ForEachDisplay( + [&](auto& display) { m_surface_flinger->AddDisplay(display.GetId()); }); + + m_conductor.emplace(system, *this, m_displays); +} + +Container::~Container() { + this->OnTerminate(); +} + +void Container::OnTerminate() { + std::scoped_lock lk{m_lock}; + + m_is_shut_down = true; + + m_layers.ForEachLayer([&](auto& layer) { this->DestroyLayerLocked(layer.GetId()); }); + + m_displays.ForEachDisplay( + [&](auto& display) { m_surface_flinger->RemoveDisplay(display.GetId()); }); +} + +SharedBufferManager* Container::GetSharedBufferManager() { + return std::addressof(*m_shared_buffer_manager); +} + +Result Container::GetBinderDriver( + std::shared_ptr<Nvnflinger::IHOSBinderDriver>* out_binder_driver) { + *out_binder_driver = m_binder_driver; + R_SUCCEED(); +} + +Result Container::GetLayerProducerHandle( + std::shared_ptr<android::BufferQueueProducer>* out_producer, u64 layer_id) { + std::scoped_lock lk{m_lock}; + + auto* const layer = m_layers.GetLayerById(layer_id); + R_UNLESS(layer != nullptr, VI::ResultNotFound); + + const auto binder = m_binder_driver->GetServer()->TryGetBinder(layer->GetProducerBinderId()); + R_UNLESS(binder != nullptr, VI::ResultNotFound); + + *out_producer = std::static_pointer_cast<android::BufferQueueProducer>(binder); + R_SUCCEED(); +} + +Result Container::OpenDisplay(u64* out_display_id, const DisplayName& display_name) { + auto* const display = m_displays.GetDisplayByName(display_name); + R_UNLESS(display != nullptr, VI::ResultNotFound); + + *out_display_id = display->GetId(); + R_SUCCEED(); +} + +Result Container::CloseDisplay(u64 display_id) { + R_SUCCEED(); +} + +Result Container::CreateManagedLayer(u64* out_layer_id, u64 display_id, u64 owner_aruid) { + std::scoped_lock lk{m_lock}; + R_RETURN(this->CreateLayerLocked(out_layer_id, display_id, owner_aruid)); +} + +Result Container::DestroyManagedLayer(u64 layer_id) { + std::scoped_lock lk{m_lock}; + + // Try to close, if open, but don't fail if not. + this->CloseLayerLocked(layer_id); + + R_RETURN(this->DestroyLayerLocked(layer_id)); +} + +Result Container::OpenLayer(s32* out_producer_binder_id, u64 layer_id, u64 aruid) { + std::scoped_lock lk{m_lock}; + R_RETURN(this->OpenLayerLocked(out_producer_binder_id, layer_id, aruid)); +} + +Result Container::CloseLayer(u64 layer_id) { + std::scoped_lock lk{m_lock}; + R_RETURN(this->CloseLayerLocked(layer_id)); +} + +Result Container::SetLayerVisibility(u64 layer_id, bool visible) { + std::scoped_lock lk{m_lock}; + + auto* const layer = m_layers.GetLayerById(layer_id); + R_UNLESS(layer != nullptr, VI::ResultNotFound); + + m_surface_flinger->SetLayerVisibility(layer->GetConsumerBinderId(), visible); + R_SUCCEED(); +} + +Result Container::SetLayerBlending(u64 layer_id, bool enabled) { + std::scoped_lock lk{m_lock}; + + auto* const layer = m_layers.GetLayerById(layer_id); + R_UNLESS(layer != nullptr, VI::ResultNotFound); + + m_surface_flinger->SetLayerBlending(layer->GetConsumerBinderId(), + enabled ? Nvnflinger::LayerBlending::Coverage + : Nvnflinger::LayerBlending::None); + R_SUCCEED(); +} + +void Container::LinkVsyncEvent(u64 display_id, Event* event) { + std::scoped_lock lk{m_lock}; + m_conductor->LinkVsyncEvent(display_id, event); +} + +void Container::UnlinkVsyncEvent(u64 display_id, Event* event) { + std::scoped_lock lk{m_lock}; + m_conductor->UnlinkVsyncEvent(display_id, event); +} + +Result Container::CreateStrayLayer(s32* out_producer_binder_id, u64* out_layer_id, u64 display_id) { + std::scoped_lock lk{m_lock}; + R_TRY(this->CreateLayerLocked(out_layer_id, display_id, {})); + R_RETURN(this->OpenLayerLocked(out_producer_binder_id, *out_layer_id, {})); +} + +Result Container::DestroyStrayLayer(u64 layer_id) { + std::scoped_lock lk{m_lock}; + R_TRY(this->CloseLayerLocked(layer_id)); + R_RETURN(this->DestroyLayerLocked(layer_id)); +} + +Result Container::CreateLayerLocked(u64* out_layer_id, u64 display_id, u64 owner_aruid) { + auto* const display = m_displays.GetDisplayById(display_id); + R_UNLESS(display != nullptr, VI::ResultNotFound); + + s32 consumer_binder_id, producer_binder_id; + m_surface_flinger->CreateBufferQueue(&consumer_binder_id, &producer_binder_id); + + auto* const layer = + m_layers.CreateLayer(owner_aruid, display, consumer_binder_id, producer_binder_id); + R_UNLESS(layer != nullptr, VI::ResultNotFound); + + m_surface_flinger->CreateLayer(consumer_binder_id); + + *out_layer_id = layer->GetId(); + R_SUCCEED(); +} + +Result Container::DestroyLayerLocked(u64 layer_id) { + auto* const layer = m_layers.GetLayerById(layer_id); + R_UNLESS(layer != nullptr, VI::ResultNotFound); + + m_surface_flinger->DestroyLayer(layer->GetConsumerBinderId()); + m_surface_flinger->DestroyBufferQueue(layer->GetConsumerBinderId(), + layer->GetProducerBinderId()); + m_layers.DestroyLayer(layer_id); + + R_SUCCEED(); +} + +Result Container::OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid) { + R_UNLESS(!m_is_shut_down, VI::ResultOperationFailed); + + auto* const layer = m_layers.GetLayerById(layer_id); + R_UNLESS(layer != nullptr, VI::ResultNotFound); + R_UNLESS(!layer->IsOpen(), VI::ResultOperationFailed); + R_UNLESS(layer->GetOwnerAruid() == aruid, VI::ResultPermissionDenied); + + layer->Open(); + + if (auto* display = layer->GetDisplay(); display != nullptr) { + m_surface_flinger->AddLayerToDisplayStack(display->GetId(), layer->GetConsumerBinderId()); + } + + *out_producer_binder_id = layer->GetProducerBinderId(); + + R_SUCCEED(); +} + +Result Container::CloseLayerLocked(u64 layer_id) { + auto* const layer = m_layers.GetLayerById(layer_id); + R_UNLESS(layer != nullptr, VI::ResultNotFound); + R_UNLESS(layer->IsOpen(), VI::ResultOperationFailed); + + if (auto* display = layer->GetDisplay(); display != nullptr) { + m_surface_flinger->RemoveLayerFromDisplayStack(display->GetId(), + layer->GetConsumerBinderId()); + } + + layer->Close(); + + R_SUCCEED(); +} + +bool Container::ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, + u64 display_id) { + std::scoped_lock lk{m_lock}; + return m_surface_flinger->ComposeDisplay(out_swap_interval, out_compose_speed_scale, + display_id); +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/container.h b/src/core/hle/service/vi/container.h new file mode 100644 index 000000000..5eac4d77d --- /dev/null +++ b/src/core/hle/service/vi/container.h @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <mutex> +#include <optional> + +#include "core/hle/service/vi/conductor.h" +#include "core/hle/service/vi/display_list.h" +#include "core/hle/service/vi/layer_list.h" +#include "core/hle/service/vi/shared_buffer_manager.h" + +union Result; + +namespace Service::android { +class BufferQueueProducer; +} + +namespace Service::Nvnflinger { +class IHOSBinderDriver; +class SurfaceFlinger; +} // namespace Service::Nvnflinger + +namespace Service { +class Event; +} + +namespace Service::VI { + +class SharedBufferManager; + +class Container { +public: + explicit Container(Core::System& system); + ~Container(); + + void OnTerminate(); + + SharedBufferManager* GetSharedBufferManager(); + + Result GetBinderDriver(std::shared_ptr<Nvnflinger::IHOSBinderDriver>* out_binder_driver); + Result GetLayerProducerHandle(std::shared_ptr<android::BufferQueueProducer>* out_producer, + u64 layer_id); + + Result OpenDisplay(u64* out_display_id, const DisplayName& display_name); + Result CloseDisplay(u64 display_id); + + // Managed layers are created by the interaction between am and ommdisp + // on behalf of an applet. Their lifetime ends with the lifetime of the + // applet's ISelfController. + Result CreateManagedLayer(u64* out_layer_id, u64 display_id, u64 owner_aruid); + Result DestroyManagedLayer(u64 layer_id); + Result OpenLayer(s32* out_producer_binder_id, u64 layer_id, u64 aruid); + Result CloseLayer(u64 layer_id); + + // Stray layers are created by non-applet sysmodules. Their lifetime ends + // with the lifetime of the IApplicationDisplayService which created them. + Result CreateStrayLayer(s32* out_producer_binder_id, u64* out_layer_id, u64 display_id); + Result DestroyStrayLayer(u64 layer_id); + + Result SetLayerVisibility(u64 layer_id, bool visible); + Result SetLayerBlending(u64 layer_id, bool enabled); + + void LinkVsyncEvent(u64 display_id, Event* event); + void UnlinkVsyncEvent(u64 display_id, Event* event); + +private: + Result CreateLayerLocked(u64* out_layer_id, u64 display_id, u64 owner_aruid); + Result DestroyLayerLocked(u64 layer_id); + Result OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid); + Result CloseLayerLocked(u64 layer_id); + +public: + bool ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id); + +private: + std::mutex m_lock{}; + DisplayList m_displays{}; + LayerList m_layers{}; + std::shared_ptr<Nvnflinger::IHOSBinderDriver> m_binder_driver{}; + std::shared_ptr<Nvnflinger::SurfaceFlinger> m_surface_flinger{}; + std::optional<SharedBufferManager> m_shared_buffer_manager{}; + std::optional<Conductor> m_conductor{}; + bool m_is_shut_down{}; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/display.h b/src/core/hle/service/vi/display.h new file mode 100644 index 000000000..fceda75e3 --- /dev/null +++ b/src/core/hle/service/vi/display.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/vi/vi_types.h" + +namespace Service::VI { + +class Display { +public: + constexpr Display() = default; + + void Initialize(u64 id, const DisplayName& display_name) { + m_id = id; + m_display_name = display_name; + m_is_initialized = true; + } + + void Finalize() { + m_id = {}; + m_display_name = {}; + m_is_initialized = {}; + } + + u64 GetId() const { + return m_id; + } + + const DisplayName& GetDisplayName() const { + return m_display_name; + } + + bool IsInitialized() const { + return m_is_initialized; + } + +private: + u64 m_id{}; + DisplayName m_display_name{}; + bool m_is_initialized{}; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp deleted file mode 100644 index 7f2af9acc..000000000 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include <utility> - -#include <fmt/format.h> - -#include "common/assert.h" -#include "core/core.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/nvdrv/core/container.h" -#include "core/hle/service/nvnflinger/buffer_item_consumer.h" -#include "core/hle/service/nvnflinger/buffer_queue_consumer.h" -#include "core/hle/service/nvnflinger/buffer_queue_core.h" -#include "core/hle/service/nvnflinger/buffer_queue_producer.h" -#include "core/hle/service/nvnflinger/hardware_composer.h" -#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" -#include "core/hle/service/vi/display/vi_display.h" -#include "core/hle/service/vi/layer/vi_layer.h" -#include "core/hle/service/vi/vi_results.h" - -namespace Service::VI { - -struct BufferQueue { - std::shared_ptr<android::BufferQueueCore> core; - std::unique_ptr<android::BufferQueueProducer> producer; - std::unique_ptr<android::BufferQueueConsumer> consumer; -}; - -static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context, - Service::Nvidia::NvCore::NvMap& nvmap) { - auto buffer_queue_core = std::make_shared<android::BufferQueueCore>(); - return { - buffer_queue_core, - std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), - std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)}; -} - -Display::Display(u64 id, std::string name_, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_, - KernelHelpers::ServiceContext& service_context_, Core::System& system_) - : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, - service_context{service_context_} { - hardware_composer = std::make_unique<Nvnflinger::HardwareComposer>(); - vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); -} - -Display::~Display() { - service_context.CloseEvent(vsync_event); -} - -Layer& Display::GetLayer(std::size_t index) { - size_t i = 0; - for (auto& layer : layers) { - if (!layer->IsOpen() || !layer->IsVisible()) { - continue; - } - - if (i == index) { - return *layer; - } - - i++; - } - - UNREACHABLE(); -} - -size_t Display::GetNumLayers() const { - return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen() && l->IsVisible(); }); -} - -Kernel::KReadableEvent* Display::GetVSyncEvent() { - return &vsync_event->GetReadableEvent(); -} - -void Display::SignalVSyncEvent() { - vsync_event->Signal(); -} - -void Display::CreateLayer(u64 layer_id, u32 binder_id, - Service::Nvidia::NvCore::Container& nv_core) { - auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); - - auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); - buffer_item_consumer->Connect(false); - - layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer, - std::move(buffer_item_consumer))); - - if (is_abandoned) { - this->FindLayer(layer_id)->GetConsumer().Abandon(); - } - - hos_binder_driver_server.RegisterProducer(std::move(producer)); -} - -void Display::DestroyLayer(u64 layer_id) { - if (auto* layer = this->FindLayer(layer_id); layer != nullptr) { - layer->GetConsumer().Abandon(); - } - - std::erase_if(layers, - [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; }); -} - -void Display::Abandon() { - for (auto& layer : layers) { - layer->GetConsumer().Abandon(); - } - is_abandoned = true; -} - -Layer* Display::FindLayer(u64 layer_id) { - const auto itr = - std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) { - return layer->GetLayerId() == layer_id; - }); - - if (itr == layers.end()) { - return nullptr; - } - - return itr->get(); -} - -const Layer* Display::FindLayer(u64 layer_id) const { - const auto itr = - std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) { - return layer->GetLayerId() == layer_id; - }); - - if (itr == layers.end()) { - return nullptr; - } - - return itr->get(); -} - -} // namespace Service::VI diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h deleted file mode 100644 index 220292cff..000000000 --- a/src/core/hle/service/vi/display/vi_display.h +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> -#include <string> -#include <vector> - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/hle/result.h" - -namespace Core { -class System; -} - -namespace Kernel { -class KEvent; -class KReadableEvent; -} // namespace Kernel - -namespace Service::android { -class BufferQueueProducer; -} - -namespace Service::KernelHelpers { -class ServiceContext; -} - -namespace Service::Nvnflinger { -class HardwareComposer; -class HosBinderDriverServer; -} // namespace Service::Nvnflinger - -namespace Service::Nvidia::NvCore { -class Container; -class NvMap; -} // namespace Service::Nvidia::NvCore - -namespace Service::VI { - -class Layer; - -/// Represents a single display type -class Display { -public: - YUZU_NON_COPYABLE(Display); - YUZU_NON_MOVEABLE(Display); - - /// Constructs a display with a given unique ID and name. - /// - /// @param id The unique ID for this display. - /// @param hos_binder_driver_server_ Nvnflinger HOSBinderDriver server instance. - /// @param service_context_ The ServiceContext for the owning service. - /// @param name_ The name for this display. - /// @param system_ The global system instance. - /// - Display(u64 id, std::string name_, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_, - KernelHelpers::ServiceContext& service_context_, Core::System& system_); - ~Display(); - - /// Gets the unique ID assigned to this display. - u64 GetID() const { - return display_id; - } - - /// Gets the name of this display - const std::string& GetName() const { - return name; - } - - /// Whether or not this display has any layers added to it. - bool HasLayers() const { - return GetNumLayers() > 0; - } - - /// Gets a layer for this display based off an index. - Layer& GetLayer(std::size_t index); - - std::size_t GetNumLayers() const; - - /// Gets the internal vsync event. - Kernel::KReadableEvent* GetVSyncEvent(); - - /// Signals the internal vsync event. - void SignalVSyncEvent(); - - /// Creates and adds a layer to this display with the given ID. - /// - /// @param layer_id The ID to assign to the created layer. - /// @param binder_id The ID assigned to the buffer queue. - /// - void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core); - - /// Removes a layer from this display with the given ID. - /// - /// @param layer_id The ID assigned to the layer to destroy. - /// - void DestroyLayer(u64 layer_id); - - /// Resets the display for a new connection. - void Reset() { - layers.clear(); - } - - void Abandon(); - - /// Attempts to find a layer with the given ID. - /// - /// @param layer_id The layer ID. - /// - /// @returns If found, the Layer instance with the given ID. - /// If not found, then nullptr is returned. - /// - Layer* FindLayer(u64 layer_id); - - /// Attempts to find a layer with the given ID. - /// - /// @param layer_id The layer ID. - /// - /// @returns If found, the Layer instance with the given ID. - /// If not found, then nullptr is returned. - /// - const Layer* FindLayer(u64 layer_id) const; - - Nvnflinger::HardwareComposer& GetComposer() const { - return *hardware_composer; - } - -private: - u64 display_id; - std::string name; - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; - KernelHelpers::ServiceContext& service_context; - - std::vector<std::unique_ptr<Layer>> layers; - std::unique_ptr<Nvnflinger::HardwareComposer> hardware_composer; - Kernel::KEvent* vsync_event{}; - bool is_abandoned{}; -}; - -} // namespace Service::VI diff --git a/src/core/hle/service/vi/display_list.h b/src/core/hle/service/vi/display_list.h new file mode 100644 index 000000000..f710ac472 --- /dev/null +++ b/src/core/hle/service/vi/display_list.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <cstring> + +#include "core/hle/service/vi/display.h" + +namespace Service::VI { + +class DisplayList { +public: + constexpr DisplayList() = default; + + bool CreateDisplay(const DisplayName& name) { + Display* const display = this->GetFreeDisplay(); + if (!display) { + return false; + } + + display->Initialize(m_next_id++, name); + return true; + } + + bool DestroyDisplay(u64 display_id) { + Display* display = this->GetDisplayById(display_id); + if (!display) { + return false; + } + + display->Finalize(); + return true; + } + + Display* GetDisplayByName(const DisplayName& name) { + for (auto& display : m_displays) { + if (display.IsInitialized() && + std::strncmp(name.data(), display.GetDisplayName().data(), sizeof(DisplayName)) == + 0) { + return &display; + } + } + + return nullptr; + } + + Display* GetDisplayById(u64 display_id) { + for (auto& display : m_displays) { + if (display.IsInitialized() && display.GetId() == display_id) { + return &display; + } + } + + return nullptr; + } + + template <typename F> + void ForEachDisplay(F&& cb) { + for (auto& display : m_displays) { + if (display.IsInitialized()) { + cb(display); + } + } + } + +private: + Display* GetFreeDisplay() { + for (auto& display : m_displays) { + if (!display.IsInitialized()) { + return &display; + } + } + + return nullptr; + } + +private: + std::array<Display, 8> m_displays{}; + u64 m_next_id{}; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/layer.h b/src/core/hle/service/vi/layer.h new file mode 100644 index 000000000..e4c9c9864 --- /dev/null +++ b/src/core/hle/service/vi/layer.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::VI { + +class Display; + +class Layer { +public: + constexpr Layer() = default; + + void Initialize(u64 id, u64 owner_aruid, Display* display, s32 consumer_binder_id, + s32 producer_binder_id) { + m_id = id; + m_owner_aruid = owner_aruid; + m_display = display; + m_consumer_binder_id = consumer_binder_id; + m_producer_binder_id = producer_binder_id; + m_is_initialized = true; + } + + void Finalize() { + m_id = {}; + m_owner_aruid = {}; + m_display = {}; + m_consumer_binder_id = {}; + m_producer_binder_id = {}; + m_is_initialized = {}; + } + + void Open() { + m_is_open = true; + } + + void Close() { + m_is_open = false; + } + + u64 GetId() const { + return m_id; + } + + u64 GetOwnerAruid() const { + return m_owner_aruid; + } + + Display* GetDisplay() const { + return m_display; + } + + s32 GetConsumerBinderId() const { + return m_consumer_binder_id; + } + + s32 GetProducerBinderId() const { + return m_producer_binder_id; + } + + bool IsInitialized() const { + return m_is_initialized; + } + + bool IsOpen() const { + return m_is_open; + } + +private: + u64 m_id{}; + u64 m_owner_aruid{}; + Display* m_display{}; + s32 m_consumer_binder_id{}; + s32 m_producer_binder_id{}; + bool m_is_initialized{}; + bool m_is_open{}; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp deleted file mode 100644 index 493bd6e9e..000000000 --- a/src/core/hle/service/vi/layer/vi_layer.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/vi/layer/vi_layer.h" - -namespace Service::VI { - -Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, - android::BufferQueueProducer& binder_, - std::shared_ptr<android::BufferItemConsumer>&& consumer_) - : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, - consumer{std::move(consumer_)}, open{false}, visible{true} {} - -Layer::~Layer() = default; - -} // namespace Service::VI diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h deleted file mode 100644 index b4b031ee7..000000000 --- a/src/core/hle/service/vi/layer/vi_layer.h +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> -#include <utility> - -#include "common/common_types.h" - -namespace Service::android { -class BufferItemConsumer; -class BufferQueueCore; -class BufferQueueProducer; -} // namespace Service::android - -namespace Service::VI { - -/// Represents a single display layer. -class Layer { -public: - /// Constructs a layer with a given ID and buffer queue. - /// - /// @param layer_id_ The ID to assign to this layer. - /// @param binder_id_ The binder ID to assign to this layer. - /// @param binder_ The buffer producer queue for this layer to use. - /// - Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, - android::BufferQueueProducer& binder_, - std::shared_ptr<android::BufferItemConsumer>&& consumer_); - ~Layer(); - - Layer(const Layer&) = delete; - Layer& operator=(const Layer&) = delete; - - Layer(Layer&&) = default; - Layer& operator=(Layer&&) = delete; - - /// Gets the ID for this layer. - u64 GetLayerId() const { - return layer_id; - } - - /// Gets the binder ID for this layer. - u32 GetBinderId() const { - return binder_id; - } - - /// Gets a reference to the buffer queue this layer is using. - android::BufferQueueProducer& GetBufferQueue() { - return binder; - } - - /// Gets a const reference to the buffer queue this layer is using. - const android::BufferQueueProducer& GetBufferQueue() const { - return binder; - } - - android::BufferItemConsumer& GetConsumer() { - return *consumer; - } - - const android::BufferItemConsumer& GetConsumer() const { - return *consumer; - } - - android::BufferQueueCore& Core() { - return core; - } - - const android::BufferQueueCore& Core() const { - return core; - } - - bool IsVisible() const { - return visible; - } - - void SetVisibility(bool v) { - visible = v; - } - - bool IsOpen() const { - return open; - } - - bool Close() { - return std::exchange(open, false); - } - - bool Open() { - return !std::exchange(open, true); - } - -private: - const u64 layer_id; - const u32 binder_id; - android::BufferQueueCore& core; - android::BufferQueueProducer& binder; - std::shared_ptr<android::BufferItemConsumer> consumer; - bool open; - bool visible; -}; - -} // namespace Service::VI diff --git a/src/core/hle/service/vi/layer_list.h b/src/core/hle/service/vi/layer_list.h new file mode 100644 index 000000000..4afca6f40 --- /dev/null +++ b/src/core/hle/service/vi/layer_list.h @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/vi/layer.h" + +namespace Service::VI { + +class LayerList { +public: + constexpr LayerList() = default; + + Layer* CreateLayer(u64 owner_aruid, Display* display, s32 consumer_binder_id, + s32 producer_binder_id) { + Layer* const layer = GetFreeLayer(); + if (!layer) { + return nullptr; + } + + layer->Initialize(++m_next_id, owner_aruid, display, consumer_binder_id, + producer_binder_id); + return layer; + } + + bool DestroyLayer(u64 layer_id) { + Layer* const layer = GetLayerById(layer_id); + if (!layer) { + return false; + } + + layer->Finalize(); + return true; + } + + Layer* GetLayerById(u64 layer_id) { + for (auto& layer : m_layers) { + if (layer.IsInitialized() && layer.GetId() == layer_id) { + return &layer; + } + } + + return nullptr; + } + + template <typename F> + void ForEachLayer(F&& cb) { + for (auto& layer : m_layers) { + if (layer.IsInitialized()) { + cb(layer); + } + } + } + +private: + Layer* GetFreeLayer() { + for (auto& layer : m_layers) { + if (!layer.IsInitialized()) { + return &layer; + } + } + + return nullptr; + } + +private: + std::array<Layer, 8> m_layers{}; + u64 m_next_id{}; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/manager_display_service.cpp b/src/core/hle/service/vi/manager_display_service.cpp new file mode 100644 index 000000000..9f856282e --- /dev/null +++ b/src/core/hle/service/vi/manager_display_service.cpp @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/vi/container.h" +#include "core/hle/service/vi/manager_display_service.h" + +namespace Service::VI { + +IManagerDisplayService::IManagerDisplayService(Core::System& system_, + std::shared_ptr<Container> container) + : ServiceFramework{system_, "IManagerDisplayService"}, m_container{std::move(container)} { + // clang-format off + static const FunctionInfo functions[] = { + {200, nullptr, "AllocateProcessHeapBlock"}, + {201, nullptr, "FreeProcessHeapBlock"}, + {1102, nullptr, "GetDisplayResolution"}, + {2010, C<&IManagerDisplayService::CreateManagedLayer>, "CreateManagedLayer"}, + {2011, C<&IManagerDisplayService::DestroyManagedLayer>, "DestroyManagedLayer"}, + {2012, nullptr, "CreateStrayLayer"}, + {2050, nullptr, "CreateIndirectLayer"}, + {2051, nullptr, "DestroyIndirectLayer"}, + {2052, nullptr, "CreateIndirectProducerEndPoint"}, + {2053, nullptr, "DestroyIndirectProducerEndPoint"}, + {2054, nullptr, "CreateIndirectConsumerEndPoint"}, + {2055, nullptr, "DestroyIndirectConsumerEndPoint"}, + {2060, nullptr, "CreateWatermarkCompositor"}, + {2062, nullptr, "SetWatermarkText"}, + {2063, nullptr, "SetWatermarkLayerStacks"}, + {2300, nullptr, "AcquireLayerTexturePresentingEvent"}, + {2301, nullptr, "ReleaseLayerTexturePresentingEvent"}, + {2302, nullptr, "GetDisplayHotplugEvent"}, + {2303, nullptr, "GetDisplayModeChangedEvent"}, + {2402, nullptr, "GetDisplayHotplugState"}, + {2501, nullptr, "GetCompositorErrorInfo"}, + {2601, nullptr, "GetDisplayErrorEvent"}, + {2701, nullptr, "GetDisplayFatalErrorEvent"}, + {4201, nullptr, "SetDisplayAlpha"}, + {4203, nullptr, "SetDisplayLayerStack"}, + {4205, nullptr, "SetDisplayPowerState"}, + {4206, nullptr, "SetDefaultDisplay"}, + {4207, nullptr, "ResetDisplayPanel"}, + {4208, nullptr, "SetDisplayFatalErrorEnabled"}, + {4209, nullptr, "IsDisplayPanelOn"}, + {4300, nullptr, "GetInternalPanelId"}, + {6000, C<&IManagerDisplayService::AddToLayerStack>, "AddToLayerStack"}, + {6001, nullptr, "RemoveFromLayerStack"}, + {6002, C<&IManagerDisplayService::SetLayerVisibility>, "SetLayerVisibility"}, + {6003, nullptr, "SetLayerConfig"}, + {6004, nullptr, "AttachLayerPresentationTracer"}, + {6005, nullptr, "DetachLayerPresentationTracer"}, + {6006, nullptr, "StartLayerPresentationRecording"}, + {6007, nullptr, "StopLayerPresentationRecording"}, + {6008, nullptr, "StartLayerPresentationFenceWait"}, + {6009, nullptr, "StopLayerPresentationFenceWait"}, + {6010, nullptr, "GetLayerPresentationAllFencesExpiredEvent"}, + {6011, nullptr, "EnableLayerAutoClearTransitionBuffer"}, + {6012, nullptr, "DisableLayerAutoClearTransitionBuffer"}, + {6013, nullptr, "SetLayerOpacity"}, + {6014, nullptr, "AttachLayerWatermarkCompositor"}, + {6015, nullptr, "DetachLayerWatermarkCompositor"}, + {7000, nullptr, "SetContentVisibility"}, + {8000, nullptr, "SetConductorLayer"}, + {8001, nullptr, "SetTimestampTracking"}, + {8100, nullptr, "SetIndirectProducerFlipOffset"}, + {8200, nullptr, "CreateSharedBufferStaticStorage"}, + {8201, nullptr, "CreateSharedBufferTransferMemory"}, + {8202, nullptr, "DestroySharedBuffer"}, + {8203, nullptr, "BindSharedLowLevelLayerToManagedLayer"}, + {8204, nullptr, "BindSharedLowLevelLayerToIndirectLayer"}, + {8207, nullptr, "UnbindSharedLowLevelLayer"}, + {8208, nullptr, "ConnectSharedLowLevelLayerToSharedBuffer"}, + {8209, nullptr, "DisconnectSharedLowLevelLayerFromSharedBuffer"}, + {8210, nullptr, "CreateSharedLayer"}, + {8211, nullptr, "DestroySharedLayer"}, + {8216, nullptr, "AttachSharedLayerToLowLevelLayer"}, + {8217, nullptr, "ForceDetachSharedLayerFromLowLevelLayer"}, + {8218, nullptr, "StartDetachSharedLayerFromLowLevelLayer"}, + {8219, nullptr, "FinishDetachSharedLayerFromLowLevelLayer"}, + {8220, nullptr, "GetSharedLayerDetachReadyEvent"}, + {8221, nullptr, "GetSharedLowLevelLayerSynchronizedEvent"}, + {8222, nullptr, "CheckSharedLowLevelLayerSynchronized"}, + {8223, nullptr, "RegisterSharedBufferImporterAruid"}, + {8224, nullptr, "UnregisterSharedBufferImporterAruid"}, + {8227, nullptr, "CreateSharedBufferProcessHeap"}, + {8228, nullptr, "GetSharedLayerLayerStacks"}, + {8229, nullptr, "SetSharedLayerLayerStacks"}, + {8291, nullptr, "PresentDetachedSharedFrameBufferToLowLevelLayer"}, + {8292, nullptr, "FillDetachedSharedFrameBufferColor"}, + {8293, nullptr, "GetDetachedSharedFrameBufferImage"}, + {8294, nullptr, "SetDetachedSharedFrameBufferImage"}, + {8295, nullptr, "CopyDetachedSharedFrameBufferImage"}, + {8296, nullptr, "SetDetachedSharedFrameBufferSubImage"}, + {8297, nullptr, "GetSharedFrameBufferContentParameter"}, + {8298, nullptr, "ExpandStartupLogoOnSharedFrameBuffer"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IManagerDisplayService::~IManagerDisplayService() = default; + +Result IManagerDisplayService::CreateSharedLayerSession(Kernel::KProcess* owner_process, + u64* out_buffer_id, u64* out_layer_handle, + u64 display_id, bool enable_blending) { + R_RETURN(m_container->GetSharedBufferManager()->CreateSession( + owner_process, out_buffer_id, out_layer_handle, display_id, enable_blending)); +} + +void IManagerDisplayService::DestroySharedLayerSession(Kernel::KProcess* owner_process) { + m_container->GetSharedBufferManager()->DestroySession(owner_process); +} + +Result IManagerDisplayService::SetLayerBlending(bool enabled, u64 layer_id) { + R_RETURN(m_container->SetLayerBlending(layer_id, enabled)); +} + +Result IManagerDisplayService::CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id, + AppletResourceUserId aruid) { + LOG_DEBUG(Service_VI, "called. flags={}, display={}, aruid={}", flags, display_id, aruid.pid); + R_RETURN(m_container->CreateManagedLayer(out_layer_id, display_id, aruid.pid)); +} + +Result IManagerDisplayService::DestroyManagedLayer(u64 layer_id) { + LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id); + R_RETURN(m_container->DestroyManagedLayer(layer_id)); +} + +Result IManagerDisplayService::AddToLayerStack(u32 stack_id, u64 layer_id) { + LOG_WARNING(Service_VI, "(STUBBED) called. stack_id={}, layer_id={}", stack_id, layer_id); + R_SUCCEED(); +} + +Result IManagerDisplayService::SetLayerVisibility(bool visible, u64 layer_id) { + LOG_DEBUG(Service_VI, "called, layer_id={}, visible={}", layer_id, visible); + R_RETURN(m_container->SetLayerVisibility(layer_id, visible)); +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/manager_display_service.h b/src/core/hle/service/vi/manager_display_service.h new file mode 100644 index 000000000..b1bdf7f41 --- /dev/null +++ b/src/core/hle/service/vi/manager_display_service.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KProcess; +} + +namespace Service::VI { + +class Container; + +class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { +public: + explicit IManagerDisplayService(Core::System& system_, std::shared_ptr<Container> container); + ~IManagerDisplayService() override; + + Result CreateSharedLayerSession(Kernel::KProcess* owner_process, u64* out_buffer_id, + u64* out_layer_handle, u64 display_id, bool enable_blending); + void DestroySharedLayerSession(Kernel::KProcess* owner_process); + + Result SetLayerBlending(bool enabled, u64 layer_id); + +public: + Result CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id, + AppletResourceUserId aruid); + Result DestroyManagedLayer(u64 layer_id); + Result AddToLayerStack(u32 stack_id, u64 layer_id); + Result SetLayerVisibility(bool visible, u64 layer_id); + +private: + const std::shared_ptr<Container> m_container; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/manager_root_service.cpp b/src/core/hle/service/vi/manager_root_service.cpp new file mode 100644 index 000000000..0f16a15b4 --- /dev/null +++ b/src/core/hle/service/vi/manager_root_service.cpp @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/nvnflinger/hos_binder_driver.h" +#include "core/hle/service/vi/application_display_service.h" +#include "core/hle/service/vi/container.h" +#include "core/hle/service/vi/manager_root_service.h" +#include "core/hle/service/vi/service_creator.h" +#include "core/hle/service/vi/vi.h" +#include "core/hle/service/vi/vi_types.h" + +namespace Service::VI { + +IManagerRootService::IManagerRootService(Core::System& system_, + std::shared_ptr<Container> container) + : ServiceFramework{system_, "vi:m"}, m_container{std::move(container)} { + static const FunctionInfo functions[] = { + {2, C<&IManagerRootService::GetDisplayService>, "GetDisplayService"}, + {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, + {100, nullptr, "PrepareFatal"}, + {101, nullptr, "ShowFatal"}, + {102, nullptr, "DrawFatalRectangle"}, + {103, nullptr, "DrawFatalText32"}, + }; + RegisterHandlers(functions); +} + +IManagerRootService::~IManagerRootService() = default; + +Result IManagerRootService::GetDisplayService( + Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) { + LOG_DEBUG(Service_VI, "called"); + R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container, + Permission::Manager, policy)); +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/manager_root_service.h b/src/core/hle/service/vi/manager_root_service.h new file mode 100644 index 000000000..77cd32869 --- /dev/null +++ b/src/core/hle/service/vi/manager_root_service.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::VI { + +class Container; +class IApplicationDisplayService; +enum class Policy : u32; + +class IManagerRootService final : public ServiceFramework<IManagerRootService> { +public: + explicit IManagerRootService(Core::System& system_, std::shared_ptr<Container> container); + ~IManagerRootService() override; + + Result GetDisplayService( + Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, + Policy policy); + +private: + const std::shared_ptr<Container> m_container; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/service_creator.cpp b/src/core/hle/service/vi/service_creator.cpp new file mode 100644 index 000000000..2b8e5f957 --- /dev/null +++ b/src/core/hle/service/vi/service_creator.cpp @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/vi/application_display_service.h" +#include "core/hle/service/vi/service_creator.h" +#include "core/hle/service/vi/vi_results.h" +#include "core/hle/service/vi/vi_types.h" + +namespace Service::VI { + +static bool IsValidServiceAccess(Permission permission, Policy policy) { + if (permission == Permission::User) { + return policy == Policy::User; + } + + if (permission == Permission::System || permission == Permission::Manager) { + return policy == Policy::User || policy == Policy::Compositor; + } + + return false; +} + +Result GetApplicationDisplayService( + std::shared_ptr<IApplicationDisplayService>* out_application_display_service, + Core::System& system, std::shared_ptr<Container> container, Permission permission, + Policy policy) { + + if (!IsValidServiceAccess(permission, policy)) { + LOG_ERROR(Service_VI, "Permission denied for policy {}", policy); + R_THROW(ResultPermissionDenied); + } + + *out_application_display_service = + std::make_shared<IApplicationDisplayService>(system, std::move(container)); + R_SUCCEED(); +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/service_creator.h b/src/core/hle/service/vi/service_creator.h new file mode 100644 index 000000000..c6ba1797d --- /dev/null +++ b/src/core/hle/service/vi/service_creator.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +#include "common/common_types.h" + +namespace Core { +class System; +} + +union Result; + +namespace Service::VI { + +class Container; +class IApplicationDisplayService; +enum class Permission; +enum class Policy : u32; + +Result GetApplicationDisplayService( + std::shared_ptr<IApplicationDisplayService>* out_application_display_service, + Core::System& system, std::shared_ptr<Container> container, Permission permission, + Policy policy); + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/shared_buffer_manager.cpp b/src/core/hle/service/vi/shared_buffer_manager.cpp new file mode 100644 index 000000000..12cba16fa --- /dev/null +++ b/src/core/hle/service/vi/shared_buffer_manager.cpp @@ -0,0 +1,431 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <random> + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_system_resource.h" +#include "core/hle/service/nvdrv/devices/nvmap.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvnflinger/buffer_queue_producer.h" +#include "core/hle/service/nvnflinger/pixel_format.h" +#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" +#include "core/hle/service/vi/container.h" +#include "core/hle/service/vi/shared_buffer_manager.h" +#include "core/hle/service/vi/vi_results.h" +#include "video_core/gpu.h" +#include "video_core/host1x/host1x.h" + +namespace Service::VI { + +namespace { + +Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_group, + Core::System& system, u32 size) { + using Core::Memory::YUZU_PAGESIZE; + + // Allocate memory for the system shared buffer. + auto& kernel = system.Kernel(); + + // Hold a temporary page group reference while we try to map it. + auto pg = std::make_unique<Kernel::KPageGroup>( + kernel, std::addressof(kernel.GetSystemSystemResource().GetBlockInfoManager())); + + // Allocate memory from secure pool. + R_TRY(kernel.MemoryManager().AllocateAndOpen( + pg.get(), size / YUZU_PAGESIZE, + Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure, + Kernel::KMemoryManager::Direction::FromBack))); + + // Fill the output data with red. + for (auto& block : *pg) { + u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress()); + u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize()); + + for (; start < end; start++) { + *start = 0xFF0000FF; + } + } + + // Return the mapped page group. + *out_page_group = std::move(pg); + + // We succeeded. + R_SUCCEED(); +} + +Result MapSharedBufferIntoProcessAddressSpace(Common::ProcessAddress* out_map_address, + std::unique_ptr<Kernel::KPageGroup>& pg, + Kernel::KProcess* process, Core::System& system) { + using Core::Memory::YUZU_PAGESIZE; + + auto& page_table = process->GetPageTable(); + + // Get bounds of where mapping is possible. + const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart()); + const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE; + const auto state = Kernel::KMemoryState::IoMemory; + const auto perm = Kernel::KMemoryPermission::UserReadWrite; + std::mt19937_64 rng{process->GetRandomEntropy(0)}; + + // Retry up to 64 times to map into alias code range. + Result res = ResultSuccess; + int i; + for (i = 0; i < 64; i++) { + *out_map_address = alias_code_begin + ((rng() % alias_code_size) * YUZU_PAGESIZE); + res = page_table.MapPageGroup(*out_map_address, *pg, state, perm); + if (R_SUCCEEDED(res)) { + break; + } + } + + // Return failure, if necessary + R_UNLESS(i < 64, res); + + // We succeeded. + R_SUCCEED(); +} + +Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) { + // Create a handle. + Nvidia::Devices::nvmap::IocCreateParams create_params{ + .size = size, + .handle = 0, + }; + R_UNLESS(nvmap.IocCreate(create_params) == Nvidia::NvResult::Success, + VI::ResultOperationFailed); + + // Assign the output handle. + *out_nv_map_handle = create_params.handle; + + // We succeeded. + R_SUCCEED(); +} + +Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Nvidia::DeviceFD nvmap_fd) { + // Free the handle. + Nvidia::Devices::nvmap::IocFreeParams free_params{ + .handle = handle, + }; + R_UNLESS(nvmap.IocFree(free_params, nvmap_fd) == Nvidia::NvResult::Success, + VI::ResultOperationFailed); + + // We succeeded. + R_SUCCEED(); +} + +Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer, + u32 size, Nvidia::DeviceFD nvmap_fd) { + // Assign the allocated memory to the handle. + Nvidia::Devices::nvmap::IocAllocParams alloc_params{ + .handle = handle, + .heap_mask = 0, + .flags = {}, + .align = 0, + .kind = 0, + .address = GetInteger(buffer), + }; + R_UNLESS(nvmap.IocAlloc(alloc_params, nvmap_fd) == Nvidia::NvResult::Success, + VI::ResultOperationFailed); + + // We succeeded. + R_SUCCEED(); +} + +Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd, + Common::ProcessAddress buffer, u32 size) { + // Get the nvmap device. + auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd); + ASSERT(nvmap != nullptr); + + // Create a handle. + R_TRY(CreateNvMapHandle(out_handle, *nvmap, size)); + + // Ensure we maintain a clean state on failure. + ON_RESULT_FAILURE { + R_ASSERT(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd)); + }; + + // Assign the allocated memory to the handle. + R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd)); +} + +void FreeHandle(u32 handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd) { + auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd); + ASSERT(nvmap != nullptr); + + R_ASSERT(FreeNvMapHandle(*nvmap, handle, nvmap_fd)); +} + +constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; +constexpr u32 SharedBufferBlockLinearBpp = 4; + +constexpr u32 SharedBufferBlockLinearWidth = 1280; +constexpr u32 SharedBufferBlockLinearHeight = 768; +constexpr u32 SharedBufferBlockLinearStride = + SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp; +constexpr u32 SharedBufferNumSlots = 7; + +constexpr u32 SharedBufferWidth = 1280; +constexpr u32 SharedBufferHeight = 720; +constexpr u32 SharedBufferAsync = false; + +constexpr u32 SharedBufferSlotSize = + SharedBufferBlockLinearWidth * SharedBufferBlockLinearHeight * SharedBufferBlockLinearBpp; +constexpr u32 SharedBufferSize = SharedBufferSlotSize * SharedBufferNumSlots; + +constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] { + SharedMemoryPoolLayout layout{}; + layout.num_slots = SharedBufferNumSlots; + + for (u32 i = 0; i < SharedBufferNumSlots; i++) { + layout.slots[i].buffer_offset = i * SharedBufferSlotSize; + layout.slots[i].size = SharedBufferSlotSize; + layout.slots[i].width = SharedBufferWidth; + layout.slots[i].height = SharedBufferHeight; + } + + return layout; +}(); + +void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { + auto buffer = std::make_shared<android::NvGraphicBuffer>(); + buffer->width = SharedBufferWidth; + buffer->height = SharedBufferHeight; + buffer->stride = SharedBufferBlockLinearStride; + buffer->format = SharedBufferBlockLinearFormat; + buffer->external_format = SharedBufferBlockLinearFormat; + buffer->buffer_id = handle; + buffer->offset = slot * SharedBufferSlotSize; + ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError); +} + +} // namespace + +SharedBufferManager::SharedBufferManager(Core::System& system, Container& container, + std::shared_ptr<Nvidia::Module> nvdrv) + : m_system(system), m_container(container), m_nvdrv(std::move(nvdrv)) {} + +SharedBufferManager::~SharedBufferManager() = default; + +Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id, + u64* out_layer_handle, u64 display_id, + bool enable_blending) { + std::scoped_lock lk{m_guard}; + + // Ensure we haven't already created. + const u64 aruid = owner_process->GetProcessId(); + R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied); + + // Allocate memory for the shared buffer if needed. + if (!m_buffer_page_group) { + R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system, + SharedBufferSize)); + + // Record buffer id. + m_buffer_id = m_next_buffer_id++; + + // Record display id. + m_display_id = display_id; + } + + // Map into process. + Common::ProcessAddress map_address{}; + R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group, + owner_process, m_system)); + + // Create new session. + auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{}); + auto& session = it->second; + + auto& container = m_nvdrv->GetContainer(); + session.session_id = container.OpenSession(owner_process); + session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id); + + // Create an nvmap handle for the buffer and assign the memory to it. + R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv, + session.nvmap_fd, map_address, SharedBufferSize)); + + // Create and open a layer for the display. + s32 producer_binder_id; + R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id), + std::addressof(session.layer_id), display_id)); + + // Configure blending. + R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending)); + + // Get the producer and set preallocated buffers. + std::shared_ptr<android::BufferQueueProducer> producer; + R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id)); + MakeGraphicBuffer(*producer, 0, session.buffer_nvmap_handle); + MakeGraphicBuffer(*producer, 1, session.buffer_nvmap_handle); + + // Assign outputs. + *out_buffer_id = m_buffer_id; + *out_layer_handle = session.layer_id; + + // We succeeded. + R_SUCCEED(); +} + +void SharedBufferManager::DestroySession(Kernel::KProcess* owner_process) { + std::scoped_lock lk{m_guard}; + + if (m_buffer_id == 0) { + return; + } + + const u64 aruid = owner_process->GetProcessId(); + const auto it = m_sessions.find(aruid); + if (it == m_sessions.end()) { + return; + } + + auto& session = it->second; + + // Destroy the layer. + m_container.DestroyStrayLayer(session.layer_id); + + // Close nvmap handle. + FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd); + + // Close nvmap device. + m_nvdrv->Close(session.nvmap_fd); + + // Close session. + auto& container = m_nvdrv->GetContainer(); + container.CloseSession(session.session_id); + + // Erase. + m_sessions.erase(it); +} + +Result SharedBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, + s32* out_nvmap_handle, + SharedMemoryPoolLayout* out_pool_layout, + u64 buffer_id, + u64 applet_resource_user_id) { + std::scoped_lock lk{m_guard}; + + R_UNLESS(m_buffer_id > 0, VI::ResultNotFound); + R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound); + R_UNLESS(m_sessions.contains(applet_resource_user_id), VI::ResultNotFound); + + *out_pool_layout = SharedBufferPoolLayout; + *out_buffer_size = SharedBufferSize; + *out_nvmap_handle = m_sessions[applet_resource_user_id].buffer_nvmap_handle; + + R_SUCCEED(); +} + +Result SharedBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence, + std::array<s32, 4>& out_slot_indexes, + s64* out_target_slot, u64 layer_id) { + // Get the producer. + std::shared_ptr<android::BufferQueueProducer> producer; + R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id)); + + // Get the next buffer from the producer. + s32 slot; + R_UNLESS(producer->DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0, + SharedBufferWidth, SharedBufferHeight, + SharedBufferBlockLinearFormat, 0) == android::Status::NoError, + VI::ResultOperationFailed); + + // Assign remaining outputs. + *out_target_slot = slot; + out_slot_indexes = {0, 1, -1, -1}; + + // We succeeded. + R_SUCCEED(); +} + +Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence, + Common::Rectangle<s32> crop_region, + u32 transform, s32 swap_interval, u64 layer_id, + s64 slot) { + // Get the producer. + std::shared_ptr<android::BufferQueueProducer> producer; + R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id)); + + // Request to queue the buffer. + std::shared_ptr<android::GraphicBuffer> buffer; + R_UNLESS(producer->RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) == + android::Status::NoError, + VI::ResultOperationFailed); + + ON_RESULT_FAILURE { + producer->CancelBuffer(static_cast<s32>(slot), fence); + }; + + // Queue the buffer to the producer. + android::QueueBufferInput input{}; + android::QueueBufferOutput output{}; + input.crop = crop_region; + input.fence = fence; + input.transform = static_cast<android::NativeWindowTransform>(transform); + input.swap_interval = swap_interval; + R_UNLESS(producer->QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) == + android::Status::NoError, + VI::ResultOperationFailed); + + // We succeeded. + R_SUCCEED(); +} + +Result SharedBufferManager::CancelSharedFrameBuffer(u64 layer_id, s64 slot) { + // Get the producer. + std::shared_ptr<android::BufferQueueProducer> producer; + R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id)); + + // Cancel. + producer->CancelBuffer(static_cast<s32>(slot), android::Fence::NoFence()); + + // We succeeded. + R_SUCCEED(); +} + +Result SharedBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, + u64 layer_id) { + // Get the producer. + std::shared_ptr<android::BufferQueueProducer> producer; + R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id)); + + // Set the event. + *out_event = producer->GetNativeHandle({}); + + // We succeeded. + R_SUCCEED(); +} + +Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) { + std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer()); + Common::ScratchBuffer<u32> scratch; + + // TODO: this could be optimized + s64 e = -1280 * 768 * 4; + for (auto& block : *m_buffer_page_group) { + u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress()); + u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize()); + + for (; start < end; start++) { + *start = 0; + + if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) { + *start = capture_buffer[e]; + } + e++; + } + + m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) { + m_system.GPU().InvalidateRegion(addr, end - start); + }); + } + + *out_was_written = true; + *out_layer_index = 1; + R_SUCCEED(); +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/shared_buffer_manager.h b/src/core/hle/service/vi/shared_buffer_manager.h new file mode 100644 index 000000000..7c9bb7199 --- /dev/null +++ b/src/core/hle/service/vi/shared_buffer_manager.h @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <map> + +#include "common/math_util.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/nvdata.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" +#include "core/hle/service/nvnflinger/ui/fence.h" + +namespace Kernel { +class KPageGroup; +class KReadableEvent; +} // namespace Kernel + +namespace Service::android { +class BufferQueueProducer; +} + +namespace Service::Nvidia { +class Module; +} + +union Result; + +namespace Service::VI { + +class Container; + +struct SharedMemorySlot { + u64 buffer_offset; + u64 size; + s32 width; + s32 height; +}; +static_assert(sizeof(SharedMemorySlot) == 0x18, "SharedMemorySlot has wrong size"); + +struct SharedMemoryPoolLayout { + s32 num_slots; + std::array<SharedMemorySlot, 0x10> slots; +}; +static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); + +struct SharedBufferSession; + +class SharedBufferManager final { +public: + explicit SharedBufferManager(Core::System& system, Container& container, + std::shared_ptr<Nvidia::Module> nvdrv); + ~SharedBufferManager(); + + Result CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle, + u64 display_id, bool enable_blending); + void DestroySession(Kernel::KProcess* owner_process); + + Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle, + SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id, + u64 applet_resource_user_id); + Result AcquireSharedFrameBuffer(android::Fence* out_fence, std::array<s32, 4>& out_slots, + s64* out_target_slot, u64 layer_id); + Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region, + u32 transform, s32 swap_interval, u64 layer_id, s64 slot); + Result CancelSharedFrameBuffer(u64 layer_id, s64 slot); + Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id); + + Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index); + +private: + u64 m_next_buffer_id = 1; + u64 m_display_id = 0; + u64 m_buffer_id = 0; + SharedMemoryPoolLayout m_pool_layout = {}; + std::map<u64, SharedBufferSession> m_sessions; + std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; + + std::mutex m_guard; + Core::System& m_system; + Container& m_container; + const std::shared_ptr<Nvidia::Module> m_nvdrv; +}; + +struct SharedBufferSession { + Nvidia::DeviceFD nvmap_fd = {}; + Nvidia::NvCore::SessionId session_id = {}; + u64 layer_id = {}; + u32 buffer_nvmap_handle = 0; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/system_display_service.cpp b/src/core/hle/service/vi/system_display_service.cpp new file mode 100644 index 000000000..c3c50b07b --- /dev/null +++ b/src/core/hle/service/vi/system_display_service.cpp @@ -0,0 +1,169 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/vi/container.h" +#include "core/hle/service/vi/system_display_service.h" +#include "core/hle/service/vi/vi_types.h" + +namespace Service::VI { + +ISystemDisplayService::ISystemDisplayService(Core::System& system_, + std::shared_ptr<Container> container) + : ServiceFramework{system_, "ISystemDisplayService"}, m_container{std::move(container)} { + // clang-format off + static const FunctionInfo functions[] = { + {1200, nullptr, "GetZOrderCountMin"}, + {1202, nullptr, "GetZOrderCountMax"}, + {1203, nullptr, "GetDisplayLogicalResolution"}, + {1204, nullptr, "SetDisplayMagnification"}, + {2201, nullptr, "SetLayerPosition"}, + {2203, nullptr, "SetLayerSize"}, + {2204, nullptr, "GetLayerZ"}, + {2205, C<&ISystemDisplayService::SetLayerZ>, "SetLayerZ"}, + {2207, C<&ISystemDisplayService::SetLayerVisibility>, "SetLayerVisibility"}, + {2209, nullptr, "SetLayerAlpha"}, + {2210, nullptr, "SetLayerPositionAndSize"}, + {2312, nullptr, "CreateStrayLayer"}, + {2400, nullptr, "OpenIndirectLayer"}, + {2401, nullptr, "CloseIndirectLayer"}, + {2402, nullptr, "FlipIndirectLayer"}, + {3000, C<&ISystemDisplayService::ListDisplayModes>, "ListDisplayModes"}, + {3001, nullptr, "ListDisplayRgbRanges"}, + {3002, nullptr, "ListDisplayContentTypes"}, + {3200, C<&ISystemDisplayService::GetDisplayMode>, "GetDisplayMode"}, + {3201, nullptr, "SetDisplayMode"}, + {3202, nullptr, "GetDisplayUnderscan"}, + {3203, nullptr, "SetDisplayUnderscan"}, + {3204, nullptr, "GetDisplayContentType"}, + {3205, nullptr, "SetDisplayContentType"}, + {3206, nullptr, "GetDisplayRgbRange"}, + {3207, nullptr, "SetDisplayRgbRange"}, + {3208, nullptr, "GetDisplayCmuMode"}, + {3209, nullptr, "SetDisplayCmuMode"}, + {3210, nullptr, "GetDisplayContrastRatio"}, + {3211, nullptr, "SetDisplayContrastRatio"}, + {3214, nullptr, "GetDisplayGamma"}, + {3215, nullptr, "SetDisplayGamma"}, + {3216, nullptr, "GetDisplayCmuLuma"}, + {3217, nullptr, "SetDisplayCmuLuma"}, + {3218, nullptr, "SetDisplayCrcMode"}, + {6013, nullptr, "GetLayerPresentationSubmissionTimestamps"}, + {8225, C<&ISystemDisplayService::GetSharedBufferMemoryHandleId>, "GetSharedBufferMemoryHandleId"}, + {8250, C<&ISystemDisplayService::OpenSharedLayer>, "OpenSharedLayer"}, + {8251, nullptr, "CloseSharedLayer"}, + {8252, C<&ISystemDisplayService::ConnectSharedLayer>, "ConnectSharedLayer"}, + {8253, nullptr, "DisconnectSharedLayer"}, + {8254, C<&ISystemDisplayService::AcquireSharedFrameBuffer>, "AcquireSharedFrameBuffer"}, + {8255, C<&ISystemDisplayService::PresentSharedFrameBuffer>, "PresentSharedFrameBuffer"}, + {8256, C<&ISystemDisplayService::GetSharedFrameBufferAcquirableEvent>, "GetSharedFrameBufferAcquirableEvent"}, + {8257, nullptr, "FillSharedFrameBufferColor"}, + {8258, C<&ISystemDisplayService::CancelSharedFrameBuffer>, "CancelSharedFrameBuffer"}, + {9000, nullptr, "GetDp2hdmiController"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +ISystemDisplayService::~ISystemDisplayService() = default; + +Result ISystemDisplayService::SetLayerZ(u32 z_value, u64 layer_id) { + LOG_WARNING(Service_VI, "(STUBBED) called. layer_id={}, z_value={}", layer_id, z_value); + R_SUCCEED(); +} + +// This function currently does nothing but return a success error code in +// the vi library itself, so do the same thing, but log out the passed in values. +Result ISystemDisplayService::SetLayerVisibility(bool visible, u64 layer_id) { + LOG_DEBUG(Service_VI, "called, layer_id={}, visible={}", layer_id, visible); + R_SUCCEED(); +} + +Result ISystemDisplayService::ListDisplayModes( + Out<u64> out_count, u64 display_id, + OutArray<DisplayMode, BufferAttr_HipcMapAlias> out_display_modes) { + LOG_WARNING(Service_VI, "(STUBBED) called, display_id={}", display_id); + + if (!out_display_modes.empty()) { + out_display_modes[0] = { + .width = 1920, + .height = 1080, + .refresh_rate = 60.f, + .unknown = {}, + }; + *out_count = 1; + } else { + *out_count = 0; + } + + R_SUCCEED(); +} + +Result ISystemDisplayService::GetDisplayMode(Out<DisplayMode> out_display_mode, u64 display_id) { + LOG_WARNING(Service_VI, "(STUBBED) called, display_id={}", display_id); + + if (Settings::IsDockedMode()) { + out_display_mode->width = static_cast<u32>(DisplayResolution::DockedWidth); + out_display_mode->height = static_cast<u32>(DisplayResolution::DockedHeight); + } else { + out_display_mode->width = static_cast<u32>(DisplayResolution::UndockedWidth); + out_display_mode->height = static_cast<u32>(DisplayResolution::UndockedHeight); + } + + out_display_mode->refresh_rate = 60.f; // This wouldn't seem to be correct for 30 fps games. + out_display_mode->unknown = 0; + + R_SUCCEED(); +} + +Result ISystemDisplayService::GetSharedBufferMemoryHandleId( + Out<s32> out_nvmap_handle, Out<u64> out_size, + OutLargeData<SharedMemoryPoolLayout, BufferAttr_HipcMapAlias> out_pool_layout, u64 buffer_id, + ClientAppletResourceUserId aruid) { + LOG_INFO(Service_VI, "called. buffer_id={}, aruid={:#x}", buffer_id, aruid.pid); + + R_RETURN(m_container->GetSharedBufferManager()->GetSharedBufferMemoryHandleId( + out_size, out_nvmap_handle, out_pool_layout, buffer_id, aruid.pid)); +} + +Result ISystemDisplayService::OpenSharedLayer(u64 layer_id) { + LOG_INFO(Service_VI, "(STUBBED) called. layer_id={}", layer_id); + R_SUCCEED(); +} + +Result ISystemDisplayService::ConnectSharedLayer(u64 layer_id) { + LOG_INFO(Service_VI, "(STUBBED) called. layer_id={}", layer_id); + R_SUCCEED(); +} + +Result ISystemDisplayService::AcquireSharedFrameBuffer(Out<android::Fence> out_fence, + Out<std::array<s32, 4>> out_slots, + Out<s64> out_target_slot, u64 layer_id) { + LOG_DEBUG(Service_VI, "called"); + R_RETURN(m_container->GetSharedBufferManager()->AcquireSharedFrameBuffer( + out_fence, *out_slots, out_target_slot, layer_id)); +} + +Result ISystemDisplayService::PresentSharedFrameBuffer(android::Fence fence, + Common::Rectangle<s32> crop_region, + u32 window_transform, s32 swap_interval, + u64 layer_id, s64 surface_id) { + LOG_DEBUG(Service_VI, "called"); + R_RETURN(m_container->GetSharedBufferManager()->PresentSharedFrameBuffer( + fence, crop_region, window_transform, swap_interval, layer_id, surface_id)); +} + +Result ISystemDisplayService::GetSharedFrameBufferAcquirableEvent( + OutCopyHandle<Kernel::KReadableEvent> out_event, u64 layer_id) { + LOG_DEBUG(Service_VI, "called"); + R_RETURN(m_container->GetSharedBufferManager()->GetSharedFrameBufferAcquirableEvent(out_event, + layer_id)); +} + +Result ISystemDisplayService::CancelSharedFrameBuffer(u64 layer_id, s64 slot) { + LOG_DEBUG(Service_VI, "called"); + R_RETURN(m_container->GetSharedBufferManager()->CancelSharedFrameBuffer(layer_id, slot)); +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/system_display_service.h b/src/core/hle/service/vi/system_display_service.h new file mode 100644 index 000000000..7228d826e --- /dev/null +++ b/src/core/hle/service/vi/system_display_service.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/math_util.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/nvnflinger/ui/fence.h" +#include "core/hle/service/service.h" +#include "core/hle/service/vi/shared_buffer_manager.h" + +namespace Service::VI { +struct DisplayMode; + +class Container; + +class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { +public: + explicit ISystemDisplayService(Core::System& system_, std::shared_ptr<Container> container); + ~ISystemDisplayService() override; + +private: + Result SetLayerZ(u32 z_value, u64 layer_id); + Result SetLayerVisibility(bool visible, u64 layer_id); + Result ListDisplayModes(Out<u64> out_count, u64 display_id, + OutArray<DisplayMode, BufferAttr_HipcMapAlias> out_display_modes); + Result GetDisplayMode(Out<DisplayMode> out_display_mode, u64 display_id); + + Result GetSharedBufferMemoryHandleId( + Out<s32> out_nvmap_handle, Out<u64> out_size, + OutLargeData<SharedMemoryPoolLayout, BufferAttr_HipcMapAlias> out_pool_layout, + u64 buffer_id, ClientAppletResourceUserId aruid); + Result OpenSharedLayer(u64 layer_id); + Result ConnectSharedLayer(u64 layer_id); + Result GetSharedFrameBufferAcquirableEvent(OutCopyHandle<Kernel::KReadableEvent> out_event, + u64 layer_id); + Result AcquireSharedFrameBuffer(Out<android::Fence> out_fence, + Out<std::array<s32, 4>> out_slots, Out<s64> out_target_slot, + u64 layer_id); + Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region, + u32 window_transform, s32 swap_interval, u64 layer_id, + s64 surface_id); + Result CancelSharedFrameBuffer(u64 layer_id, s64 slot); + +private: + const std::shared_ptr<Container> m_container; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/system_root_service.cpp b/src/core/hle/service/vi/system_root_service.cpp new file mode 100644 index 000000000..3489727d8 --- /dev/null +++ b/src/core/hle/service/vi/system_root_service.cpp @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/vi/application_display_service.h" +#include "core/hle/service/vi/container.h" +#include "core/hle/service/vi/service_creator.h" +#include "core/hle/service/vi/system_root_service.h" +#include "core/hle/service/vi/vi.h" +#include "core/hle/service/vi/vi_types.h" + +namespace Service::VI { + +ISystemRootService::ISystemRootService(Core::System& system_, std::shared_ptr<Container> container) + : ServiceFramework{system_, "vi:s"}, m_container{std::move(container)} { + static const FunctionInfo functions[] = { + {1, C<&ISystemRootService::GetDisplayService>, "GetDisplayService"}, + {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, + }; + RegisterHandlers(functions); +} + +ISystemRootService::~ISystemRootService() = default; + +Result ISystemRootService::GetDisplayService( + Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) { + LOG_DEBUG(Service_VI, "called"); + R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container, + Permission::System, policy)); +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/system_root_service.h b/src/core/hle/service/vi/system_root_service.h new file mode 100644 index 000000000..9d5aa53d3 --- /dev/null +++ b/src/core/hle/service/vi/system_root_service.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::VI { + +class Container; +class IApplicationDisplayService; +enum class Policy : u32; + +class ISystemRootService final : public ServiceFramework<ISystemRootService> { +public: + explicit ISystemRootService(Core::System& system_, std::shared_ptr<Container> container); + ~ISystemRootService() override; + +private: + Result GetDisplayService( + Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, + Policy policy); + + const std::shared_ptr<Container> m_container; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index d508ed28c..b388efaf6 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -1,974 +1,30 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <algorithm> -#include <array> -#include <cstring> -#include <memory> -#include <optional> -#include <type_traits> -#include <utility> - -#include "common/alignment.h" -#include "common/assert.h" -#include "common/common_funcs.h" -#include "common/logging/log.h" -#include "common/math_util.h" -#include "common/settings.h" -#include "common/string_util.h" -#include "common/swap.h" -#include "core/core_timing.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_thread.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nvdrv/devices/nvmap.h" -#include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvdrv/nvdrv.h" -#include "core/hle/service/nvnflinger/binder.h" -#include "core/hle/service/nvnflinger/buffer_queue_producer.h" -#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" -#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" -#include "core/hle/service/nvnflinger/nvnflinger.h" -#include "core/hle/service/nvnflinger/parcel.h" +#include "core/core.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/service.h" +#include "core/hle/service/vi/application_root_service.h" +#include "core/hle/service/vi/container.h" +#include "core/hle/service/vi/manager_root_service.h" +#include "core/hle/service/vi/system_root_service.h" #include "core/hle/service/vi/vi.h" -#include "core/hle/service/vi/vi_m.h" -#include "core/hle/service/vi/vi_results.h" -#include "core/hle/service/vi/vi_s.h" -#include "core/hle/service/vi/vi_u.h" namespace Service::VI { -struct DisplayInfo { - /// The name of this particular display. - char display_name[0x40]{"Default"}; - - /// Whether or not the display has a limited number of layers. - u8 has_limited_layers{1}; - INSERT_PADDING_BYTES(7); - - /// Indicates the total amount of layers supported by the display. - /// @note This is only valid if has_limited_layers is set. - u64 max_layers{1}; - - /// Maximum width in pixels. - u64 width{1920}; - - /// Maximum height in pixels. - u64 height{1080}; -}; -static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); - -class NativeWindow final { -public: - constexpr explicit NativeWindow(u32 id_) : id{id_} {} - constexpr explicit NativeWindow(const NativeWindow& other) = default; - -private: - const u32 magic = 2; - const u32 process_id = 1; - const u64 id; - INSERT_PADDING_WORDS(2); - std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'}; - INSERT_PADDING_WORDS(2); -}; -static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size"); - -class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { -public: - explicit IHOSBinderDriver(Core::System& system_, Nvnflinger::HosBinderDriverServer& server_) - : ServiceFramework{system_, "IHOSBinderDriver"}, server(server_) { - static const FunctionInfo functions[] = { - {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, - {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, - {2, &IHOSBinderDriver::GetNativeHandle, "GetNativeHandle"}, - {3, &IHOSBinderDriver::TransactParcel, "TransactParcelAuto"}, - }; - RegisterHandlers(functions); - } - -private: - void TransactParcel(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 id = rp.Pop<u32>(); - const auto transaction = static_cast<android::TransactionId>(rp.Pop<u32>()); - const u32 flags = rp.Pop<u32>(); - - LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, - transaction, flags); - - server.TryGetProducer(id)->Transact(ctx, transaction, flags); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void AdjustRefcount(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 id = rp.Pop<u32>(); - const s32 addval = rp.PopRaw<s32>(); - const u32 type = rp.Pop<u32>(); - - LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={:08X}, type={:08X}", id, addval, - type); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetNativeHandle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 id = rp.Pop<u32>(); - const u32 unknown = rp.Pop<u32>(); - - LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(server.TryGetProducer(id)->GetNativeHandle()); - } - -private: - Nvnflinger::HosBinderDriverServer& server; -}; - -class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { -public: - explicit ISystemDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_) - : ServiceFramework{system_, "ISystemDisplayService"}, nvnflinger{nvnflinger_} { - // clang-format off - static const FunctionInfo functions[] = { - {1200, nullptr, "GetZOrderCountMin"}, - {1202, nullptr, "GetZOrderCountMax"}, - {1203, nullptr, "GetDisplayLogicalResolution"}, - {1204, nullptr, "SetDisplayMagnification"}, - {2201, nullptr, "SetLayerPosition"}, - {2203, nullptr, "SetLayerSize"}, - {2204, nullptr, "GetLayerZ"}, - {2205, &ISystemDisplayService::SetLayerZ, "SetLayerZ"}, - {2207, &ISystemDisplayService::SetLayerVisibility, "SetLayerVisibility"}, - {2209, nullptr, "SetLayerAlpha"}, - {2210, nullptr, "SetLayerPositionAndSize"}, - {2312, nullptr, "CreateStrayLayer"}, - {2400, nullptr, "OpenIndirectLayer"}, - {2401, nullptr, "CloseIndirectLayer"}, - {2402, nullptr, "FlipIndirectLayer"}, - {3000, nullptr, "ListDisplayModes"}, - {3001, nullptr, "ListDisplayRgbRanges"}, - {3002, nullptr, "ListDisplayContentTypes"}, - {3200, &ISystemDisplayService::GetDisplayMode, "GetDisplayMode"}, - {3201, nullptr, "SetDisplayMode"}, - {3202, nullptr, "GetDisplayUnderscan"}, - {3203, nullptr, "SetDisplayUnderscan"}, - {3204, nullptr, "GetDisplayContentType"}, - {3205, nullptr, "SetDisplayContentType"}, - {3206, nullptr, "GetDisplayRgbRange"}, - {3207, nullptr, "SetDisplayRgbRange"}, - {3208, nullptr, "GetDisplayCmuMode"}, - {3209, nullptr, "SetDisplayCmuMode"}, - {3210, nullptr, "GetDisplayContrastRatio"}, - {3211, nullptr, "SetDisplayContrastRatio"}, - {3214, nullptr, "GetDisplayGamma"}, - {3215, nullptr, "SetDisplayGamma"}, - {3216, nullptr, "GetDisplayCmuLuma"}, - {3217, nullptr, "SetDisplayCmuLuma"}, - {3218, nullptr, "SetDisplayCrcMode"}, - {6013, nullptr, "GetLayerPresentationSubmissionTimestamps"}, - {8225, &ISystemDisplayService::GetSharedBufferMemoryHandleId, "GetSharedBufferMemoryHandleId"}, - {8250, &ISystemDisplayService::OpenSharedLayer, "OpenSharedLayer"}, - {8251, nullptr, "CloseSharedLayer"}, - {8252, &ISystemDisplayService::ConnectSharedLayer, "ConnectSharedLayer"}, - {8253, nullptr, "DisconnectSharedLayer"}, - {8254, &ISystemDisplayService::AcquireSharedFrameBuffer, "AcquireSharedFrameBuffer"}, - {8255, &ISystemDisplayService::PresentSharedFrameBuffer, "PresentSharedFrameBuffer"}, - {8256, &ISystemDisplayService::GetSharedFrameBufferAcquirableEvent, "GetSharedFrameBufferAcquirableEvent"}, - {8257, nullptr, "FillSharedFrameBufferColor"}, - {8258, nullptr, "CancelSharedFrameBuffer"}, - {9000, nullptr, "GetDp2hdmiController"}, - }; - // clang-format on - RegisterHandlers(functions); - } - -private: - void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 buffer_id = rp.PopRaw<u64>(); - const u64 aruid = ctx.GetPID(); - - LOG_INFO(Service_VI, "called. buffer_id={:#x}, aruid={:#x}", buffer_id, aruid); - - struct OutputParameters { - s32 nvmap_handle; - u64 size; - }; - - OutputParameters out{}; - Nvnflinger::SharedMemoryPoolLayout layout{}; - const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId( - &out.size, &out.nvmap_handle, &layout, buffer_id, aruid); - - ctx.WriteBuffer(&layout, sizeof(layout)); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(result); - rb.PushRaw(out); - } - - void OpenSharedLayer(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 layer_id = rp.PopRaw<u64>(); - - LOG_INFO(Service_VI, "(STUBBED) called. layer_id={:#x}", layer_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void ConnectSharedLayer(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 layer_id = rp.PopRaw<u64>(); - - LOG_INFO(Service_VI, "(STUBBED) called. layer_id={:#x}", layer_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetSharedFrameBufferAcquirableEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_VI, "called"); - - IPC::RequestParser rp{ctx}; - const u64 layer_id = rp.PopRaw<u64>(); - - Kernel::KReadableEvent* event{}; - const auto result = nvnflinger.GetSystemBufferManager().GetSharedFrameBufferAcquirableEvent( - &event, layer_id); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(result); - rb.PushCopyObjects(event); - } - - void AcquireSharedFrameBuffer(HLERequestContext& ctx) { - LOG_DEBUG(Service_VI, "called"); - - IPC::RequestParser rp{ctx}; - const u64 layer_id = rp.PopRaw<u64>(); - - struct OutputParameters { - android::Fence fence; - std::array<s32, 4> slots; - s64 target_slot; - }; - static_assert(sizeof(OutputParameters) == 0x40, "OutputParameters has wrong size"); - - OutputParameters out{}; - const auto result = nvnflinger.GetSystemBufferManager().AcquireSharedFrameBuffer( - &out.fence, out.slots, &out.target_slot, layer_id); - - IPC::ResponseBuilder rb{ctx, 18}; - rb.Push(result); - rb.PushRaw(out); - } - - void PresentSharedFrameBuffer(HLERequestContext& ctx) { - LOG_DEBUG(Service_VI, "called"); - - struct InputParameters { - android::Fence fence; - Common::Rectangle<s32> crop_region; - u32 window_transform; - s32 swap_interval; - u64 layer_id; - s64 surface_id; - }; - static_assert(sizeof(InputParameters) == 0x50, "InputParameters has wrong size"); - - IPC::RequestParser rp{ctx}; - auto input = rp.PopRaw<InputParameters>(); - - const auto result = nvnflinger.GetSystemBufferManager().PresentSharedFrameBuffer( - input.fence, input.crop_region, input.window_transform, input.swap_interval, - input.layer_id, input.surface_id); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void SetLayerZ(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 layer_id = rp.Pop<u64>(); - const u64 z_value = rp.Pop<u64>(); - - LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}, z_value=0x{:016X}", layer_id, - z_value); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - // This function currently does nothing but return a success error code in - // the vi library itself, so do the same thing, but log out the passed in values. - void SetLayerVisibility(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 layer_id = rp.Pop<u64>(); - const bool visibility = rp.Pop<bool>(); - - LOG_DEBUG(Service_VI, "called, layer_id=0x{:08X}, visibility={}", layer_id, visibility); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetDisplayMode(HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - - if (Settings::IsDockedMode()) { - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); - } else { - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); - } - - rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games. - rb.Push<u32>(0); - } - -private: - Nvnflinger::Nvnflinger& nvnflinger; -}; - -class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { -public: - explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_) - : ServiceFramework{system_, "IManagerDisplayService"}, nvnflinger{nvnflinger_} { - // clang-format off - static const FunctionInfo functions[] = { - {200, nullptr, "AllocateProcessHeapBlock"}, - {201, nullptr, "FreeProcessHeapBlock"}, - {1020, &IManagerDisplayService::CloseDisplay, "CloseDisplay"}, - {1102, nullptr, "GetDisplayResolution"}, - {2010, &IManagerDisplayService::CreateManagedLayer, "CreateManagedLayer"}, - {2011, nullptr, "DestroyManagedLayer"}, - {2012, nullptr, "CreateStrayLayer"}, - {2050, nullptr, "CreateIndirectLayer"}, - {2051, nullptr, "DestroyIndirectLayer"}, - {2052, nullptr, "CreateIndirectProducerEndPoint"}, - {2053, nullptr, "DestroyIndirectProducerEndPoint"}, - {2054, nullptr, "CreateIndirectConsumerEndPoint"}, - {2055, nullptr, "DestroyIndirectConsumerEndPoint"}, - {2060, nullptr, "CreateWatermarkCompositor"}, - {2062, nullptr, "SetWatermarkText"}, - {2063, nullptr, "SetWatermarkLayerStacks"}, - {2300, nullptr, "AcquireLayerTexturePresentingEvent"}, - {2301, nullptr, "ReleaseLayerTexturePresentingEvent"}, - {2302, nullptr, "GetDisplayHotplugEvent"}, - {2303, nullptr, "GetDisplayModeChangedEvent"}, - {2402, nullptr, "GetDisplayHotplugState"}, - {2501, nullptr, "GetCompositorErrorInfo"}, - {2601, nullptr, "GetDisplayErrorEvent"}, - {2701, nullptr, "GetDisplayFatalErrorEvent"}, - {4201, nullptr, "SetDisplayAlpha"}, - {4203, nullptr, "SetDisplayLayerStack"}, - {4205, nullptr, "SetDisplayPowerState"}, - {4206, nullptr, "SetDefaultDisplay"}, - {4207, nullptr, "ResetDisplayPanel"}, - {4208, nullptr, "SetDisplayFatalErrorEnabled"}, - {4209, nullptr, "IsDisplayPanelOn"}, - {4300, nullptr, "GetInternalPanelId"}, - {6000, &IManagerDisplayService::AddToLayerStack, "AddToLayerStack"}, - {6001, nullptr, "RemoveFromLayerStack"}, - {6002, &IManagerDisplayService::SetLayerVisibility, "SetLayerVisibility"}, - {6003, nullptr, "SetLayerConfig"}, - {6004, nullptr, "AttachLayerPresentationTracer"}, - {6005, nullptr, "DetachLayerPresentationTracer"}, - {6006, nullptr, "StartLayerPresentationRecording"}, - {6007, nullptr, "StopLayerPresentationRecording"}, - {6008, nullptr, "StartLayerPresentationFenceWait"}, - {6009, nullptr, "StopLayerPresentationFenceWait"}, - {6010, nullptr, "GetLayerPresentationAllFencesExpiredEvent"}, - {6011, nullptr, "EnableLayerAutoClearTransitionBuffer"}, - {6012, nullptr, "DisableLayerAutoClearTransitionBuffer"}, - {6013, nullptr, "SetLayerOpacity"}, - {6014, nullptr, "AttachLayerWatermarkCompositor"}, - {6015, nullptr, "DetachLayerWatermarkCompositor"}, - {7000, nullptr, "SetContentVisibility"}, - {8000, nullptr, "SetConductorLayer"}, - {8001, nullptr, "SetTimestampTracking"}, - {8100, nullptr, "SetIndirectProducerFlipOffset"}, - {8200, nullptr, "CreateSharedBufferStaticStorage"}, - {8201, nullptr, "CreateSharedBufferTransferMemory"}, - {8202, nullptr, "DestroySharedBuffer"}, - {8203, nullptr, "BindSharedLowLevelLayerToManagedLayer"}, - {8204, nullptr, "BindSharedLowLevelLayerToIndirectLayer"}, - {8207, nullptr, "UnbindSharedLowLevelLayer"}, - {8208, nullptr, "ConnectSharedLowLevelLayerToSharedBuffer"}, - {8209, nullptr, "DisconnectSharedLowLevelLayerFromSharedBuffer"}, - {8210, nullptr, "CreateSharedLayer"}, - {8211, nullptr, "DestroySharedLayer"}, - {8216, nullptr, "AttachSharedLayerToLowLevelLayer"}, - {8217, nullptr, "ForceDetachSharedLayerFromLowLevelLayer"}, - {8218, nullptr, "StartDetachSharedLayerFromLowLevelLayer"}, - {8219, nullptr, "FinishDetachSharedLayerFromLowLevelLayer"}, - {8220, nullptr, "GetSharedLayerDetachReadyEvent"}, - {8221, nullptr, "GetSharedLowLevelLayerSynchronizedEvent"}, - {8222, nullptr, "CheckSharedLowLevelLayerSynchronized"}, - {8223, nullptr, "RegisterSharedBufferImporterAruid"}, - {8224, nullptr, "UnregisterSharedBufferImporterAruid"}, - {8227, nullptr, "CreateSharedBufferProcessHeap"}, - {8228, nullptr, "GetSharedLayerLayerStacks"}, - {8229, nullptr, "SetSharedLayerLayerStacks"}, - {8291, nullptr, "PresentDetachedSharedFrameBufferToLowLevelLayer"}, - {8292, nullptr, "FillDetachedSharedFrameBufferColor"}, - {8293, nullptr, "GetDetachedSharedFrameBufferImage"}, - {8294, nullptr, "SetDetachedSharedFrameBufferImage"}, - {8295, nullptr, "CopyDetachedSharedFrameBufferImage"}, - {8296, nullptr, "SetDetachedSharedFrameBufferSubImage"}, - {8297, nullptr, "GetSharedFrameBufferContentParameter"}, - {8298, nullptr, "ExpandStartupLogoOnSharedFrameBuffer"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void CloseDisplay(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 display = rp.Pop<u64>(); - - const Result rc = nvnflinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(rc); - } - - void CreateManagedLayer(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 unknown = rp.Pop<u32>(); - rp.Skip(1, false); - const u64 display = rp.Pop<u64>(); - const u64 aruid = rp.Pop<u64>(); - - LOG_WARNING(Service_VI, - "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}", - unknown, display, aruid); - - const auto layer_id = nvnflinger.CreateLayer(display); - if (!layer_id) { - LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(*layer_id); - } - - void AddToLayerStack(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 stack = rp.Pop<u32>(); - const u64 layer_id = rp.Pop<u64>(); - - LOG_WARNING(Service_VI, "(STUBBED) called. stack=0x{:08X}, layer_id=0x{:016X}", stack, - layer_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void SetLayerVisibility(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 layer_id = rp.Pop<u64>(); - const bool visibility = rp.Pop<bool>(); - - LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id, - visibility); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - Nvnflinger::Nvnflinger& nvnflinger; -}; - -class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { -public: - IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_) - : ServiceFramework{system_, "IApplicationDisplayService"}, nvnflinger{nvnflinger_}, - hos_binder_driver_server{hos_binder_driver_server_} { - - static const FunctionInfo functions[] = { - {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, - {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"}, - {102, &IApplicationDisplayService::GetManagerDisplayService, - "GetManagerDisplayService"}, - {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService, - "GetIndirectDisplayTransactionService"}, - {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"}, - {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"}, - {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"}, - {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"}, - {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"}, - {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"}, - {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"}, - {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"}, - {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"}, - {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"}, - {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, - {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, - {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, - "GetIndirectLayerImageMap"}, - {2451, nullptr, "GetIndirectLayerImageCropMap"}, - {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo, - "GetIndirectLayerImageRequiredMemoryInfo"}, - {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, - {5203, nullptr, "GetDisplayVsyncEventForDebug"}, - }; - RegisterHandlers(functions); - } - - ~IApplicationDisplayService() { - for (const auto layer_id : stray_layer_ids) { - nvnflinger.DestroyLayer(layer_id); - } - } - -private: - enum class ConvertedScaleMode : u64 { - Freeze = 0, - ScaleToWindow = 1, - ScaleAndCrop = 2, - None = 3, - PreserveAspectRatio = 4, - }; - - enum class NintendoScaleMode : u32 { - None = 0, - Freeze = 1, - ScaleToWindow = 2, - ScaleAndCrop = 3, - PreserveAspectRatio = 4, - }; - - void GetRelayService(HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server); - } - - void GetSystemDisplayService(HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemDisplayService>(system, nvnflinger); - } - - void GetManagerDisplayService(HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IManagerDisplayService>(system, nvnflinger); - } - - void GetIndirectDisplayTransactionService(HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); +void LoopProcess(Core::System& system, std::stop_token token) { + const auto container = std::make_shared<Container>(system); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server); - } - - void OpenDisplay(HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); - - IPC::RequestParser rp{ctx}; - const auto name_buf = rp.PopRaw<std::array<char, 0x40>>(); - - OpenDisplayImpl(ctx, std::string_view{name_buf.data(), name_buf.size()}); - } - - void OpenDefaultDisplay(HLERequestContext& ctx) { - LOG_DEBUG(Service_VI, "called"); - - OpenDisplayImpl(ctx, "Default"); - } - - void OpenDisplayImpl(HLERequestContext& ctx, std::string_view name) { - const auto trim_pos = name.find('\0'); - - if (trim_pos != std::string_view::npos) { - name.remove_suffix(name.size() - trim_pos); - } - - ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); - - const auto display_id = nvnflinger.OpenDisplay(name); - if (!display_id) { - LOG_ERROR(Service_VI, "Display not found! display_name={}", name); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultNotFound); - return; - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(*display_id); - } - - void CloseDisplay(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 display_id = rp.Pop<u64>(); - - const Result rc = nvnflinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(rc); - } - - // This literally does nothing internally in the actual service itself, - // and just returns a successful result code regardless of the input. - void SetDisplayEnabled(HLERequestContext& ctx) { - LOG_DEBUG(Service_VI, "called."); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetDisplayResolution(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 display_id = rp.Pop<u64>(); - - LOG_DEBUG(Service_VI, "called. display_id=0x{:016X}", display_id); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - - // This only returns the fixed values of 1280x720 and makes no distinguishing - // between docked and undocked dimensions. We take the liberty of applying - // the resolution scaling factor here. - rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth)); - rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight)); - } - - void SetLayerScalingMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto scaling_mode = rp.PopEnum<NintendoScaleMode>(); - const u64 unknown = rp.Pop<u64>(); - - LOG_DEBUG(Service_VI, "called. scaling_mode=0x{:08X}, unknown=0x{:016X}", scaling_mode, - unknown); - - IPC::ResponseBuilder rb{ctx, 2}; - - if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) { - LOG_ERROR(Service_VI, "Invalid scaling mode provided."); - rb.Push(ResultOperationFailed); - return; - } - - if (scaling_mode != NintendoScaleMode::ScaleToWindow && - scaling_mode != NintendoScaleMode::PreserveAspectRatio) { - LOG_ERROR(Service_VI, "Unsupported scaling mode supplied."); - rb.Push(ResultNotSupported); - return; - } - - rb.Push(ResultSuccess); - } - - void ListDisplays(HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); - - const DisplayInfo display_info; - ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(1); - } - - void OpenLayer(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); - const std::string display_name(Common::StringFromBuffer(name_buf)); - - const u64 layer_id = rp.Pop<u64>(); - const u64 aruid = rp.Pop<u64>(); - - LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid); - - const auto display_id = nvnflinger.OpenDisplay(display_name); - if (!display_id) { - LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultNotFound); - return; - } - - const auto buffer_queue_id = nvnflinger.FindBufferQueueId(*display_id, layer_id); - if (!buffer_queue_id) { - LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultNotFound); - return; - } - - if (!nvnflinger.OpenLayer(layer_id)) { - LOG_WARNING(Service_VI, "Tried to open layer which was already open"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultOperationFailed); - return; - } - - android::OutputParcel parcel; - parcel.WriteInterface(NativeWindow{*buffer_queue_id}); - - const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(buffer_size); - } - - void CloseLayer(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto layer_id{rp.Pop<u64>()}; - - LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id); - - if (!nvnflinger.CloseLayer(layer_id)) { - LOG_WARNING(Service_VI, "Tried to close layer which was not open"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultOperationFailed); - return; - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void CreateStrayLayer(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u32 flags = rp.Pop<u32>(); - rp.Pop<u32>(); // padding - const u64 display_id = rp.Pop<u64>(); - - LOG_DEBUG(Service_VI, "called. flags=0x{:08X}, display_id=0x{:016X}", flags, display_id); - - // TODO(Subv): What's the difference between a Stray and a Managed layer? - - const auto layer_id = nvnflinger.CreateLayer(display_id); - if (!layer_id) { - LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultNotFound); - return; - } - - stray_layer_ids.push_back(*layer_id); - const auto buffer_queue_id = nvnflinger.FindBufferQueueId(display_id, *layer_id); - if (!buffer_queue_id) { - LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultNotFound); - return; - } - - android::OutputParcel parcel; - parcel.WriteInterface(NativeWindow{*buffer_queue_id}); - - const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(*layer_id); - rb.Push<u64>(buffer_size); - } - - void DestroyStrayLayer(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 layer_id = rp.Pop<u64>(); - - LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id); - nvnflinger.DestroyLayer(layer_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetDisplayVsyncEvent(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 display_id = rp.Pop<u64>(); - - LOG_DEBUG(Service_VI, "called. display_id={}", display_id); - - Kernel::KReadableEvent* vsync_event{}; - const auto result = nvnflinger.FindVsyncEvent(&vsync_event, display_id); - if (result != ResultSuccess) { - if (result == ResultNotFound) { - LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - if (vsync_event_fetched) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(VI::ResultPermissionDenied); - return; - } - vsync_event_fetched = true; - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(vsync_event); - } - - void ConvertScalingMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto mode = rp.PopEnum<NintendoScaleMode>(); - LOG_DEBUG(Service_VI, "called mode={}", mode); - - ConvertedScaleMode converted_mode{}; - const auto result = ConvertScalingModeImpl(&converted_mode, mode); - - if (result == ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(converted_mode); - } else { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - } - - void GetIndirectLayerImageMap(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto width = rp.Pop<s64>(); - const auto height = rp.Pop<s64>(); - const auto indirect_layer_consumer_handle = rp.Pop<u64>(); - const auto applet_resource_user_id = rp.Pop<u64>(); - - LOG_WARNING(Service_VI, - "(STUBBED) called, width={}, height={}, indirect_layer_consumer_handle={}, " - "applet_resource_user_id={}", - width, height, indirect_layer_consumer_handle, applet_resource_user_id); - - std::vector<u8> out_buffer(0x46); - ctx.WriteBuffer(out_buffer); - - // TODO: Figure out what these are - - constexpr s64 unknown_result_1 = 0; - constexpr s64 unknown_result_2 = 0; - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(unknown_result_1); - rb.Push(unknown_result_2); - rb.Push(ResultSuccess); - } - - void GetIndirectLayerImageRequiredMemoryInfo(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto width = rp.Pop<u64>(); - const auto height = rp.Pop<u64>(); - LOG_DEBUG(Service_VI, "called width={}, height={}", width, height); - - constexpr u64 base_size = 0x20000; - constexpr u64 alignment = 0x1000; - const auto texture_size = width * height * 4; - const auto out_size = (texture_size + base_size - 1) / base_size * base_size; - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(out_size); - rb.Push(alignment); - } - - static Result ConvertScalingModeImpl(ConvertedScaleMode* out_scaling_mode, - NintendoScaleMode mode) { - switch (mode) { - case NintendoScaleMode::None: - *out_scaling_mode = ConvertedScaleMode::None; - return ResultSuccess; - case NintendoScaleMode::Freeze: - *out_scaling_mode = ConvertedScaleMode::Freeze; - return ResultSuccess; - case NintendoScaleMode::ScaleToWindow: - *out_scaling_mode = ConvertedScaleMode::ScaleToWindow; - return ResultSuccess; - case NintendoScaleMode::ScaleAndCrop: - *out_scaling_mode = ConvertedScaleMode::ScaleAndCrop; - return ResultSuccess; - case NintendoScaleMode::PreserveAspectRatio: - *out_scaling_mode = ConvertedScaleMode::PreserveAspectRatio; - return ResultSuccess; - default: - LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); - return ResultOperationFailed; - } - } - - Nvnflinger::Nvnflinger& nvnflinger; - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; - std::vector<u64> stray_layer_ids; - bool vsync_event_fetched{false}; -}; - -static bool IsValidServiceAccess(Permission permission, Policy policy) { - if (permission == Permission::User) { - return policy == Policy::User; - } - - if (permission == Permission::System || permission == Permission::Manager) { - return policy == Policy::User || policy == Policy::Compositor; - } - - return false; -} - -void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system, - Nvnflinger::Nvnflinger& nvnflinger, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server, - Permission permission) { - IPC::RequestParser rp{ctx}; - const auto policy = rp.PopEnum<Policy>(); - - if (!IsValidServiceAccess(permission, policy)) { - LOG_ERROR(Service_VI, "Permission denied for policy {}", policy); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultPermissionDenied); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IApplicationDisplayService>(system, nvnflinger, hos_binder_driver_server); -} - -void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) { auto server_manager = std::make_unique<ServerManager>(system); + server_manager->RegisterNamedService("vi:m", + std::make_shared<IManagerRootService>(system, container)); + server_manager->RegisterNamedService("vi:s", + std::make_shared<ISystemRootService>(system, container)); server_manager->RegisterNamedService( - "vi:m", std::make_shared<VI_M>(system, nvnflinger, hos_binder_driver_server)); - server_manager->RegisterNamedService( - "vi:s", std::make_shared<VI_S>(system, nvnflinger, hos_binder_driver_server)); - server_manager->RegisterNamedService( - "vi:u", std::make_shared<VI_U>(system, nvnflinger, hos_binder_driver_server)); + "vi:u", std::make_shared<IApplicationRootService>(system, container)); + + std::stop_callback cb(token, [=] { container->OnTerminate(); }); + ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index ee4bcbcfa..7c1f350d8 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -3,52 +3,14 @@ #pragma once -#include "common/common_types.h" +#include "common/polyfill_thread.h" namespace Core { class System; } -namespace Service { -class HLERequestContext; -} - -namespace Service::Nvnflinger { -class HosBinderDriverServer; -class Nvnflinger; -} // namespace Service::Nvnflinger - namespace Service::VI { -enum class DisplayResolution : u32 { - DockedWidth = 1920, - DockedHeight = 1080, - UndockedWidth = 1280, - UndockedHeight = 720, -}; - -/// Permission level for a particular VI service instance -enum class Permission { - User, - System, - Manager, -}; - -/// A policy type that may be requested via GetDisplayService and -/// GetDisplayServiceWithProxyNameExchange -enum class Policy { - User, - Compositor, -}; - -namespace detail { -void GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system, - Nvnflinger::Nvnflinger& nv_flinger, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server, - Permission permission); -} // namespace detail - -void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server); +void LoopProcess(Core::System& system, std::stop_token token); } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp deleted file mode 100644 index 0f06dc2f3..000000000 --- a/src/core/hle/service/vi/vi_m.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/hle/service/vi/vi.h" -#include "core/hle/service/vi/vi_m.h" - -namespace Service::VI { - -VI_M::VI_M(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_) - : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ - hos_binder_driver_server_} { - static const FunctionInfo functions[] = { - {2, &VI_M::GetDisplayService, "GetDisplayService"}, - {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, - {100, nullptr, "PrepareFatal"}, - {101, nullptr, "ShowFatal"}, - {102, nullptr, "DrawFatalRectangle"}, - {103, nullptr, "DrawFatalText32"}, - }; - RegisterHandlers(functions); -} - -VI_M::~VI_M() = default; - -void VI_M::GetDisplayService(HLERequestContext& ctx) { - LOG_DEBUG(Service_VI, "called"); - - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, - Permission::Manager); -} - -} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h deleted file mode 100644 index 9ca6f3905..000000000 --- a/src/core/hle/service/vi/vi_m.h +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Core { -class System; -} - -namespace Service::Nvnflinger { -class HosBinderDriverServer; -class Nvnflinger; -} // namespace Service::Nvnflinger - -namespace Service::VI { - -class VI_M final : public ServiceFramework<VI_M> { -public: - explicit VI_M(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_); - ~VI_M() override; - -private: - void GetDisplayService(HLERequestContext& ctx); - - Nvnflinger::Nvnflinger& nv_flinger; - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; -}; - -} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp deleted file mode 100644 index 77f7a88ff..000000000 --- a/src/core/hle/service/vi/vi_s.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/hle/service/vi/vi.h" -#include "core/hle/service/vi/vi_s.h" - -namespace Service::VI { - -VI_S::VI_S(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_) - : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ - hos_binder_driver_server_} { - static const FunctionInfo functions[] = { - {1, &VI_S::GetDisplayService, "GetDisplayService"}, - {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, - }; - RegisterHandlers(functions); -} - -VI_S::~VI_S() = default; - -void VI_S::GetDisplayService(HLERequestContext& ctx) { - LOG_DEBUG(Service_VI, "called"); - - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, - Permission::System); -} - -} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h deleted file mode 100644 index 157839c91..000000000 --- a/src/core/hle/service/vi/vi_s.h +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Core { -class System; -} - -namespace Service::Nvnflinger { -class HosBinderDriverServer; -class Nvnflinger; -} // namespace Service::Nvnflinger - -namespace Service::VI { - -class VI_S final : public ServiceFramework<VI_S> { -public: - explicit VI_S(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_); - ~VI_S() override; - -private: - void GetDisplayService(HLERequestContext& ctx); - - Nvnflinger::Nvnflinger& nv_flinger; - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; -}; - -} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_types.h b/src/core/hle/service/vi/vi_types.h new file mode 100644 index 000000000..95ff66358 --- /dev/null +++ b/src/core/hle/service/vi/vi_types.h @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" + +namespace Service::VI { + +enum class DisplayResolution : u32 { + DockedWidth = 1920, + DockedHeight = 1080, + UndockedWidth = 1280, + UndockedHeight = 720, +}; + +/// Permission level for a particular VI service instance +enum class Permission { + User, + System, + Manager, +}; + +/// A policy type that may be requested via GetDisplayService and +/// GetDisplayServiceWithProxyNameExchange +enum class Policy : u32 { + User, + Compositor, +}; + +enum class ConvertedScaleMode : u64 { + Freeze = 0, + ScaleToWindow = 1, + ScaleAndCrop = 2, + None = 3, + PreserveAspectRatio = 4, +}; + +enum class NintendoScaleMode : u32 { + None = 0, + Freeze = 1, + ScaleToWindow = 2, + ScaleAndCrop = 3, + PreserveAspectRatio = 4, +}; + +using DisplayName = std::array<char, 0x40>; + +struct DisplayInfo { + /// The name of this particular display. + DisplayName display_name{"Default"}; + + /// Whether or not the display has a limited number of layers. + u8 has_limited_layers{1}; + INSERT_PADDING_BYTES(7); + + /// Indicates the total amount of layers supported by the display. + /// @note This is only valid if has_limited_layers is set. + u64 max_layers{1}; + + /// Maximum width in pixels. + u64 width{1920}; + + /// Maximum height in pixels. + u64 height{1080}; +}; +static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); + +struct DisplayMode { + u32 width; + u32 height; + f32 refresh_rate; + u32 unknown; +}; +static_assert(sizeof(DisplayMode) == 0x10, "DisplayMode has wrong size"); + +class NativeWindow final { +public: + constexpr explicit NativeWindow(s32 id_) : id{static_cast<u64>(id_)} {} + constexpr explicit NativeWindow(const NativeWindow& other) = default; + +private: + const u32 magic = 2; + const u32 process_id = 1; + const u64 id; + INSERT_PADDING_WORDS(2); + std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'}; + INSERT_PADDING_WORDS(2); +}; +static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size"); + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp deleted file mode 100644 index 59e13c86b..000000000 --- a/src/core/hle/service/vi/vi_u.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/hle/service/vi/vi.h" -#include "core/hle/service/vi/vi_u.h" - -namespace Service::VI { - -VI_U::VI_U(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_) - : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ - hos_binder_driver_server_} { - static const FunctionInfo functions[] = { - {0, &VI_U::GetDisplayService, "GetDisplayService"}, - {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, - }; - RegisterHandlers(functions); -} - -VI_U::~VI_U() = default; - -void VI_U::GetDisplayService(HLERequestContext& ctx) { - LOG_DEBUG(Service_VI, "called"); - - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, - Permission::User); -} - -} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h deleted file mode 100644 index 5d9ca54c6..000000000 --- a/src/core/hle/service/vi/vi_u.h +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Core { -class System; -} - -namespace Service::Nvnflinger { -class HosBinderDriverServer; -class Nvnflinger; -} // namespace Service::Nvnflinger - -namespace Service::VI { - -class VI_U final : public ServiceFramework<VI_U> { -public: - explicit VI_U(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_, - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_); - ~VI_U() override; - -private: - void GetDisplayService(HLERequestContext& ctx); - - Nvnflinger::Nvnflinger& nv_flinger; - Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; -}; - -} // namespace Service::VI diff --git a/src/core/hle/service/vi/vsync_manager.cpp b/src/core/hle/service/vi/vsync_manager.cpp new file mode 100644 index 000000000..bdc4dfa96 --- /dev/null +++ b/src/core/hle/service/vi/vsync_manager.cpp @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/os/event.h" +#include "core/hle/service/vi/vsync_manager.h" + +namespace Service::VI { + +VsyncManager::VsyncManager() = default; +VsyncManager::~VsyncManager() = default; + +void VsyncManager::SignalVsync() { + for (auto* event : m_vsync_events) { + event->Signal(); + } +} + +void VsyncManager::LinkVsyncEvent(Event* event) { + m_vsync_events.insert(event); +} + +void VsyncManager::UnlinkVsyncEvent(Event* event) { + m_vsync_events.erase(event); +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vsync_manager.h b/src/core/hle/service/vi/vsync_manager.h new file mode 100644 index 000000000..5d45bb5ee --- /dev/null +++ b/src/core/hle/service/vi/vsync_manager.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <set> + +namespace Service { +class Event; +} + +namespace Service::VI { + +class DisplayList; + +class VsyncManager { +public: + explicit VsyncManager(); + ~VsyncManager(); + + void SignalVsync(); + void LinkVsyncEvent(Event* event); + void UnlinkVsyncEvent(Event* event); + +private: + std::set<Event*> m_vsync_events; +}; + +} // namespace Service::VI diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 2a32b1276..de27ec49e 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -118,7 +118,9 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function<bool(size_t, size_t)> mbedtls_sha256_starts_ret(&ctx, 0); // Ensure we maintain a clean state on exit. - SCOPE_EXIT({ mbedtls_sha256_free(&ctx); }); + SCOPE_EXIT { + mbedtls_sha256_free(&ctx); + }; // Declare counters. const size_t total_size = file->GetSize(); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index e10a4601e..8775369a4 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -831,11 +831,11 @@ struct Memory::Impl { if (core == sys_core) [[unlikely]] { sys_core_guard.lock(); } - SCOPE_EXIT({ + SCOPE_EXIT { if (core == sys_core) [[unlikely]] { sys_core_guard.unlock(); } - }); + }; gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) { auto& current_area = rasterizer_write_areas[core]; PAddr subaddress = address >> YUZU_PAGEBITS; @@ -866,11 +866,11 @@ struct Memory::Impl { if (core == sys_core) [[unlikely]] { sys_core_guard.lock(); } - SCOPE_EXIT({ + SCOPE_EXIT { if (core == sys_core) [[unlikely]] { sys_core_guard.unlock(); } - }); + }; auto& gpu = system.GPU(); gpu_device_memory->ApplyOpOnPointer( p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); }); diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index b84b57d92..d8921e565 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -117,9 +117,9 @@ bool StandardVmCallbacks::IsAddressInRange(VAddr in) const { (in < metadata.heap_extents.base || in >= metadata.heap_extents.base + metadata.heap_extents.size) && (in < metadata.alias_extents.base || - in >= metadata.heap_extents.base + metadata.alias_extents.size) && + in >= metadata.alias_extents.base + metadata.alias_extents.size) && (in < metadata.aslr_extents.base || - in >= metadata.heap_extents.base + metadata.aslr_extents.size)) { + in >= metadata.aslr_extents.base + metadata.aslr_extents.size)) { LOG_DEBUG(CheatEngine, "Cheat attempting to access memory at invalid address={:016X}, if this " "persists, " diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp index f7097d01d..caceeec4f 100644 --- a/src/core/memory/dmnt_cheat_vm.cpp +++ b/src/core/memory/dmnt_cheat_vm.cpp @@ -224,12 +224,12 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { // If we've ever seen a decode failure, return false. bool valid = decode_success; CheatVmOpcode opcode = {}; - SCOPE_EXIT({ + SCOPE_EXIT { decode_success &= valid; if (valid) { out = opcode; } - }); + }; // Helper function for getting instruction dwords. const auto GetNextDword = [&] { diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp index af6b10db6..95f8c8c36 100644 --- a/src/frontend_common/config.cpp +++ b/src/frontend_common/config.cpp @@ -138,6 +138,7 @@ void Config::ReadPlayerValues(const std::size_t player_index) { if (profile_name.empty()) { // Use the global input config player = Settings::values.players.GetValue(true)[player_index]; + player.profile_name = ""; return; } player.profile_name = profile_name; @@ -401,6 +402,14 @@ void Config::ReadNetworkValues() { EndGroup(); } +void Config::ReadLibraryAppletValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet)); + + ReadCategory(Settings::Category::LibraryApplet); + + EndGroup(); +} + void Config::ReadValues() { if (global) { ReadDataStorageValues(); @@ -410,6 +419,7 @@ void Config::ReadValues() { ReadServiceValues(); ReadWebServiceValues(); ReadMiscellaneousValues(); + ReadLibraryAppletValues(); } ReadControlValues(); ReadCoreValues(); @@ -511,6 +521,7 @@ void Config::SaveValues() { SaveNetworkValues(); SaveWebServiceValues(); SaveMiscellaneousValues(); + SaveLibraryAppletValues(); } else { LOG_DEBUG(Config, "Saving only generic configuration values"); } @@ -691,6 +702,14 @@ void Config::SaveWebServiceValues() { EndGroup(); } +void Config::SaveLibraryAppletValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet)); + + WriteCategory(Settings::Category::LibraryApplet); + + EndGroup(); +} + bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) { std::string full_key = GetFullKey(key, false); if (!default_value.has_value()) { diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h index 4ecb97044..8b0599cc3 100644 --- a/src/frontend_common/config.h +++ b/src/frontend_common/config.h @@ -88,6 +88,7 @@ protected: void ReadSystemValues(); void ReadWebServiceValues(); void ReadNetworkValues(); + void ReadLibraryAppletValues(); // Read platform specific sections virtual void ReadHidbusValues() = 0; @@ -121,6 +122,7 @@ protected: void SaveScreenshotValues(); void SaveSystemValues(); void SaveWebServiceValues(); + void SaveLibraryAppletValues(); // Save platform specific sections virtual void SaveHidbusValues() = 0; diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h index f3efe3465..c4e97a47b 100644 --- a/src/frontend_common/content_manager.h +++ b/src/frontend_common/content_manager.h @@ -251,11 +251,12 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string& * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the installed contents and the second is the current progress. If * you return true to the callback, it will cancel the installation as soon as possible. + * \param firmware_only Set to true to only scan system nand NCAs (firmware), post firmware install. * \return A list of entries that failed to install. Returns an empty vector if successful. */ inline std::vector<std::string> VerifyInstalledContents( Core::System& system, FileSys::ManualContentProvider& provider, - const std::function<bool(size_t, size_t)>& callback) { + const std::function<bool(size_t, size_t)>& callback, bool firmware_only = false) { // Get content registries. auto bis_contents = system.GetFileSystemController().GetSystemNANDContents(); auto user_contents = system.GetFileSystemController().GetUserNANDContents(); @@ -264,7 +265,7 @@ inline std::vector<std::string> VerifyInstalledContents( if (bis_contents) { content_providers.push_back(bis_contents); } - if (user_contents) { + if (user_contents && !firmware_only) { content_providers.push_back(user_contents); } diff --git a/src/hid_core/frontend/emulated_controller.cpp b/src/hid_core/frontend/emulated_controller.cpp index 819460eb5..5cd26819c 100644 --- a/src/hid_core/frontend/emulated_controller.cpp +++ b/src/hid_core/frontend/emulated_controller.cpp @@ -174,18 +174,25 @@ void EmulatedController::LoadDevices() { // Only map virtual devices to the first controller if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) { camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; - ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; +#ifdef HAVE_LIBUSB + ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; +#endif +#ifdef ANDROID + android_params = Common::ParamPackage{"engine:android,port:100"}; +#endif } output_params[LeftIndex] = left_joycon; output_params[RightIndex] = right_joycon; output_params[2] = camera_params[1]; output_params[3] = nfc_params[0]; + output_params[4] = android_params; output_params[LeftIndex].Set("output", true); output_params[RightIndex].Set("output", true); output_params[2].Set("output", true); output_params[3].Set("output", true); + output_params[4].Set("output", true); LoadTASParams(); LoadVirtualGamepadParams(); @@ -578,6 +585,9 @@ void EmulatedController::DisableConfiguration() { // Get Joycon colors before turning on the controller for (const auto& color_device : color_devices) { + if (color_device == nullptr) { + continue; + } color_device->ForceUpdate(); } @@ -923,8 +933,9 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, if (index >= controller.stick_values.size()) { return; } - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); }); + auto trigger_guard = SCOPE_GUARD { + TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); + }; std::scoped_lock lock{mutex}; const auto stick_value = TransformToStick(callback); @@ -979,8 +990,9 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac if (index >= controller.trigger_values.size()) { return; } - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); }); + auto trigger_guard = SCOPE_GUARD { + TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); + }; std::scoped_lock lock{mutex}; const auto trigger_value = TransformToTrigger(callback); @@ -1026,7 +1038,9 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback if (index >= controller.motion_values.size()) { return; } - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); }); + SCOPE_EXIT { + TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); + }; std::scoped_lock lock{mutex}; auto& raw_status = controller.motion_values[index].raw_status; auto& emulated = controller.motion_values[index].emulated; @@ -1060,8 +1074,9 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback if (index >= controller.color_values.size()) { return; } - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); }); + auto trigger_guard = SCOPE_GUARD { + TriggerOnChange(ControllerTriggerType::Color, !is_configuring); + }; std::scoped_lock lock{mutex}; controller.color_values[index] = TransformToColor(callback); @@ -1110,7 +1125,9 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac if (index >= controller.battery_values.size()) { return; } - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); }); + SCOPE_EXIT { + TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); + }; std::scoped_lock lock{mutex}; controller.battery_values[index] = TransformToBattery(callback); @@ -1173,7 +1190,9 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac } void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) { - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); }); + SCOPE_EXIT { + TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); + }; std::scoped_lock lock{mutex}; controller.camera_values = TransformToCamera(callback); @@ -1188,7 +1207,9 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback } void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) { - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); }); + SCOPE_EXIT { + TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); + }; std::scoped_lock lock{mutex}; const auto force_value = TransformToStick(callback); @@ -1202,7 +1223,9 @@ void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& call } void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { - SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); }); + SCOPE_EXIT { + TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); + }; std::scoped_lock lock{mutex}; controller.nfc_values = TransformToNfc(callback); @@ -1277,6 +1300,10 @@ bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationV .high_frequency = vibration.high_frequency, .type = type, }; + + // Send vibrations to Android's input overlay + output_devices[4]->SetVibration(status); + return output_devices[index]->SetVibration(status) == Common::Input::DriverResult::Success; } @@ -1671,8 +1698,9 @@ void EmulatedController::Connect(bool use_temporary_value) { return; } - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); }); + auto trigger_guard = SCOPE_GUARD { + TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); + }; std::scoped_lock lock{connect_mutex, mutex}; if (is_configuring) { tmp_is_connected = true; @@ -1687,8 +1715,9 @@ void EmulatedController::Connect(bool use_temporary_value) { } void EmulatedController::Disconnect() { - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); }); + auto trigger_guard = SCOPE_GUARD { + TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); + }; std::scoped_lock lock{connect_mutex, mutex}; if (is_configuring) { tmp_is_connected = false; @@ -1724,8 +1753,9 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c } void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { - auto trigger_guard = - SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); }); + auto trigger_guard = SCOPE_GUARD { + TriggerOnChange(ControllerTriggerType::Type, !is_configuring); + }; std::scoped_lock lock{mutex, npad_mutex}; if (is_configuring) { diff --git a/src/hid_core/frontend/emulated_controller.h b/src/hid_core/frontend/emulated_controller.h index 701b38300..ab3c6fcd3 100644 --- a/src/hid_core/frontend/emulated_controller.h +++ b/src/hid_core/frontend/emulated_controller.h @@ -21,7 +21,7 @@ namespace Core::HID { const std::size_t max_emulated_controllers = 2; -const std::size_t output_devices_size = 4; +const std::size_t output_devices_size = 5; struct ControllerMotionInfo { Common::Input::MotionStatus raw_status{}; MotionInput emulated{}; @@ -597,6 +597,7 @@ private: CameraParams camera_params; RingAnalogParams ring_params; NfcParams nfc_params; + Common::ParamPackage android_params; OutputParams output_params; ButtonDevices button_devices; diff --git a/src/hid_core/resources/hid_firmware_settings.cpp b/src/hid_core/resources/hid_firmware_settings.cpp index b32c0660a..c0a76d0eb 100644 --- a/src/hid_core/resources/hid_firmware_settings.cpp +++ b/src/hid_core/resources/hid_firmware_settings.cpp @@ -22,29 +22,30 @@ void HidFirmwareSettings::LoadSettings(bool reload_config) { return; } - m_set_sys->GetSettingsItemValue<bool>(is_debug_pad_enabled, "hid_debug", "enables_debugpad"); - m_set_sys->GetSettingsItemValue<bool>(is_device_managed, "hid_debug", "manages_devices"); - m_set_sys->GetSettingsItemValue<bool>(is_touch_i2c_managed, "hid_debug", - "manages_touch_ic_i2c"); - m_set_sys->GetSettingsItemValue<bool>(is_future_devices_emulated, "hid_debug", - "emulate_future_device"); - m_set_sys->GetSettingsItemValue<bool>(is_mcu_hardware_error_emulated, "hid_debug", - "emulate_mcu_hardware_error"); - m_set_sys->GetSettingsItemValue<bool>(is_rail_enabled, "hid_debug", "enables_rail"); - m_set_sys->GetSettingsItemValue<bool>(is_firmware_update_failure_emulated, "hid_debug", - "emulate_firmware_update_failure"); + m_set_sys->GetSettingsItemValueImpl<bool>(is_debug_pad_enabled, "hid_debug", + "enables_debugpad"); + m_set_sys->GetSettingsItemValueImpl<bool>(is_device_managed, "hid_debug", "manages_devices"); + m_set_sys->GetSettingsItemValueImpl<bool>(is_touch_i2c_managed, "hid_debug", + "manages_touch_ic_i2c"); + m_set_sys->GetSettingsItemValueImpl<bool>(is_future_devices_emulated, "hid_debug", + "emulate_future_device"); + m_set_sys->GetSettingsItemValueImpl<bool>(is_mcu_hardware_error_emulated, "hid_debug", + "emulate_mcu_hardware_error"); + m_set_sys->GetSettingsItemValueImpl<bool>(is_rail_enabled, "hid_debug", "enables_rail"); + m_set_sys->GetSettingsItemValueImpl<bool>(is_firmware_update_failure_emulated, "hid_debug", + "emulate_firmware_update_failure"); is_firmware_update_failure = {}; - m_set_sys->GetSettingsItemValue<bool>(is_ble_disabled, "hid_debug", "ble_disabled"); - m_set_sys->GetSettingsItemValue<bool>(is_dscale_disabled, "hid_debug", "dscale_disabled"); - m_set_sys->GetSettingsItemValue<bool>(is_handheld_forced, "hid_debug", "force_handheld"); + m_set_sys->GetSettingsItemValueImpl<bool>(is_ble_disabled, "hid_debug", "ble_disabled"); + m_set_sys->GetSettingsItemValueImpl<bool>(is_dscale_disabled, "hid_debug", "dscale_disabled"); + m_set_sys->GetSettingsItemValueImpl<bool>(is_handheld_forced, "hid_debug", "force_handheld"); features_per_id_disabled = {}; - m_set_sys->GetSettingsItemValue<bool>(is_touch_firmware_auto_update_disabled, "hid_debug", - "touch_firmware_auto_update_disabled"); + m_set_sys->GetSettingsItemValueImpl<bool>(is_touch_firmware_auto_update_disabled, "hid_debug", + "touch_firmware_auto_update_disabled"); bool has_rail_interface{}; bool has_sio_mcu{}; - m_set_sys->GetSettingsItemValue<bool>(has_rail_interface, "hid", "has_rail_interface"); - m_set_sys->GetSettingsItemValue<bool>(has_sio_mcu, "hid", "has_sio_mcu"); + m_set_sys->GetSettingsItemValueImpl<bool>(has_rail_interface, "hid", "has_rail_interface"); + m_set_sys->GetSettingsItemValueImpl<bool>(has_sio_mcu, "hid", "has_sio_mcu"); platform_config.has_rail_interface.Assign(has_rail_interface); platform_config.has_sio_mcu.Assign(has_sio_mcu); diff --git a/src/hid_core/resources/npad/npad_vibration.cpp b/src/hid_core/resources/npad/npad_vibration.cpp index 02b1f0290..4c103889a 100644 --- a/src/hid_core/resources/npad/npad_vibration.cpp +++ b/src/hid_core/resources/npad/npad_vibration.cpp @@ -15,7 +15,7 @@ Result NpadVibration::Activate() { std::scoped_lock lock{mutex}; f32 master_volume = 1.0f; - m_set_sys->GetVibrationMasterVolume(master_volume); + m_set_sys->GetVibrationMasterVolume(&master_volume); if (master_volume < 0.0f || master_volume > 1.0f) { return ResultVibrationStrengthOutOfRange; } @@ -57,7 +57,7 @@ Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const { std::scoped_lock lock{mutex}; f32 master_volume = 1.0f; - m_set_sys->GetVibrationMasterVolume(master_volume); + m_set_sys->GetVibrationMasterVolume(&master_volume); if (master_volume < 0.0f || master_volume > 1.0f) { return ResultVibrationStrengthOutOfRange; } @@ -77,7 +77,7 @@ Result NpadVibration::EndPermitVibrationSession() { std::scoped_lock lock{mutex}; f32 master_volume = 1.0f; - m_set_sys->GetVibrationMasterVolume(master_volume); + m_set_sys->GetVibrationMasterVolume(&master_volume); if (master_volume < 0.0f || master_volume > 1.0f) { return ResultVibrationStrengthOutOfRange; } diff --git a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp index c39321915..79ddaa4df 100644 --- a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp +++ b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp @@ -48,7 +48,7 @@ Result TouchResource::ActivateTouch() { } Set::TouchScreenMode touch_mode{Set::TouchScreenMode::Standard}; - m_set_sys->GetTouchScreenMode(touch_mode); + m_set_sys->GetTouchScreenMode(&touch_mode); default_touch_screen_mode = static_cast<Core::HID::TouchScreenModeForNx>(touch_mode); global_ref_counter++; diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index d0a71a15b..d455323e0 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -2,8 +2,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later add_library(input_common STATIC - drivers/android.cpp - drivers/android.h drivers/camera.cpp drivers/camera.h drivers/keyboard.cpp @@ -94,3 +92,11 @@ target_link_libraries(input_common PUBLIC hid_core PRIVATE common Boost::headers if (YUZU_USE_PRECOMPILED_HEADERS) target_precompile_headers(input_common PRIVATE precompiled_headers.h) endif() + +if (ANDROID) + target_sources(input_common PRIVATE + drivers/android.cpp + drivers/android.h + ) + target_link_libraries(input_common PRIVATE android) +endif() diff --git a/src/input_common/drivers/android.cpp b/src/input_common/drivers/android.cpp index b6a03fdc0..e859cc538 100644 --- a/src/input_common/drivers/android.cpp +++ b/src/input_common/drivers/android.cpp @@ -1,30 +1,47 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include <set> +#include <common/settings_input.h> +#include <jni.h> +#include "common/android/android_common.h" +#include "common/android/id_cache.h" #include "input_common/drivers/android.h" namespace InputCommon { Android::Android(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} -void Android::RegisterController(std::size_t controller_number) { - PreSetController(GetIdentifier(controller_number)); +void Android::RegisterController(jobject j_input_device) { + auto env = Common::Android::GetEnvForThread(); + const std::string guid = Common::Android::GetJString( + env, static_cast<jstring>( + env->CallObjectMethod(j_input_device, Common::Android::GetYuzuDeviceGetGUID()))); + const s32 port = env->CallIntMethod(j_input_device, Common::Android::GetYuzuDeviceGetPort()); + const auto identifier = GetIdentifier(guid, static_cast<size_t>(port)); + PreSetController(identifier); + + if (input_devices.find(identifier) != input_devices.end()) { + env->DeleteGlobalRef(input_devices[identifier]); + } + auto new_device = env->NewGlobalRef(j_input_device); + input_devices[identifier] = new_device; } -void Android::SetButtonState(std::size_t controller_number, int button_id, bool value) { - const auto identifier = GetIdentifier(controller_number); +void Android::SetButtonState(std::string guid, size_t port, int button_id, bool value) { + const auto identifier = GetIdentifier(guid, port); SetButton(identifier, button_id, value); } -void Android::SetAxisState(std::size_t controller_number, int axis_id, float value) { - const auto identifier = GetIdentifier(controller_number); +void Android::SetAxisPosition(std::string guid, size_t port, int axis_id, float value) { + const auto identifier = GetIdentifier(guid, port); SetAxis(identifier, axis_id, value); } -void Android::SetMotionState(std::size_t controller_number, u64 delta_timestamp, float gyro_x, +void Android::SetMotionState(std::string guid, size_t port, u64 delta_timestamp, float gyro_x, float gyro_y, float gyro_z, float accel_x, float accel_y, float accel_z) { - const auto identifier = GetIdentifier(controller_number); + const auto identifier = GetIdentifier(guid, port); const BasicMotion motion_data{ .gyro_x = gyro_x, .gyro_y = gyro_y, @@ -37,10 +54,295 @@ void Android::SetMotionState(std::size_t controller_number, u64 delta_timestamp, SetMotion(identifier, 0, motion_data); } -PadIdentifier Android::GetIdentifier(std::size_t controller_number) const { +Common::Input::DriverResult Android::SetVibration( + [[maybe_unused]] const PadIdentifier& identifier, + [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { + auto device = input_devices.find(identifier); + if (device != input_devices.end()) { + Common::Android::RunJNIOnFiber<void>([&](JNIEnv* env) { + float average_intensity = + static_cast<float>((vibration.high_amplitude + vibration.low_amplitude) / 2.0); + env->CallVoidMethod(device->second, Common::Android::GetYuzuDeviceVibrate(), + average_intensity); + }); + return Common::Input::DriverResult::Success; + } + return Common::Input::DriverResult::NotSupported; +} + +bool Android::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { + auto device = input_devices.find(identifier); + if (device != input_devices.end()) { + return Common::Android::RunJNIOnFiber<bool>([&](JNIEnv* env) { + return static_cast<bool>(env->CallBooleanMethod( + device->second, Common::Android::GetYuzuDeviceGetSupportsVibration())); + }); + } + return false; +} + +std::vector<Common::ParamPackage> Android::GetInputDevices() const { + std::vector<Common::ParamPackage> devices; + auto env = Common::Android::GetEnvForThread(); + for (const auto& [key, value] : input_devices) { + auto name_object = static_cast<jstring>( + env->CallObjectMethod(value, Common::Android::GetYuzuDeviceGetName())); + const std::string name = + fmt::format("{} {}", Common::Android::GetJString(env, name_object), key.port); + devices.emplace_back(Common::ParamPackage{ + {"engine", GetEngineName()}, + {"display", std::move(name)}, + {"guid", key.guid.RawString()}, + {"port", std::to_string(key.port)}, + }); + } + return devices; +} + +std::set<s32> Android::GetDeviceAxes(JNIEnv* env, jobject& j_device) const { + auto j_axes = static_cast<jobjectArray>( + env->CallObjectMethod(j_device, Common::Android::GetYuzuDeviceGetAxes())); + std::set<s32> axes; + for (int i = 0; i < env->GetArrayLength(j_axes); ++i) { + jobject axis = env->GetObjectArrayElement(j_axes, i); + axes.insert(env->GetIntField(axis, Common::Android::GetIntegerValueField())); + } + return axes; +} + +Common::ParamPackage Android::BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x, + int axis_y) const { + Common::ParamPackage params; + params.Set("engine", GetEngineName()); + params.Set("port", static_cast<int>(identifier.port)); + params.Set("guid", identifier.guid.RawString()); + params.Set("axis_x", axis_x); + params.Set("axis_y", axis_y); + params.Set("offset_x", 0); + params.Set("offset_y", 0); + params.Set("invert_x", "+"); + + // Invert Y-Axis by default + params.Set("invert_y", "-"); + return params; +} + +Common::ParamPackage Android::BuildAnalogParamPackageForButton(PadIdentifier identifier, s32 axis, + bool invert) const { + Common::ParamPackage params{}; + params.Set("engine", GetEngineName()); + params.Set("port", static_cast<int>(identifier.port)); + params.Set("guid", identifier.guid.RawString()); + params.Set("axis", axis); + params.Set("threshold", "0.5"); + params.Set("invert", invert ? "-" : "+"); + return params; +} + +Common::ParamPackage Android::BuildButtonParamPackageForButton(PadIdentifier identifier, + s32 button) const { + Common::ParamPackage params{}; + params.Set("engine", GetEngineName()); + params.Set("port", static_cast<int>(identifier.port)); + params.Set("guid", identifier.guid.RawString()); + params.Set("button", button); + return params; +} + +bool Android::MatchVID(Common::UUID device, const std::vector<std::string>& vids) const { + for (size_t i = 0; i < vids.size(); ++i) { + auto fucker = device.RawString(); + if (fucker.find(vids[i]) != std::string::npos) { + return true; + } + } + return false; +} + +AnalogMapping Android::GetAnalogMappingForDevice(const Common::ParamPackage& params) { + if (!params.Has("guid") || !params.Has("port")) { + return {}; + } + + auto identifier = + GetIdentifier(params.Get("guid", ""), static_cast<size_t>(params.Get("port", 0))); + auto& j_device = input_devices[identifier]; + if (j_device == nullptr) { + return {}; + } + + auto env = Common::Android::GetEnvForThread(); + std::set<s32> axes = GetDeviceAxes(env, j_device); + if (axes.size() == 0) { + return {}; + } + + AnalogMapping mapping = {}; + if (axes.find(AXIS_X) != axes.end() && axes.find(AXIS_Y) != axes.end()) { + mapping.insert_or_assign(Settings::NativeAnalog::LStick, + BuildParamPackageForAnalog(identifier, AXIS_X, AXIS_Y)); + } + + if (axes.find(AXIS_RX) != axes.end() && axes.find(AXIS_RY) != axes.end()) { + mapping.insert_or_assign(Settings::NativeAnalog::RStick, + BuildParamPackageForAnalog(identifier, AXIS_RX, AXIS_RY)); + } else if (axes.find(AXIS_Z) != axes.end() && axes.find(AXIS_RZ) != axes.end()) { + mapping.insert_or_assign(Settings::NativeAnalog::RStick, + BuildParamPackageForAnalog(identifier, AXIS_Z, AXIS_RZ)); + } + return mapping; +} + +ButtonMapping Android::GetButtonMappingForDevice(const Common::ParamPackage& params) { + if (!params.Has("guid") || !params.Has("port")) { + return {}; + } + + auto identifier = + GetIdentifier(params.Get("guid", ""), static_cast<size_t>(params.Get("port", 0))); + auto& j_device = input_devices[identifier]; + if (j_device == nullptr) { + return {}; + } + + auto env = Common::Android::GetEnvForThread(); + jintArray j_keys = env->NewIntArray(static_cast<int>(keycode_ids.size())); + env->SetIntArrayRegion(j_keys, 0, static_cast<int>(keycode_ids.size()), keycode_ids.data()); + auto j_has_keys_object = static_cast<jbooleanArray>( + env->CallObjectMethod(j_device, Common::Android::GetYuzuDeviceHasKeys(), j_keys)); + jboolean isCopy = false; + jboolean* j_has_keys = env->GetBooleanArrayElements(j_has_keys_object, &isCopy); + + std::set<s32> available_keys; + for (size_t i = 0; i < keycode_ids.size(); ++i) { + if (j_has_keys[i]) { + available_keys.insert(keycode_ids[i]); + } + } + + // Some devices use axes instead of buttons for certain controls so we need all the axes here + std::set<s32> axes = GetDeviceAxes(env, j_device); + + ButtonMapping mapping = {}; + if (axes.find(AXIS_HAT_X) != axes.end() && axes.find(AXIS_HAT_Y) != axes.end()) { + mapping.insert_or_assign(Settings::NativeButton::DUp, + BuildAnalogParamPackageForButton(identifier, AXIS_HAT_Y, true)); + mapping.insert_or_assign(Settings::NativeButton::DDown, + BuildAnalogParamPackageForButton(identifier, AXIS_HAT_Y, false)); + mapping.insert_or_assign(Settings::NativeButton::DLeft, + BuildAnalogParamPackageForButton(identifier, AXIS_HAT_X, true)); + mapping.insert_or_assign(Settings::NativeButton::DRight, + BuildAnalogParamPackageForButton(identifier, AXIS_HAT_X, false)); + } else if (available_keys.find(KEYCODE_DPAD_UP) != available_keys.end() && + available_keys.find(KEYCODE_DPAD_DOWN) != available_keys.end() && + available_keys.find(KEYCODE_DPAD_LEFT) != available_keys.end() && + available_keys.find(KEYCODE_DPAD_RIGHT) != available_keys.end()) { + mapping.insert_or_assign(Settings::NativeButton::DUp, + BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_UP)); + mapping.insert_or_assign(Settings::NativeButton::DDown, + BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_DOWN)); + mapping.insert_or_assign(Settings::NativeButton::DLeft, + BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_LEFT)); + mapping.insert_or_assign(Settings::NativeButton::DRight, + BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_RIGHT)); + } + + if (axes.find(AXIS_LTRIGGER) != axes.end()) { + mapping.insert_or_assign(Settings::NativeButton::ZL, BuildAnalogParamPackageForButton( + identifier, AXIS_LTRIGGER, false)); + } else if (available_keys.find(KEYCODE_BUTTON_L2) != available_keys.end()) { + mapping.insert_or_assign(Settings::NativeButton::ZL, + BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_L2)); + } + + if (axes.find(AXIS_RTRIGGER) != axes.end()) { + mapping.insert_or_assign(Settings::NativeButton::ZR, BuildAnalogParamPackageForButton( + identifier, AXIS_RTRIGGER, false)); + } else if (available_keys.find(KEYCODE_BUTTON_R2) != available_keys.end()) { + mapping.insert_or_assign(Settings::NativeButton::ZR, + BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_R2)); + } + + if (available_keys.find(KEYCODE_BUTTON_A) != available_keys.end()) { + if (MatchVID(identifier.guid, flipped_ab_vids)) { + mapping.insert_or_assign(Settings::NativeButton::B, BuildButtonParamPackageForButton( + identifier, KEYCODE_BUTTON_A)); + } else { + mapping.insert_or_assign(Settings::NativeButton::A, BuildButtonParamPackageForButton( + identifier, KEYCODE_BUTTON_A)); + } + } + if (available_keys.find(KEYCODE_BUTTON_B) != available_keys.end()) { + if (MatchVID(identifier.guid, flipped_ab_vids)) { + mapping.insert_or_assign(Settings::NativeButton::A, BuildButtonParamPackageForButton( + identifier, KEYCODE_BUTTON_B)); + } else { + mapping.insert_or_assign(Settings::NativeButton::B, BuildButtonParamPackageForButton( + identifier, KEYCODE_BUTTON_B)); + } + } + if (available_keys.find(KEYCODE_BUTTON_X) != available_keys.end()) { + if (MatchVID(identifier.guid, flipped_xy_vids)) { + mapping.insert_or_assign(Settings::NativeButton::Y, BuildButtonParamPackageForButton( + identifier, KEYCODE_BUTTON_X)); + } else { + mapping.insert_or_assign(Settings::NativeButton::X, BuildButtonParamPackageForButton( + identifier, KEYCODE_BUTTON_X)); + } + } + if (available_keys.find(KEYCODE_BUTTON_Y) != available_keys.end()) { + if (MatchVID(identifier.guid, flipped_xy_vids)) { + mapping.insert_or_assign(Settings::NativeButton::X, BuildButtonParamPackageForButton( + identifier, KEYCODE_BUTTON_Y)); + } else { + mapping.insert_or_assign(Settings::NativeButton::Y, BuildButtonParamPackageForButton( + identifier, KEYCODE_BUTTON_Y)); + } + } + + if (available_keys.find(KEYCODE_BUTTON_L1) != available_keys.end()) { + mapping.insert_or_assign(Settings::NativeButton::L, + BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_L1)); + } + if (available_keys.find(KEYCODE_BUTTON_R1) != available_keys.end()) { + mapping.insert_or_assign(Settings::NativeButton::R, + BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_R1)); + } + + if (available_keys.find(KEYCODE_BUTTON_THUMBL) != available_keys.end()) { + mapping.insert_or_assign( + Settings::NativeButton::LStick, + BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_THUMBL)); + } + if (available_keys.find(KEYCODE_BUTTON_THUMBR) != available_keys.end()) { + mapping.insert_or_assign( + Settings::NativeButton::RStick, + BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_THUMBR)); + } + + if (available_keys.find(KEYCODE_BUTTON_START) != available_keys.end()) { + mapping.insert_or_assign( + Settings::NativeButton::Plus, + BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_START)); + } + if (available_keys.find(KEYCODE_BUTTON_SELECT) != available_keys.end()) { + mapping.insert_or_assign( + Settings::NativeButton::Minus, + BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_SELECT)); + } + + return mapping; +} + +Common::Input::ButtonNames Android::GetUIName( + [[maybe_unused]] const Common::ParamPackage& params) const { + return Common::Input::ButtonNames::Value; +} + +PadIdentifier Android::GetIdentifier(const std::string& guid, size_t port) const { return { - .guid = Common::UUID{}, - .port = controller_number, + .guid = Common::UUID{guid}, + .port = port, .pad = 0, }; } diff --git a/src/input_common/drivers/android.h b/src/input_common/drivers/android.h index 3f01817f6..8a386c1b1 100644 --- a/src/input_common/drivers/android.h +++ b/src/input_common/drivers/android.h @@ -3,6 +3,8 @@ #pragma once +#include <set> +#include <jni.h> #include "input_common/input_engine.h" namespace InputCommon { @@ -15,40 +17,122 @@ public: explicit Android(std::string input_engine_); /** - * Registers controller number to accept new inputs - * @param controller_number the controller number that will take this action + * Registers controller number to accept new inputs. + * @param j_input_device YuzuInputDevice object from the Android frontend to register. */ - void RegisterController(std::size_t controller_number); + void RegisterController(jobject j_input_device); /** - * Sets the status of all buttons bound with the key to pressed - * @param controller_number the controller number that will take this action - * @param button_id the id of the button - * @param value indicates if the button is pressed or not + * Sets the status of a button on a specific controller. + * @param guid 32 character hexadecimal string consisting of the controller's PID+VID. + * @param port Port determined by controller connection order. + * @param button_id The Android Keycode corresponding to this event. + * @param value Whether the button is pressed or not. */ - void SetButtonState(std::size_t controller_number, int button_id, bool value); + void SetButtonState(std::string guid, size_t port, int button_id, bool value); /** - * Sets the status of a analog input to a specific player index - * @param controller_number the controller number that will take this action - * @param axis_id the id of the axis to move - * @param value the analog position of the axis + * Sets the status of an axis on a specific controller. + * @param guid 32 character hexadecimal string consisting of the controller's PID+VID. + * @param port Port determined by controller connection order. + * @param axis_id The Android axis ID corresponding to this event. + * @param value Value along the given axis. */ - void SetAxisState(std::size_t controller_number, int axis_id, float value); + void SetAxisPosition(std::string guid, size_t port, int axis_id, float value); /** - * Sets the status of the motion sensor to a specific player index - * @param controller_number the controller number that will take this action - * @param delta_timestamp time passed since last reading - * @param gyro_x,gyro_y,gyro_z the gyro sensor readings - * @param accel_x,accel_y,accel_z the accelerometer reading + * Sets the status of the motion sensor on a specific controller + * @param guid 32 character hexadecimal string consisting of the controller's PID+VID. + * @param port Port determined by controller connection order. + * @param delta_timestamp Time passed since the last read. + * @param gyro_x,gyro_y,gyro_z Gyro sensor readings. + * @param accel_x,accel_y,accel_z Accelerometer sensor readings. */ - void SetMotionState(std::size_t controller_number, u64 delta_timestamp, float gyro_x, + void SetMotionState(std::string guid, size_t port, u64 delta_timestamp, float gyro_x, float gyro_y, float gyro_z, float accel_x, float accel_y, float accel_z); + Common::Input::DriverResult SetVibration( + const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; + + bool IsVibrationEnabled(const PadIdentifier& identifier) override; + + std::vector<Common::ParamPackage> GetInputDevices() const override; + + /** + * Gets the axes reported by the YuzuInputDevice. + * @param env JNI environment pointer. + * @param j_device YuzuInputDevice from the Android frontend. + * @return Set of the axes reported by the underlying Android InputDevice + */ + std::set<s32> GetDeviceAxes(JNIEnv* env, jobject& j_device) const; + + Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x, + int axis_y) const; + + Common::ParamPackage BuildAnalogParamPackageForButton(PadIdentifier identifier, s32 axis, + bool invert) const; + + Common::ParamPackage BuildButtonParamPackageForButton(PadIdentifier identifier, + s32 button) const; + + bool MatchVID(Common::UUID device, const std::vector<std::string>& vids) const; + + AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; + + ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; + + Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override; + private: + std::unordered_map<PadIdentifier, jobject> input_devices; + /// Returns the correct identifier corresponding to the player index - PadIdentifier GetIdentifier(std::size_t controller_number) const; + PadIdentifier GetIdentifier(const std::string& guid, size_t port) const; + + static constexpr s32 AXIS_X = 0; + static constexpr s32 AXIS_Y = 1; + static constexpr s32 AXIS_Z = 11; + static constexpr s32 AXIS_RX = 12; + static constexpr s32 AXIS_RY = 13; + static constexpr s32 AXIS_RZ = 14; + static constexpr s32 AXIS_HAT_X = 15; + static constexpr s32 AXIS_HAT_Y = 16; + static constexpr s32 AXIS_LTRIGGER = 17; + static constexpr s32 AXIS_RTRIGGER = 18; + + static constexpr s32 KEYCODE_DPAD_UP = 19; + static constexpr s32 KEYCODE_DPAD_DOWN = 20; + static constexpr s32 KEYCODE_DPAD_LEFT = 21; + static constexpr s32 KEYCODE_DPAD_RIGHT = 22; + static constexpr s32 KEYCODE_BUTTON_A = 96; + static constexpr s32 KEYCODE_BUTTON_B = 97; + static constexpr s32 KEYCODE_BUTTON_X = 99; + static constexpr s32 KEYCODE_BUTTON_Y = 100; + static constexpr s32 KEYCODE_BUTTON_L1 = 102; + static constexpr s32 KEYCODE_BUTTON_R1 = 103; + static constexpr s32 KEYCODE_BUTTON_L2 = 104; + static constexpr s32 KEYCODE_BUTTON_R2 = 105; + static constexpr s32 KEYCODE_BUTTON_THUMBL = 106; + static constexpr s32 KEYCODE_BUTTON_THUMBR = 107; + static constexpr s32 KEYCODE_BUTTON_START = 108; + static constexpr s32 KEYCODE_BUTTON_SELECT = 109; + const std::vector<s32> keycode_ids{ + KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_RIGHT, + KEYCODE_BUTTON_A, KEYCODE_BUTTON_B, KEYCODE_BUTTON_X, KEYCODE_BUTTON_Y, + KEYCODE_BUTTON_L1, KEYCODE_BUTTON_R1, KEYCODE_BUTTON_L2, KEYCODE_BUTTON_R2, + KEYCODE_BUTTON_THUMBL, KEYCODE_BUTTON_THUMBR, KEYCODE_BUTTON_START, KEYCODE_BUTTON_SELECT, + }; + + const std::string sony_vid{"054c"}; + const std::string nintendo_vid{"057e"}; + const std::string razer_vid{"1532"}; + const std::string redmagic_vid{"3537"}; + const std::string backbone_labs_vid{"358a"}; + const std::string xbox_vid{"045e"}; + const std::vector<std::string> flipped_ab_vids{sony_vid, nintendo_vid, razer_vid, + redmagic_vid, backbone_labs_vid, xbox_vid}; + const std::vector<std::string> flipped_xy_vids{sony_vid, razer_vid, redmagic_vid, + backbone_labs_vid, xbox_vid}; }; } // namespace InputCommon diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index c9f903213..0dd1c958a 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -268,7 +268,9 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) { } Common::Input::DriverResult JoyconDriver::SetPollingMode() { - SCOPE_EXIT({ disable_input_thread = false; }); + SCOPE_EXIT { + disable_input_thread = false; + }; disable_input_thread = true; rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index f8749ebbf..62a7ae40f 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -4,7 +4,6 @@ #include <memory> #include "common/input.h" #include "common/param_package.h" -#include "input_common/drivers/android.h" #include "input_common/drivers/camera.h" #include "input_common/drivers/keyboard.h" #include "input_common/drivers/mouse.h" @@ -28,6 +27,10 @@ #include "input_common/drivers/sdl_driver.h" #endif +#ifdef ANDROID +#include "input_common/drivers/android.h" +#endif + namespace InputCommon { /// Dummy engine to get periodic updates @@ -79,7 +82,9 @@ struct InputSubsystem::Impl { RegisterEngine("cemuhookudp", udp_client); RegisterEngine("tas", tas_input); RegisterEngine("camera", camera); +#ifdef ANDROID RegisterEngine("android", android); +#endif RegisterEngine("virtual_amiibo", virtual_amiibo); RegisterEngine("virtual_gamepad", virtual_gamepad); #ifdef HAVE_SDL2 @@ -111,7 +116,9 @@ struct InputSubsystem::Impl { UnregisterEngine(udp_client); UnregisterEngine(tas_input); UnregisterEngine(camera); +#ifdef ANDROID UnregisterEngine(android); +#endif UnregisterEngine(virtual_amiibo); UnregisterEngine(virtual_gamepad); #ifdef HAVE_SDL2 @@ -128,12 +135,16 @@ struct InputSubsystem::Impl { Common::ParamPackage{{"display", "Any"}, {"engine", "any"}}, }; +#ifndef ANDROID auto keyboard_devices = keyboard->GetInputDevices(); devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end()); auto mouse_devices = mouse->GetInputDevices(); devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end()); +#endif +#ifdef ANDROID auto android_devices = android->GetInputDevices(); devices.insert(devices.end(), android_devices.begin(), android_devices.end()); +#endif #ifdef HAVE_LIBUSB auto gcadapter_devices = gcadapter->GetInputDevices(); devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end()); @@ -162,9 +173,11 @@ struct InputSubsystem::Impl { if (engine == mouse->GetEngineName()) { return mouse; } +#ifdef ANDROID if (engine == android->GetEngineName()) { return android; } +#endif #ifdef HAVE_LIBUSB if (engine == gcadapter->GetEngineName()) { return gcadapter; @@ -245,9 +258,11 @@ struct InputSubsystem::Impl { if (engine == mouse->GetEngineName()) { return true; } +#ifdef ANDROID if (engine == android->GetEngineName()) { return true; } +#endif #ifdef HAVE_LIBUSB if (engine == gcadapter->GetEngineName()) { return true; @@ -276,7 +291,9 @@ struct InputSubsystem::Impl { void BeginConfiguration() { keyboard->BeginConfiguration(); mouse->BeginConfiguration(); +#ifdef ANDROID android->BeginConfiguration(); +#endif #ifdef HAVE_LIBUSB gcadapter->BeginConfiguration(); #endif @@ -290,7 +307,9 @@ struct InputSubsystem::Impl { void EndConfiguration() { keyboard->EndConfiguration(); mouse->EndConfiguration(); +#ifdef ANDROID android->EndConfiguration(); +#endif #ifdef HAVE_LIBUSB gcadapter->EndConfiguration(); #endif @@ -321,7 +340,6 @@ struct InputSubsystem::Impl { std::shared_ptr<TasInput::Tas> tas_input; std::shared_ptr<CemuhookUDP::UDPClient> udp_client; std::shared_ptr<Camera> camera; - std::shared_ptr<Android> android; std::shared_ptr<VirtualAmiibo> virtual_amiibo; std::shared_ptr<VirtualGamepad> virtual_gamepad; @@ -333,6 +351,10 @@ struct InputSubsystem::Impl { std::shared_ptr<SDLDriver> sdl; std::shared_ptr<Joycons> joycon; #endif + +#ifdef ANDROID + std::shared_ptr<Android> android; +#endif }; InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} @@ -387,6 +409,7 @@ const Camera* InputSubsystem::GetCamera() const { return impl->camera.get(); } +#ifdef ANDROID Android* InputSubsystem::GetAndroid() { return impl->android.get(); } @@ -394,6 +417,7 @@ Android* InputSubsystem::GetAndroid() { const Android* InputSubsystem::GetAndroid() const { return impl->android.get(); } +#endif VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() { return impl->virtual_amiibo.get(); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 44281e407..945cdb42b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -60,11 +60,10 @@ public: Add(spv::ImageOperandsMask::ConstOffsets, offsets); } - explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, Id lod, Id ms) { + explicit ImageOperands(Id lod, Id ms) { if (Sirit::ValidId(lod)) { Add(spv::ImageOperandsMask::Lod, lod); } - AddOffset(ctx, offset, ImageFetchOffsetAllowed); if (Sirit::ValidId(ms)) { Add(spv::ImageOperandsMask::Sample, ms); } @@ -312,6 +311,43 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info, return coords; } } + +void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, Id& coords, + Id offset) { + if (!Sirit::ValidId(offset)) { + return; + } + + Id result_type{}; + switch (info.type) { + case TextureType::Buffer: + case TextureType::Color1D: { + result_type = ctx.U32[1]; + break; + } + case TextureType::ColorArray1D: + offset = ctx.OpCompositeConstruct(ctx.U32[2], offset, ctx.u32_zero_value); + [[fallthrough]]; + case TextureType::Color2D: + case TextureType::Color2DRect: { + result_type = ctx.U32[2]; + break; + } + case TextureType::ColorArray2D: + offset = ctx.OpCompositeConstruct(ctx.U32[3], ctx.OpCompositeExtract(ctx.U32[1], coords, 0), + ctx.OpCompositeExtract(ctx.U32[1], coords, 1), + ctx.u32_zero_value); + [[fallthrough]]; + case TextureType::Color3D: { + result_type = ctx.U32[3]; + break; + } + case TextureType::ColorCube: + case TextureType::ColorArrayCube: + return; + } + coords = ctx.OpIAdd(result_type, coords, offset); +} } // Anonymous namespace Id EmitBindlessImageSampleImplicitLod(EmitContext&) { @@ -494,9 +530,10 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, operands.Span()); } -Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, - const IR::Value& offset, Id lod, Id ms) { +Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, + Id lod, Id ms) { const auto info{inst->Flags<IR::TextureInstInfo>()}; + AddOffsetToCoordinates(ctx, info, coords, offset); if (info.type == TextureType::Buffer) { lod = Id{}; } @@ -504,7 +541,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c // This image is multisampled, lod must be implicit lod = Id{}; } - const ImageOperands operands(ctx, offset, lod, ms); + const ImageOperands operands(lod, ms); return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 08fcabd58..5c01b1012 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -537,8 +537,8 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id const IR::Value& offset, const IR::Value& offset2); Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, const IR::Value& offset, const IR::Value& offset2, Id dref); -Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, - const IR::Value& offset, Id lod, Id ms); +Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, + Id lod, Id ms); Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, const IR::Value& skip_mips); Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 55180f4b5..2de2beb6e 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -18,6 +18,7 @@ add_library(video_core STATIC buffer_cache/usage_tracker.h buffer_cache/word_manager.h cache_types.h + capture.h cdma_pusher.cpp cdma_pusher.h compatible_formats.cpp @@ -101,6 +102,7 @@ add_library(video_core STATIC memory_manager.cpp memory_manager.h precompiled_headers.h + present.h pte_kind.h query_cache/bank_base.h query_cache/query_base.h diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 3818e00f4..ed7a5b27e 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1130,7 +1130,7 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) { channel_state->vertex_buffers[index] = NULL_BINDING; return; } - if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { + if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) { size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); } const BufferId buffer_id = FindBuffer(*device_addr, size); diff --git a/src/video_core/capture.h b/src/video_core/capture.h new file mode 100644 index 000000000..8db14a8ec --- /dev/null +++ b/src/video_core/capture.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/alignment.h" +#include "common/bit_util.h" +#include "common/common_types.h" +#include "core/frontend/framebuffer_layout.h" +#include "video_core/surface.h" + +namespace VideoCore::Capture { + +constexpr u32 BlockHeight = 4; +constexpr u32 BlockDepth = 0; +constexpr u32 BppLog2 = 2; + +constexpr auto PixelFormat = Surface::PixelFormat::B8G8R8A8_UNORM; + +constexpr auto LinearWidth = Layout::ScreenUndocked::Width; +constexpr auto LinearHeight = Layout::ScreenUndocked::Height; +constexpr auto LinearDepth = 1U; +constexpr auto BytesPerPixel = 4U; + +constexpr auto TiledWidth = LinearWidth; +constexpr auto TiledHeight = Common::AlignUpLog2(LinearHeight, BlockHeight + BlockDepth + BppLog2); +constexpr auto TiledSize = TiledWidth * TiledHeight * (1 << BppLog2); + +constexpr Layout::FramebufferLayout Layout{ + .width = LinearWidth, + .height = LinearHeight, + .screen = {0, 0, LinearWidth, LinearHeight}, + .is_srgb = false, +}; + +} // namespace VideoCore::Capture diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index a94e1f043..0d47b032c 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -291,7 +291,9 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { } void Maxwell3D::ConsumeSinkImpl() { - SCOPE_EXIT({ method_sink.clear(); }); + SCOPE_EXIT { + method_sink.clear(); + }; const auto control = shadow_state.shadow_ram_control; if (control == Regs::ShadowRamControl::Track || control == Regs::ShadowRamControl::TrackWithFilter) { diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index c3eda6893..2135f1f2d 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -197,7 +197,9 @@ private: MicroProfileOnThreadCreate(name.c_str()); // Cleanup - SCOPE_EXIT({ MicroProfileOnThreadExit(); }); + SCOPE_EXIT { + MicroProfileOnThreadExit(); + }; Common::SetCurrentThreadName(name.c_str()); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h index 6a18b76fb..8b2a49de5 100644 --- a/src/video_core/framebuffer_config.h +++ b/src/video_core/framebuffer_config.h @@ -11,6 +11,12 @@ namespace Tegra { +enum class BlendMode { + Opaque, + Premultiplied, + Coverage, +}; + /** * Struct describing framebuffer configuration */ @@ -23,6 +29,7 @@ struct FramebufferConfig { Service::android::PixelFormat pixel_format{}; Service::android::BufferTransformFlags transform_flags{}; Common::Rectangle<int> crop_rect{}; + BlendMode blending{}; }; Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index f4a5d831c..8e663f2a8 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -347,6 +347,17 @@ struct GPU::Impl { WaitForSyncOperation(wait_fence); } + std::vector<u8> GetAppletCaptureBuffer() { + std::vector<u8> out; + + const auto wait_fence = + RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); }); + gpu_thread.TickGPU(); + WaitForSyncOperation(wait_fence); + + return out; + } + GPU& gpu; Core::System& system; Host1x::Host1x& host1x; @@ -505,6 +516,10 @@ void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, impl->RequestComposite(std::move(layers), std::move(fences)); } +std::vector<u8> GPU::GetAppletCaptureBuffer() { + return impl->GetAppletCaptureBuffer(); +} + u64 GPU::GetTicks() const { return impl->GetTicks(); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index c4602ca37..ad535512c 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -215,6 +215,8 @@ public: void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, std::vector<Service::Nvidia::NvFence>&& fences); + std::vector<u8> GetAppletCaptureBuffer(); + /// Performs any additional setup necessary in order to begin GPU emulation. /// This can be used to launch any necessary threads and register any necessary /// core timing events. diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 58d8110b8..477e11457 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -22,7 +22,9 @@ static void RunThread(std::stop_token stop_token, Core::System& system, Tegra::Control::Scheduler& scheduler, SynchState& state) { std::string name = "GPU"; MicroProfileOnThreadCreate(name.c_str()); - SCOPE_EXIT({ MicroProfileOnThreadExit(); }); + SCOPE_EXIT { + MicroProfileOnThreadExit(); + }; Common::SetCurrentThreadName(name.c_str()); Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp index 96686da59..1003cd38d 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp +++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp @@ -273,10 +273,10 @@ DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) { const AVFilter* buffer_sink = avfilter_get_by_name("buffersink"); AVFilterInOut* inputs = avfilter_inout_alloc(); AVFilterInOut* outputs = avfilter_inout_alloc(); - SCOPE_EXIT({ + SCOPE_EXIT { avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); - }); + }; // Don't know how to get the accurate time_base but it doesn't matter for yadif filter // so just use 1/1 to make buffer filter happy diff --git a/src/video_core/host_shaders/fidelityfx_fsr.frag b/src/video_core/host_shaders/fidelityfx_fsr.frag index a266e1c4e..54eedb450 100644 --- a/src/video_core/host_shaders/fidelityfx_fsr.frag +++ b/src/video_core/host_shaders/fidelityfx_fsr.frag @@ -37,6 +37,7 @@ layout(set=0,binding=0) uniform sampler2D InputTexture; #define A_GPU 1 #define A_GLSL 1 +#define FSR_RCAS_PASSTHROUGH_ALPHA 1 #ifndef YUZU_USE_FP16 #include "ffx_a.h" @@ -71,9 +72,7 @@ layout(set=0,binding=0) uniform sampler2D InputTexture; #include "ffx_fsr1.h" -#if USE_RCAS - layout(location = 0) in vec2 frag_texcoord; -#endif +layout (location = 0) in vec2 frag_texcoord; layout (location = 0) out vec4 frag_color; void CurrFilter(AU2 pos) { @@ -81,22 +80,22 @@ void CurrFilter(AU2 pos) { #ifndef YUZU_USE_FP16 AF3 c; FsrEasuF(c, pos, Const0, Const1, Const2, Const3); - frag_color = AF4(c, 1.0); + frag_color = AF4(c, texture(InputTexture, frag_texcoord).a); #else AH3 c; FsrEasuH(c, pos, Const0, Const1, Const2, Const3); - frag_color = AH4(c, 1.0); + frag_color = AH4(c, texture(InputTexture, frag_texcoord).a); #endif #endif #if USE_RCAS #ifndef YUZU_USE_FP16 - AF3 c; - FsrRcasF(c.r, c.g, c.b, pos, Const0); - frag_color = AF4(c, 1.0); + AF4 c; + FsrRcasF(c.r, c.g, c.b, c.a, pos, Const0); + frag_color = c; #else - AH3 c; - FsrRcasH(c.r, c.g, c.b, pos, Const0); - frag_color = AH4(c, 1.0); + AH4 c; + FsrRcasH(c.r, c.g, c.b, c.a, pos, Const0); + frag_color = c; #endif #endif } diff --git a/src/video_core/host_shaders/fxaa.frag b/src/video_core/host_shaders/fxaa.frag index 9bffc20d5..192a602c1 100644 --- a/src/video_core/host_shaders/fxaa.frag +++ b/src/video_core/host_shaders/fxaa.frag @@ -71,5 +71,5 @@ vec3 FxaaPixelShader(vec4 posPos, sampler2D tex) { } void main() { - frag_color = vec4(FxaaPixelShader(posPos, input_texture), 1.0); + frag_color = vec4(FxaaPixelShader(posPos, input_texture), texture(input_texture, posPos.xy).a); } diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag index 16d22f58e..fc47d3810 100644 --- a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag +++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag @@ -31,6 +31,7 @@ layout (location = 0) uniform uvec4 constants[4]; #define A_GPU 1 #define A_GLSL 1 +#define FSR_RCAS_PASSTHROUGH_ALPHA 1 #ifdef YUZU_USE_FP16 #define A_HALF @@ -67,9 +68,7 @@ layout (location = 0) uniform uvec4 constants[4]; #include "ffx_fsr1.h" -#if USE_RCAS - layout(location = 0) in vec2 frag_texcoord; -#endif +layout (location = 0) in vec2 frag_texcoord; layout (location = 0) out vec4 frag_color; void CurrFilter(AU2 pos) @@ -78,22 +77,22 @@ void CurrFilter(AU2 pos) #ifndef YUZU_USE_FP16 AF3 c; FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]); - frag_color = AF4(c, 1.0); + frag_color = AF4(c, texture(InputTexture, frag_texcoord).a); #else AH3 c; FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]); - frag_color = AH4(c, 1.0); + frag_color = AH4(c, texture(InputTexture, frag_texcoord).a); #endif #endif #if USE_RCAS #ifndef YUZU_USE_FP16 - AF3 c; - FsrRcasF(c.r, c.g, c.b, pos, constants[0]); - frag_color = AF4(c, 1.0); + AF4 c; + FsrRcasF(c.r, c.g, c.b, c.a, pos, constants[0]); + frag_color = c; #else AH3 c; - FsrRcasH(c.r, c.g, c.b, pos, constants[0]); - frag_color = AH4(c, 1.0); + FsrRcasH(c.r, c.g, c.b, c.a, pos, constants[0]); + frag_color = c; #endif #endif } diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag index 5fd7ad297..096b4e4db 100644 --- a/src/video_core/host_shaders/opengl_present.frag +++ b/src/video_core/host_shaders/opengl_present.frag @@ -9,5 +9,5 @@ layout (location = 0) out vec4 color; layout (binding = 0) uniform sampler2D color_texture; void main() { - color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f); + color = vec4(texture(color_texture, frag_tex_coord)); } diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag index c814629cf..a9d9d40a3 100644 --- a/src/video_core/host_shaders/present_bicubic.frag +++ b/src/video_core/host_shaders/present_bicubic.frag @@ -52,5 +52,5 @@ vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { } void main() { - color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f); + color = textureBicubic(color_texture, frag_tex_coord); } diff --git a/src/video_core/host_shaders/present_gaussian.frag b/src/video_core/host_shaders/present_gaussian.frag index ad9bb76a4..78edeb9b4 100644 --- a/src/video_core/host_shaders/present_gaussian.frag +++ b/src/video_core/host_shaders/present_gaussian.frag @@ -46,14 +46,14 @@ vec4 blurDiagonal(sampler2D textureSampler, vec2 coord, vec2 norm) { } void main() { - vec3 base = texture(color_texture, vec2(frag_tex_coord)).rgb * weight[0]; + vec4 base = texture(color_texture, vec2(frag_tex_coord)) * weight[0]; vec2 tex_offset = 1.0f / textureSize(color_texture, 0); // TODO(Blinkhawk): This code can be optimized through shader group instructions. - vec3 horizontal = blurHorizontal(color_texture, frag_tex_coord, tex_offset).rgb; - vec3 vertical = blurVertical(color_texture, frag_tex_coord, tex_offset).rgb; - vec3 diagonalA = blurDiagonal(color_texture, frag_tex_coord, tex_offset).rgb; - vec3 diagonalB = blurDiagonal(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0)).rgb; - vec3 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f); - color = vec4(combination + base, 1.0f); + vec4 horizontal = blurHorizontal(color_texture, frag_tex_coord, tex_offset); + vec4 vertical = blurVertical(color_texture, frag_tex_coord, tex_offset); + vec4 diagonalA = blurDiagonal(color_texture, frag_tex_coord, tex_offset); + vec4 diagonalB = blurDiagonal(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0)); + vec4 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f); + color = combination + base; } diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag index d369bef06..05d033310 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag @@ -6,5 +6,6 @@ #define YUZU_USE_FP16 #define USE_EASU 1 +#define VERSION 1 #include "fidelityfx_fsr.frag" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag index 6f25ef00f..7ae11dd66 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag @@ -5,5 +5,6 @@ #extension GL_GOOGLE_include_directive : enable #define USE_EASU 1 +#define VERSION 1 #include "fidelityfx_fsr.frag" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag index 0c953a900..c017214a5 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag @@ -6,5 +6,6 @@ #define YUZU_USE_FP16 #define USE_RCAS 1 +#define VERSION 1 #include "fidelityfx_fsr.frag" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag index 02e9a27c6..976825f4b 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag @@ -5,5 +5,6 @@ #extension GL_GOOGLE_include_directive : enable #define USE_RCAS 1 +#define VERSION 1 #include "fidelityfx_fsr.frag" diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag index 79ea817c2..cea5dac9d 100644 --- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag +++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag @@ -5,7 +5,7 @@ #extension GL_GOOGLE_include_directive : enable -#define VERSION 1 +#define VERSION 2 #define YUZU_USE_FP16 #include "opengl_present_scaleforce.frag" diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag index 9605bb58b..10ddf0401 100644 --- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag +++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag @@ -5,6 +5,6 @@ #extension GL_GOOGLE_include_directive : enable -#define VERSION 1 +#define VERSION 2 #include "opengl_present_scaleforce.frag" diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 46e853e04..fb529f88b 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp @@ -92,12 +92,12 @@ public: private: void Fallback(const std::vector<u32>& parameters) { - SCOPE_EXIT({ + SCOPE_EXIT { if (extended) { maxwell3d.engine_state = Maxwell3D::EngineHint::None; maxwell3d.replace_table.clear(); } - }); + }; maxwell3d.RefreshParameters(); const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); @@ -281,12 +281,12 @@ public: private: void Fallback(const std::vector<u32>& parameters) { - SCOPE_EXIT({ + SCOPE_EXIT { // Clean everything. maxwell3d.regs.vertex_id_base = 0x0; maxwell3d.engine_state = Maxwell3D::EngineHint::None; maxwell3d.replace_table.clear(); - }); + }; maxwell3d.RefreshParameters(); const u32 start_indirect = parameters[0]; const u32 end_indirect = parameters[1]; diff --git a/src/video_core/present.h b/src/video_core/present.h new file mode 100644 index 000000000..4fdfcca68 --- /dev/null +++ b/src/video_core/present.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/settings.h" + +static inline Settings::ScalingFilter GetScalingFilter() { + return Settings::values.scaling_filter.GetValue(); +} + +static inline Settings::AntiAliasing GetAntiAliasing() { + return Settings::values.anti_aliasing.GetValue(); +} + +static inline Settings::ScalingFilter GetScalingFilterForAppletCapture() { + return Settings::ScalingFilter::Bilinear; +} + +static inline Settings::AntiAliasing GetAntiAliasingForAppletCapture() { + return Settings::AntiAliasing::None; +} + +struct PresentFilters { + Settings::ScalingFilter (*get_scaling_filter)(); + Settings::AntiAliasing (*get_anti_aliasing)(); +}; + +constexpr PresentFilters PresentFiltersForDisplay{ + .get_scaling_filter = &GetScalingFilter, + .get_anti_aliasing = &GetAntiAliasing, +}; + +constexpr PresentFilters PresentFiltersForAppletCapture{ + .get_scaling_filter = &GetScalingFilterForAppletCapture, + .get_anti_aliasing = &GetAntiAliasingForAppletCapture, +}; diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 3ad180f67..67427f937 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -40,6 +40,9 @@ public: /// Finalize rendering the guest frame and draw into the presentation texture virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0; + /// Get the tiled applet layer capture buffer + virtual std::vector<u8> GetAppletCaptureBuffer() = 0; + [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; [[nodiscard]] virtual std::string GetDeviceVendor() const = 0; diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp index c89daff53..e6147d66c 100644 --- a/src/video_core/renderer_null/renderer_null.cpp +++ b/src/video_core/renderer_null/renderer_null.cpp @@ -3,6 +3,7 @@ #include "core/frontend/emu_window.h" #include "core/frontend/graphics_context.h" +#include "video_core/capture.h" #include "video_core/renderer_null/renderer_null.h" namespace Null { @@ -22,4 +23,8 @@ void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuff render_window.OnFrameDisplayed(); } +std::vector<u8> RendererNull::GetAppletCaptureBuffer() { + return std::vector<u8>(VideoCore::Capture::TiledSize); +} + } // namespace Null diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h index 063b476bb..34dbe1e4f 100644 --- a/src/video_core/renderer_null/renderer_null.h +++ b/src/video_core/renderer_null/renderer_null.h @@ -19,6 +19,8 @@ public: void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override; + std::vector<u8> GetAppletCaptureBuffer() override; + VideoCore::RasterizerInterface* ReadRasterizer() override { return &m_rasterizer; } diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp index 6ba8b214b..9260a4dc4 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.cpp +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/settings.h" +#include "video_core/present.h" #include "video_core/renderer_opengl/gl_blit_screen.h" #include "video_core/renderer_opengl/gl_state_tracker.h" #include "video_core/renderer_opengl/present/filters.h" @@ -13,14 +14,14 @@ namespace OpenGL { BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_, StateTracker& state_tracker_, ProgramManager& program_manager_, - Device& device_) + Device& device_, const PresentFilters& filters_) : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_), - program_manager(program_manager_), device(device_) {} + program_manager(program_manager_), device(device_), filters(filters_) {} BlitScreen::~BlitScreen() = default; void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, - const Layout::FramebufferLayout& layout) { + const Layout::FramebufferLayout& layout, bool invert_y) { // TODO: Signal state tracker about these changes state_tracker.NotifyScreenDrawVertexArray(); state_tracker.NotifyPolygonModes(); @@ -56,22 +57,22 @@ void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffe glDepthRangeIndexed(0, 0.0, 0.0); while (layers.size() < framebuffers.size()) { - layers.emplace_back(rasterizer, device_memory); + layers.emplace_back(rasterizer, device_memory, filters); } CreateWindowAdapt(); - window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout); + window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout, invert_y); // TODO // program_manager.RestoreGuestPipeline(); } void BlitScreen::CreateWindowAdapt() { - if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) { + if (window_adapt && filters.get_scaling_filter() == current_window_adapt) { return; } - current_window_adapt = Settings::values.scaling_filter.GetValue(); + current_window_adapt = filters.get_scaling_filter(); switch (current_window_adapt) { case Settings::ScalingFilter::NearestNeighbor: window_adapt = MakeNearestNeighbor(device); diff --git a/src/video_core/renderer_opengl/gl_blit_screen.h b/src/video_core/renderer_opengl/gl_blit_screen.h index 0c3d838f1..df2da9424 100644 --- a/src/video_core/renderer_opengl/gl_blit_screen.h +++ b/src/video_core/renderer_opengl/gl_blit_screen.h @@ -15,6 +15,8 @@ namespace Layout { struct FramebufferLayout; } +struct PresentFilters; + namespace Tegra { struct FramebufferConfig; } @@ -46,12 +48,12 @@ public: explicit BlitScreen(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory, StateTracker& state_tracker, ProgramManager& program_manager, - Device& device); + Device& device, const PresentFilters& filters); ~BlitScreen(); /// Draws the emulated screens to the emulator window. void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, - const Layout::FramebufferLayout& layout); + const Layout::FramebufferLayout& layout, bool invert_y); private: void CreateWindowAdapt(); @@ -61,6 +63,7 @@ private: StateTracker& state_tracker; ProgramManager& program_manager; Device& device; + const PresentFilters& filters; Settings::ScalingFilter current_window_adapt{}; std::unique_ptr<WindowAdaptPass> window_adapt; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index b42fb110c..16af8e6bd 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -230,7 +230,9 @@ template <typename Func> void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) { MICROPROFILE_SCOPE(OpenGL_Drawing); - SCOPE_EXIT({ gpu.TickWork(); }); + SCOPE_EXIT { + gpu.TickWork(); + }; gpu_memory->FlushCaching(); GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; @@ -355,7 +357,9 @@ void RasterizerOpenGL::DrawIndirect() { void RasterizerOpenGL::DrawTexture() { MICROPROFILE_SCOPE(OpenGL_Drawing); - SCOPE_EXIT({ gpu.TickWork(); }); + SCOPE_EXIT { + gpu.TickWork(); + }; texture_cache.SynchronizeGraphicsDescriptors(); texture_cache.UpdateRenderTargets(false); diff --git a/src/video_core/renderer_opengl/present/layer.cpp b/src/video_core/renderer_opengl/present/layer.cpp index 8643e07c6..6c7092d22 100644 --- a/src/video_core/renderer_opengl/present/layer.cpp +++ b/src/video_core/renderer_opengl/present/layer.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "video_core/framebuffer_config.h" +#include "video_core/present.h" #include "video_core/renderer_opengl/gl_blit_screen.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/present/fsr.h" @@ -14,8 +15,9 @@ namespace OpenGL { -Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_) - : rasterizer(rasterizer_), device_memory(device_memory_) { +Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_, + const PresentFilters& filters_) + : rasterizer(rasterizer_), device_memory(device_memory_), filters(filters_) { // Allocate textures for the screen framebuffer_texture.resource.Create(GL_TEXTURE_2D); @@ -34,12 +36,12 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, std::array<ScreenRectVertex, 4>& out_vertices, ProgramManager& program_manager, const Tegra::FramebufferConfig& framebuffer, - const Layout::FramebufferLayout& layout) { + const Layout::FramebufferLayout& layout, bool invert_y) { FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); GLuint texture = info.display_texture; - auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); + auto anti_aliasing = filters.get_anti_aliasing(); if (anti_aliasing != Settings::AntiAliasing::None) { glEnablei(GL_SCISSOR_TEST, 0); auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width); @@ -64,7 +66,7 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, glDisablei(GL_SCISSOR_TEST, 0); - if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { + if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) { if (!fsr || fsr->NeedsRecreation(layout.screen)) { fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight()); } @@ -83,10 +85,15 @@ GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, const auto w = screen.GetWidth(); const auto h = screen.GetHeight(); - out_vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); - out_vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); - out_vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); - out_vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); + const auto left = crop.left; + const auto right = crop.right; + const auto top = invert_y ? crop.bottom : crop.top; + const auto bottom = invert_y ? crop.top : crop.bottom; + + out_vertices[0] = ScreenRectVertex(x, y, left, top); + out_vertices[1] = ScreenRectVertex(x + w, y, right, top); + out_vertices[2] = ScreenRectVertex(x, y + h, left, bottom); + out_vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom); return texture; } @@ -131,10 +138,12 @@ FramebufferTextureInfo Layer::LoadFBToScreenInfo(const Tegra::FramebufferConfig& const u64 size_in_bytes{Tegra::Texture::CalculateSize( true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)}; - const std::span<const u8> input_data(host_ptr, size_in_bytes); - Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, - framebuffer.width, framebuffer.height, 1, block_height_log2, - 0); + if (host_ptr) { + const std::span<const u8> input_data(host_ptr, size_in_bytes); + Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, + framebuffer.width, framebuffer.height, 1, + block_height_log2, 0); + } glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); diff --git a/src/video_core/renderer_opengl/present/layer.h b/src/video_core/renderer_opengl/present/layer.h index ef1055abf..5b15b730f 100644 --- a/src/video_core/renderer_opengl/present/layer.h +++ b/src/video_core/renderer_opengl/present/layer.h @@ -13,6 +13,8 @@ namespace Layout { struct FramebufferLayout; } +struct PresentFilters; + namespace Service::android { enum class PixelFormat : u32; }; @@ -44,14 +46,15 @@ struct ScreenRectVertex; class Layer { public: - explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory); + explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory, + const PresentFilters& filters); ~Layer(); GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, std::array<ScreenRectVertex, 4>& out_vertices, ProgramManager& program_manager, const Tegra::FramebufferConfig& framebuffer, - const Layout::FramebufferLayout& layout); + const Layout::FramebufferLayout& layout, bool invert_y); private: /// Loads framebuffer from emulated memory into the active OpenGL texture. @@ -65,6 +68,7 @@ private: private: RasterizerOpenGL& rasterizer; Tegra::MaxwellDeviceMemoryManager& device_memory; + const PresentFilters& filters; /// OpenGL framebuffer data std::vector<u8> gl_framebuffer_data; diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp index 4d681606b..d8b6a11cb 100644 --- a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp @@ -37,7 +37,7 @@ WindowAdaptPass::~WindowAdaptPass() = default; void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, std::span<const Tegra::FramebufferConfig> framebuffers, - const Layout::FramebufferLayout& layout) { + const Layout::FramebufferLayout& layout, bool invert_y) { GLint old_read_fb; GLint old_draw_fb; glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); @@ -51,7 +51,7 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li auto layer_it = layers.begin(); for (size_t i = 0; i < layer_count; i++) { textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager, - framebuffers[i], layout); + framebuffers[i], layout, invert_y); layer_it++; } @@ -92,6 +92,21 @@ void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::li glClear(GL_COLOR_BUFFER_BIT); for (size_t i = 0; i < layer_count; i++) { + switch (framebuffers[i].blending) { + case Tegra::BlendMode::Opaque: + default: + glDisablei(GL_BLEND, 0); + break; + case Tegra::BlendMode::Premultiplied: + glEnablei(GL_BLEND, 0); + glBlendFuncSeparatei(0, GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + break; + case Tegra::BlendMode::Coverage: + glEnablei(GL_BLEND, 0); + glBlendFuncSeparatei(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + break; + } + glBindTextureUnit(0, textures[i]); glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE, matrices[i].data()); diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.h b/src/video_core/renderer_opengl/present/window_adapt_pass.h index 00975a9c6..0a8bcef2f 100644 --- a/src/video_core/renderer_opengl/present/window_adapt_pass.h +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.h @@ -31,7 +31,7 @@ public: void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, std::span<const Tegra::FramebufferConfig> framebuffers, - const Layout::FramebufferLayout& layout); + const Layout::FramebufferLayout& layout, bool invert_y); private: const Device& device; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index e33a32592..5fb54635d 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -16,6 +16,8 @@ #include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/telemetry_session.h" +#include "video_core/capture.h" +#include "video_core/present.h" #include "video_core/renderer_opengl/gl_blit_screen.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_manager.h" @@ -120,7 +122,15 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); } blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, - program_manager, device); + program_manager, device, PresentFiltersForDisplay); + blit_applet = + std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, program_manager, + device, PresentFiltersForAppletCapture); + capture_framebuffer.Create(); + capture_renderbuffer.Create(); + glBindRenderbuffer(GL_RENDERBUFFER, capture_renderbuffer.handle); + glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, VideoCore::Capture::LinearWidth, + VideoCore::Capture::LinearHeight); } RendererOpenGL::~RendererOpenGL() = default; @@ -130,10 +140,11 @@ void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebu return; } + RenderAppletCaptureLayer(framebuffers); RenderScreenshot(framebuffers); state_tracker.BindFramebuffer(0); - blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout()); + blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout(), false); ++m_current_frame; @@ -159,11 +170,8 @@ void RendererOpenGL::AddTelemetryFields() { telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); } -void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { - if (!renderer_settings.screenshot_requested) { - return; - } - +void RendererOpenGL::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout, void* dst) { GLint old_read_fb; GLint old_draw_fb; glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); @@ -173,29 +181,86 @@ void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> screenshot_framebuffer.Create(); glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle); - const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; - GLuint renderbuffer; glGenRenderbuffers(1, &renderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); - blit_screen->DrawScreen(framebuffers, layout); + blit_screen->DrawScreen(framebuffers, layout, false); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glPixelStorei(GL_PACK_ROW_LENGTH, 0); - glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, - renderer_settings.screenshot_bits); + glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst); screenshot_framebuffer.Release(); glDeleteRenderbuffers(1, &renderbuffer); glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); +} + +void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { + if (!renderer_settings.screenshot_requested) { + return; + } + + RenderToBuffer(framebuffers, renderer_settings.screenshot_framebuffer_layout, + renderer_settings.screenshot_bits); renderer_settings.screenshot_complete_callback(true); renderer_settings.screenshot_requested = false; } +void RendererOpenGL::RenderAppletCaptureLayer( + std::span<const Tegra::FramebufferConfig> framebuffers) { + GLint old_read_fb; + GLint old_draw_fb; + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); + + glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + capture_renderbuffer.handle); + + blit_applet->DrawScreen(framebuffers, VideoCore::Capture::Layout, true); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); +} + +std::vector<u8> RendererOpenGL::GetAppletCaptureBuffer() { + using namespace VideoCore::Capture; + + std::vector<u8> linear(TiledSize); + std::vector<u8> out(TiledSize); + + GLint old_read_fb; + GLint old_draw_fb; + GLint old_pixel_pack_buffer; + GLint old_pack_row_length; + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); + glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &old_pixel_pack_buffer); + glGetIntegerv(GL_PACK_ROW_LENGTH, &old_pack_row_length); + + glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + capture_renderbuffer.handle); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glReadPixels(0, 0, LinearWidth, LinearHeight, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, + linear.data()); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); + glBindBuffer(GL_PIXEL_PACK_BUFFER, old_pixel_pack_buffer); + glPixelStorei(GL_PACK_ROW_LENGTH, old_pack_row_length); + + Tegra::Texture::SwizzleTexture(out, linear, BytesPerPixel, LinearWidth, LinearHeight, + LinearDepth, BlockHeight, BlockDepth); + + return out; +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index c4625c96e..60d6a1477 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -42,6 +42,8 @@ public: void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; + std::vector<u8> GetAppletCaptureBuffer() override; + VideoCore::RasterizerInterface* ReadRasterizer() override { return &rasterizer; } @@ -52,7 +54,11 @@ public: private: void AddTelemetryFields(); + + void RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout, void* dst); void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); + void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers); Core::TelemetrySession& telemetry_session; Core::Frontend::EmuWindow& emu_window; @@ -64,8 +70,11 @@ private: ProgramManager program_manager; RasterizerOpenGL rasterizer; OGLFramebuffer screenshot_framebuffer; + OGLFramebuffer capture_framebuffer; + OGLRenderbuffer capture_renderbuffer; std::unique_ptr<BlitScreen> blit_screen; + std::unique_ptr<BlitScreen> blit_applet; }; } // namespace OpenGL diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp index cfc04be44..4e41afe5b 100644 --- a/src/video_core/renderer_vulkan/present/layer.cpp +++ b/src/video_core/renderer_vulkan/present/layer.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "video_core/present.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" #include "common/settings.h" @@ -48,12 +49,12 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, - VkExtent2D output_size, VkDescriptorSetLayout layout) + VkExtent2D output_size, VkDescriptorSetLayout layout, const PresentFilters& filters_) : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_), - device_memory(device_memory_), image_count(image_count_) { + device_memory(device_memory_), filters(filters_), image_count(image_count_) { CreateDescriptorPool(); CreateDescriptorSets(layout); - if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { + if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) { CreateFSR(output_size); } } @@ -81,7 +82,9 @@ void Layer::ConfigureDraw(PresentPushConstants* out_push_constants, // Finish any pending renderpass scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Wait(resource_ticks[image_index]); - SCOPE_EXIT({ resource_ticks[image_index] = scheduler.CurrentTick(); }); + SCOPE_EXIT { + resource_ticks[image_index] = scheduler.CurrentTick(); + }; if (!use_accelerated) { UpdateRawImage(framebuffer, image_index); @@ -171,11 +174,11 @@ void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { } void Layer::SetAntiAliasPass() { - if (anti_alias && anti_alias_setting == Settings::values.anti_aliasing.GetValue()) { + if (anti_alias && anti_alias_setting == filters.get_anti_aliasing()) { return; } - anti_alias_setting = Settings::values.anti_aliasing.GetValue(); + anti_alias_setting = filters.get_anti_aliasing(); const VkExtent2D render_area{ .width = Settings::values.resolution_info.ScaleUp(raw_width), @@ -270,9 +273,11 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i const u64 linear_size{GetSizeInBytes(framebuffer)}; const u64 tiled_size{Tegra::Texture::CalculateSize( true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; - Tegra::Texture::UnswizzleTexture( - mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), - bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); + if (host_ptr) { + Tegra::Texture::UnswizzleTexture( + mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), + bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); + } const VkBufferImageCopy copy{ .bufferOffset = image_offset, diff --git a/src/video_core/renderer_vulkan/present/layer.h b/src/video_core/renderer_vulkan/present/layer.h index 88d43fc5f..f5effdcd7 100644 --- a/src/video_core/renderer_vulkan/present/layer.h +++ b/src/video_core/renderer_vulkan/present/layer.h @@ -11,6 +11,8 @@ namespace Layout { struct FramebufferLayout; } +struct PresentFilters; + namespace Tegra { struct FramebufferConfig; } @@ -37,7 +39,8 @@ class Layer final { public: explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler, Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count, - VkExtent2D output_size, VkDescriptorSetLayout layout); + VkExtent2D output_size, VkDescriptorSetLayout layout, + const PresentFilters& filters); ~Layer(); void ConfigureDraw(PresentPushConstants* out_push_constants, @@ -71,6 +74,7 @@ private: MemoryAllocator& memory_allocator; Scheduler& scheduler; Tegra::MaxwellDeviceMemoryManager& device_memory; + const PresentFilters& filters; const size_t image_count{}; vk::DescriptorPool descriptor_pool{}; vk::DescriptorSets descriptor_sets{}; diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 6ee16595d..7f27c7c1b 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -362,10 +362,10 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, }); } -vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, - vk::PipelineLayout& layout, - std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, - bool enable_blending) { +static vk::Pipeline CreateWrappedPipelineImpl( + const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, + std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, + VkPipelineColorBlendAttachmentState blending) { const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, @@ -443,30 +443,6 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp .alphaToOneEnable = VK_FALSE, }; - constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{ - .blendEnable = VK_FALSE, - .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, - .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, - .colorBlendOp = VK_BLEND_OP_ADD, - .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, - .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, - .alphaBlendOp = VK_BLEND_OP_ADD, - .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, - }; - - constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_enabled{ - .blendEnable = VK_TRUE, - .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, - .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, - .colorBlendOp = VK_BLEND_OP_ADD, - .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, - .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, - .alphaBlendOp = VK_BLEND_OP_ADD, - .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, - }; - const VkPipelineColorBlendStateCreateInfo color_blend_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .pNext = nullptr, @@ -474,8 +450,7 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp .logicOpEnable = VK_FALSE, .logicOp = VK_LOGIC_OP_COPY, .attachmentCount = 1, - .pAttachments = - enable_blending ? &color_blend_attachment_enabled : &color_blend_attachment_disabled, + .pAttachments = &blending, .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, }; @@ -515,6 +490,63 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp }); } +vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, + vk::PipelineLayout& layout, + std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { + constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{ + .blendEnable = VK_FALSE, + .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, + .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + }; + + return CreateWrappedPipelineImpl(device, renderpass, layout, shaders, + color_blend_attachment_disabled); +} + +vk::Pipeline CreateWrappedPremultipliedBlendingPipeline( + const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, + std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { + constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_premultiplied{ + .blendEnable = VK_TRUE, + .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, + .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + }; + + return CreateWrappedPipelineImpl(device, renderpass, layout, shaders, + color_blend_attachment_premultiplied); +} + +vk::Pipeline CreateWrappedCoverageBlendingPipeline( + const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, + std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { + constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_coverage{ + .blendEnable = VK_TRUE, + .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, + .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + }; + + return CreateWrappedPipelineImpl(device, renderpass, layout, shaders, + color_blend_attachment_coverage); +} + VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, VkSampler sampler, VkImageView view, VkDescriptorSet set, u32 binding) { diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h index 1104aaa15..5b22f0fa8 100644 --- a/src/video_core/renderer_vulkan/present/util.h +++ b/src/video_core/renderer_vulkan/present/util.h @@ -42,8 +42,13 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, vk::DescriptorSetLayout& layout); vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, - std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, - bool enable_blending = false); + std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders); +vk::Pipeline CreateWrappedPremultipliedBlendingPipeline( + const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, + std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders); +vk::Pipeline CreateWrappedCoverageBlendingPipeline( + const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, + std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders); VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, VkSampler sampler, VkImageView view, VkDescriptorSet set, u32 binding); diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp index c5db0230d..22ffacf11 100644 --- a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp @@ -22,7 +22,7 @@ WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format, CreatePipelineLayout(); CreateVertexShader(); CreateRenderPass(frame_format); - CreatePipeline(); + CreatePipelines(); } WindowAdaptPass::~WindowAdaptPass() = default; @@ -34,7 +34,6 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s const VkFramebuffer host_framebuffer{*dst->framebuffer}; const VkRenderPass renderpass{*render_pass}; - const VkPipeline graphics_pipeline{*pipeline}; const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; const VkExtent2D render_area{ .width = dst->width, @@ -44,9 +43,23 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s const size_t layer_count = configs.size(); std::vector<PresentPushConstants> push_constants(layer_count); std::vector<VkDescriptorSet> descriptor_sets(layer_count); + std::vector<VkPipeline> graphics_pipelines(layer_count); auto layer_it = layers.begin(); for (size_t i = 0; i < layer_count; i++) { + switch (configs[i].blending) { + case Tegra::BlendMode::Opaque: + default: + graphics_pipelines[i] = *opaque_pipeline; + break; + case Tegra::BlendMode::Premultiplied: + graphics_pipelines[i] = *premultiplied_pipeline; + break; + case Tegra::BlendMode::Coverage: + graphics_pipelines[i] = *coverage_pipeline; + break; + } + layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler, image_index, configs[i], layout); layer_it++; @@ -77,8 +90,8 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area); cmdbuf.ClearAttachments({clear_attachment}, {clear_rect}); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); for (size_t i = 0; i < layer_count; i++) { + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipelines[i]); cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants[i]); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, @@ -129,9 +142,13 @@ void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) { render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); } -void WindowAdaptPass::CreatePipeline() { - pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, - std::tie(vertex_shader, fragment_shader), false); +void WindowAdaptPass::CreatePipelines() { + opaque_pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, + std::tie(vertex_shader, fragment_shader)); + premultiplied_pipeline = CreateWrappedPremultipliedBlendingPipeline( + device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader)); + coverage_pipeline = CreateWrappedCoverageBlendingPipeline( + device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader)); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.h b/src/video_core/renderer_vulkan/present/window_adapt_pass.h index 0e2edfc31..cf667a4fc 100644 --- a/src/video_core/renderer_vulkan/present/window_adapt_pass.h +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.h @@ -42,7 +42,7 @@ private: void CreatePipelineLayout(); void CreateVertexShader(); void CreateRenderPass(VkFormat frame_format); - void CreatePipeline(); + void CreatePipelines(); private: const Device& device; @@ -52,7 +52,9 @@ private: vk::ShaderModule vertex_shader; vk::ShaderModule fragment_shader; vk::RenderPass render_pass; - vk::Pipeline pipeline; + vk::Pipeline opaque_pipeline; + vk::Pipeline premultiplied_pipeline; + vk::Pipeline coverage_pipeline; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 48a105327..c553f5b3d 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -19,7 +19,9 @@ #include "core/core_timing.h" #include "core/frontend/graphics_context.h" #include "core/telemetry_session.h" +#include "video_core/capture.h" #include "video_core/gpu.h" +#include "video_core/present.h" #include "video_core/renderer_vulkan/present/util.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/vk_blit_screen.h" @@ -38,6 +40,20 @@ namespace Vulkan { namespace { + +constexpr VkExtent2D CaptureImageSize{ + .width = VideoCore::Capture::LinearWidth, + .height = VideoCore::Capture::LinearHeight, +}; + +constexpr VkExtent3D CaptureImageExtent{ + .width = VideoCore::Capture::LinearWidth, + .height = VideoCore::Capture::LinearHeight, + .depth = VideoCore::Capture::LinearDepth, +}; + +constexpr VkFormat CaptureFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32; + std::string GetReadableVersion(u32 version) { return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), VK_VERSION_PATCH(version)); @@ -99,10 +115,15 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, render_window.GetFramebufferLayout().height), present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, surface), - blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler), - blit_screenshot(device_memory, device, memory_allocator, present_manager, scheduler), + blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler, + PresentFiltersForDisplay), + blit_capture(device_memory, device, memory_allocator, present_manager, scheduler, + PresentFiltersForDisplay), + blit_applet(device_memory, device, memory_allocator, present_manager, scheduler, + PresentFiltersForAppletCapture), rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker, - scheduler) { + scheduler), + applet_frame() { if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { turbo_mode.emplace(instance, dld); scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); @@ -123,7 +144,11 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu return; } - SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); + SCOPE_EXIT { + render_window.OnFrameDisplayed(); + }; + + RenderAppletCaptureLayer(framebuffers); if (!render_window.IsShown()) { return; @@ -167,30 +192,20 @@ void RendererVulkan::Report() const { telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); } -void Vulkan::RendererVulkan::RenderScreenshot( - std::span<const Tegra::FramebufferConfig> framebuffers) { - if (!renderer_settings.screenshot_requested) { - return; - } - - constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM}; - const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; - +vk::Buffer RendererVulkan::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout, VkFormat format, + VkDeviceSize buffer_size) { auto frame = [&]() { Frame f{}; - f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, - ScreenshotFormat); - f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat); - f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat); + f.image = + CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, format); + f.image_view = CreateWrappedImageView(device, f.image, format); + f.framebuffer = blit_capture.CreateFramebuffer(layout, *f.image_view, format); return f; }(); - blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, - VK_FORMAT_B8G8R8A8_UNORM); - - const auto dst_buffer = CreateWrappedBuffer( - memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4), - MemoryUsage::Download); + auto dst_buffer = CreateWrappedBuffer(memory_allocator, buffer_size, MemoryUsage::Download); + blit_capture.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, format); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([&](vk::CommandBuffer cmdbuf) { @@ -198,15 +213,68 @@ void Vulkan::RendererVulkan::RenderScreenshot( VkExtent3D{layout.width, layout.height, 1}); }); - // Ensure the copy is fully completed before saving the screenshot + // Ensure the copy is fully completed before saving the capture scheduler.Finish(); - // Copy backing image data to the QImage screenshot buffer + // Copy backing image data to the capture buffer dst_buffer.Invalidate(); + return dst_buffer; +} + +void RendererVulkan::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { + if (!renderer_settings.screenshot_requested) { + return; + } + + const auto& layout{renderer_settings.screenshot_framebuffer_layout}; + const auto dst_buffer = RenderToBuffer(framebuffers, layout, VK_FORMAT_B8G8R8A8_UNORM, + layout.width * layout.height * 4); + std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(), dst_buffer.Mapped().size()); renderer_settings.screenshot_complete_callback(false); renderer_settings.screenshot_requested = false; } +std::vector<u8> RendererVulkan::GetAppletCaptureBuffer() { + using namespace VideoCore::Capture; + + std::vector<u8> out(VideoCore::Capture::TiledSize); + + if (!applet_frame.image) { + return out; + } + + const auto dst_buffer = + CreateWrappedBuffer(memory_allocator, VideoCore::Capture::TiledSize, MemoryUsage::Download); + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + DownloadColorImage(cmdbuf, *applet_frame.image, *dst_buffer, CaptureImageExtent); + }); + + // Ensure the copy is fully completed before writing the capture + scheduler.Finish(); + + // Swizzle image data to the capture buffer + dst_buffer.Invalidate(); + Tegra::Texture::SwizzleTexture(out, dst_buffer.Mapped(), BytesPerPixel, LinearWidth, + LinearHeight, LinearDepth, BlockHeight, BlockDepth); + + return out; +} + +void RendererVulkan::RenderAppletCaptureLayer( + std::span<const Tegra::FramebufferConfig> framebuffers) { + if (!applet_frame.image) { + applet_frame.image = CreateWrappedImage(memory_allocator, CaptureImageSize, CaptureFormat); + applet_frame.image_view = CreateWrappedImageView(device, applet_frame.image, CaptureFormat); + applet_frame.framebuffer = blit_applet.CreateFramebuffer( + VideoCore::Capture::Layout, *applet_frame.image_view, CaptureFormat); + } + + blit_applet.DrawToFrame(rasterizer, &applet_frame, framebuffers, VideoCore::Capture::Layout, 1, + CaptureFormat); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index c6d8a0f21..fb9d83412 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -48,6 +48,8 @@ public: void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; + std::vector<u8> GetAppletCaptureBuffer() override; + VideoCore::RasterizerInterface* ReadRasterizer() override { return &rasterizer; } @@ -59,7 +61,11 @@ public: private: void Report() const; + vk::Buffer RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout, VkFormat format, + VkDeviceSize buffer_size); void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); + void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers); Core::TelemetrySession& telemetry_session; Tegra::MaxwellDeviceMemoryManager& device_memory; @@ -79,9 +85,12 @@ private: Swapchain swapchain; PresentManager present_manager; BlitScreen blit_swapchain; - BlitScreen blit_screenshot; + BlitScreen blit_capture; + BlitScreen blit_applet; RasterizerVulkan rasterizer; std::optional<TurboMode> turbo_mode; + + Frame applet_frame; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 2275fcc46..b7797f833 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "video_core/framebuffer_config.h" +#include "video_core/present.h" #include "video_core/renderer_vulkan/present/filters.h" #include "video_core/renderer_vulkan/present/layer.h" #include "video_core/renderer_vulkan/vk_blit_screen.h" @@ -12,9 +13,9 @@ namespace Vulkan { BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, MemoryAllocator& memory_allocator_, PresentManager& present_manager_, - Scheduler& scheduler_) + Scheduler& scheduler_, const PresentFilters& filters_) : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, - present_manager{present_manager_}, scheduler{scheduler_}, image_count{1}, + present_manager{present_manager_}, scheduler{scheduler_}, filters{filters_}, image_count{1}, swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {} BlitScreen::~BlitScreen() = default; @@ -27,7 +28,7 @@ void BlitScreen::WaitIdle() { void BlitScreen::SetWindowAdaptPass() { layers.clear(); - scaling_filter = Settings::values.scaling_filter.GetValue(); + scaling_filter = filters.get_scaling_filter(); switch (scaling_filter) { case Settings::ScalingFilter::NearestNeighbor: @@ -59,7 +60,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, bool presentation_recreate_required = false; // Recreate dynamic resources if the adapting filter changed - if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue()) { + if (!window_adapt || scaling_filter != filters.get_scaling_filter()) { resource_update_required = true; } @@ -102,7 +103,7 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, while (layers.size() < framebuffers.size()) { layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count, - window_size, window_adapt->GetDescriptorSetLayout()); + window_size, window_adapt->GetDescriptorSetLayout(), filters); } // Perform the draw @@ -119,8 +120,7 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& l VkFormat current_view_format) { const bool format_updated = std::exchange(swapchain_view_format, current_view_format) != current_view_format; - if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() || - format_updated) { + if (!window_adapt || scaling_filter != filters.get_scaling_filter() || format_updated) { WaitIdle(); SetWindowAdaptPass(); } diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index cbdf2d5d0..531c57fc5 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -16,6 +16,8 @@ namespace Core { class System; } +struct PresentFilters; + namespace Tegra { struct FramebufferConfig; } @@ -47,7 +49,7 @@ class BlitScreen { public: explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device, MemoryAllocator& memory_allocator, PresentManager& present_manager, - Scheduler& scheduler); + Scheduler& scheduler, const PresentFilters& filters); ~BlitScreen(); void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, @@ -70,6 +72,7 @@ private: MemoryAllocator& memory_allocator; PresentManager& present_manager; Scheduler& scheduler; + const PresentFilters& filters; std::size_t image_count{}; std::size_t image_index{}; VkFormat swapchain_view_format{}; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index aa0a027bb..74f9f099e 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -196,7 +196,9 @@ template <typename Func> void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { MICROPROFILE_SCOPE(Vulkan_Drawing); - SCOPE_EXIT({ gpu.TickWork(); }); + SCOPE_EXIT { + gpu.TickWork(); + }; FlushWork(); gpu_memory->FlushCaching(); @@ -288,7 +290,9 @@ void RasterizerVulkan::DrawIndirect() { void RasterizerVulkan::DrawTexture() { MICROPROFILE_SCOPE(Vulkan_Drawing); - SCOPE_EXIT({ gpu.TickWork(); }); + SCOPE_EXIT { + gpu.TickWork(); + }; FlushWork(); query_cache.NotifySegment(true); 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 ca0794214..53b4876f2 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); - } } } @@ -746,7 +746,13 @@ std::pair<typename P::ImageView*, bool> TextureCache<P>::TryFindFramebufferImage }(); const auto GetImageViewForFramebuffer = [&](ImageId image_id) { - const ImageViewInfo info{ImageViewType::e2D, view_format}; + ImageViewInfo info{ImageViewType::e2D, view_format}; + if (config.blending == Tegra::BlendMode::Opaque) { + info.x_source = static_cast<u8>(SwizzleSource::R); + info.y_source = static_cast<u8>(SwizzleSource::G); + info.z_source = static_cast<u8>(SwizzleSource::B); + info.w_source = static_cast<u8>(SwizzleSource::OneFloat); + } return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)], slot_images[image_id].IsRescaled()); }; @@ -1463,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/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp index 5fa0d9620..f41c3e506 100644 --- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp +++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp @@ -116,7 +116,9 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump, LOG_ERROR(Render_Vulkan, "Failed to create decoder"); return; } - SCOPE_EXIT({ GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder); }); + SCOPE_EXIT { + GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder); + }; u32 json_size = 0; if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON( diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 76f06da12..0259a8c29 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -41,6 +41,9 @@ add_executable(yuzu configuration/configuration_shared.cpp configuration/configuration_shared.h configuration/configure.ui + configuration/configure_applets.cpp + configuration/configure_applets.h + configuration/configure_applets.ui configuration/configure_audio.cpp configuration/configure_audio.h configuration/configure_audio.ui diff --git a/src/yuzu/configuration/configure_applets.cpp b/src/yuzu/configuration/configure_applets.cpp new file mode 100644 index 000000000..513ecb548 --- /dev/null +++ b/src/yuzu/configuration/configure_applets.cpp @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "core/core.h" +#include "ui_configure_applets.h" +#include "yuzu/configuration/configuration_shared.h" +#include "yuzu/configuration/configure_applets.h" +#include "yuzu/configuration/shared_widget.h" + +ConfigureApplets::ConfigureApplets(Core::System& system_, + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_, + const ConfigurationShared::Builder& builder, QWidget* parent) + : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureApplets>()}, system{system_} { + ui->setupUi(this); + + Setup(builder); + + SetConfiguration(); +} + +ConfigureApplets::~ConfigureApplets() = default; + +void ConfigureApplets::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QWidget::changeEvent(event); +} + +void ConfigureApplets::RetranslateUI() { + ui->retranslateUi(this); +} + +void ConfigureApplets::Setup(const ConfigurationShared::Builder& builder) { + auto& library_applets_layout = *ui->group_library_applet_modes->layout(); + std::map<u32, QWidget*> applets_hold{}; + + std::vector<Settings::BasicSetting*> settings; + auto push = [&settings](auto& list) { + for (auto setting : list) { + settings.push_back(setting); + } + }; + + push(Settings::values.linkage.by_category[Settings::Category::LibraryApplet]); + + for (auto setting : settings) { + ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs); + + if (widget == nullptr) { + continue; + } + if (!widget->Valid()) { + widget->deleteLater(); + continue; + } + + // Untested applets + if (setting->Id() == Settings::values.data_erase_applet_mode.Id() || + setting->Id() == Settings::values.error_applet_mode.Id() || + setting->Id() == Settings::values.net_connect_applet_mode.Id() || + setting->Id() == Settings::values.web_applet_mode.Id() || + setting->Id() == Settings::values.shop_applet_mode.Id() || + setting->Id() == Settings::values.login_share_applet_mode.Id() || + setting->Id() == Settings::values.wifi_web_auth_applet_mode.Id() || + setting->Id() == Settings::values.my_page_applet_mode.Id()) { + widget->setHidden(true); + } + + applets_hold.emplace(setting->Id(), widget); + } + for (const auto& [label, widget] : applets_hold) { + library_applets_layout.addWidget(widget); + } +} + +void ConfigureApplets::SetConfiguration() {} + +void ConfigureApplets::ApplyConfiguration() { + const bool powered_on = system.IsPoweredOn(); + for (const auto& func : apply_funcs) { + func(powered_on); + } +} diff --git a/src/yuzu/configuration/configure_applets.h b/src/yuzu/configuration/configure_applets.h new file mode 100644 index 000000000..54f494d2f --- /dev/null +++ b/src/yuzu/configuration/configure_applets.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <QWidget> +#include "yuzu/configuration/configuration_shared.h" + +class QCheckBox; +class QLineEdit; +class QComboBox; +class QDateTimeEdit; +namespace Core { +class System; +} + +namespace Ui { +class ConfigureApplets; +} + +namespace ConfigurationShared { +class Builder; +} + +class ConfigureApplets : public ConfigurationShared::Tab { +public: + explicit ConfigureApplets(Core::System& system_, + std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group, + const ConfigurationShared::Builder& builder, + QWidget* parent = nullptr); + ~ConfigureApplets() override; + + void ApplyConfiguration() override; + void SetConfiguration() override; + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + void Setup(const ConfigurationShared::Builder& builder); + + std::vector<std::function<void(bool)>> apply_funcs{}; + + std::unique_ptr<Ui::ConfigureApplets> ui; + bool enabled = false; + + Core::System& system; +}; diff --git a/src/yuzu/configuration/configure_applets.ui b/src/yuzu/configuration/configure_applets.ui new file mode 100644 index 000000000..6f2ca66bd --- /dev/null +++ b/src/yuzu/configuration/configure_applets.ui @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureApplets</class> + <widget class="QWidget" name="ConfigureApplets"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>605</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <property name="accessibleName"> + <string>Applets</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_1"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="group_library_applet_modes"> + <property name="title"> + <string>Applet mode preference</string> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="QWidget" name="applets_widget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index aab54a1cc..37f23388e 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -8,6 +8,7 @@ #include "core/core.h" #include "ui_configure.h" #include "vk_device_info.h" +#include "yuzu/configuration/configure_applets.h" #include "yuzu/configuration/configure_audio.h" #include "yuzu/configuration/configure_cpu.h" #include "yuzu/configuration/configure_debug_tab.h" @@ -34,6 +35,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry(registry_), system{system_}, builder{std::make_unique<ConfigurationShared::Builder>( this, !system_.IsPoweredOn())}, + applets_tab{std::make_unique<ConfigureApplets>(system_, nullptr, *builder, this)}, audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *builder, this)}, cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *builder, this)}, debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)}, @@ -58,6 +60,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, ui->setupUi(this); + ui->tabWidget->addTab(applets_tab.get(), tr("Applets")); ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); ui->tabWidget->addTab(cpu_tab.get(), tr("CPU")); ui->tabWidget->addTab(debug_tab_tab.get(), tr("Debug")); @@ -124,6 +127,7 @@ void ConfigureDialog::ApplyConfiguration() { debug_tab_tab->ApplyConfiguration(); web_tab->ApplyConfiguration(); network_tab->ApplyConfiguration(); + applets_tab->ApplyConfiguration(); system.ApplySettings(); Settings::LogSettings(); } @@ -161,7 +165,8 @@ void ConfigureDialog::PopulateSelectionList() { {{tr("General"), {general_tab.get(), hotkeys_tab.get(), ui_tab.get(), web_tab.get(), debug_tab_tab.get()}}, {tr("System"), - {system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get()}}, + {system_tab.get(), profile_tab.get(), network_tab.get(), filesystem_tab.get(), + applets_tab.get()}}, {tr("CPU"), {cpu_tab.get()}}, {tr("Graphics"), {graphics_tab.get(), graphics_advanced_tab.get()}}, {tr("Audio"), {audio_tab.get()}}, diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index b28ce288c..d0a24a07b 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -15,6 +15,7 @@ namespace Core { class System; } +class ConfigureApplets; class ConfigureAudio; class ConfigureCpu; class ConfigureDebugTab; @@ -75,6 +76,7 @@ private: std::unique_ptr<ConfigurationShared::Builder> builder; std::vector<ConfigurationShared::Tab*> tab_group; + std::unique_ptr<ConfigureApplets> applets_tab; std::unique_ptr<ConfigureAudio> audio_tab; std::unique_ptr<ConfigureCpu> cpu_tab; std::unique_ptr<ConfigureDebugTab> debug_tab_tab; diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index e28df10bd..28c3baf08 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -8,10 +8,7 @@ #include "common/settings_enums.h" #include "core/core.h" #include "core/hle/service/am/am.h" -#include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_manager.h" -#include "core/hle/service/am/applet_message_queue.h" -#include "core/hle/service/am/applet_oe.h" #include "core/hle/service/sm/sm.h" #include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp index 1051031f2..37951b9c8 100644 --- a/src/yuzu/configuration/qt_config.cpp +++ b/src/yuzu/configuration/qt_config.cpp @@ -90,6 +90,7 @@ void QtConfig::ReadQtPlayerValues(const std::size_t player_index) { if (profile_name.empty()) { // Use the global input config player = Settings::values.players.GetValue(true)[player_index]; + player.profile_name = ""; return; } } diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index ed9c7d859..d138b53c8 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -26,6 +26,23 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { // A setting can be ignored by giving it a blank name + // Applets + INSERT(Settings, cabinet_applet_mode, tr("Amiibo editor"), QStringLiteral()); + INSERT(Settings, controller_applet_mode, tr("Controller configuration"), QStringLiteral()); + INSERT(Settings, data_erase_applet_mode, tr("Data erase"), QStringLiteral()); + INSERT(Settings, error_applet_mode, tr("Error"), QStringLiteral()); + INSERT(Settings, net_connect_applet_mode, tr("Net connect"), QStringLiteral()); + INSERT(Settings, player_select_applet_mode, tr("Player select"), QStringLiteral()); + INSERT(Settings, swkbd_applet_mode, tr("Software keyboard"), QStringLiteral()); + INSERT(Settings, mii_edit_applet_mode, tr("Mii Edit"), QStringLiteral()); + INSERT(Settings, web_applet_mode, tr("Online web"), QStringLiteral()); + INSERT(Settings, shop_applet_mode, tr("Shop"), QStringLiteral()); + INSERT(Settings, photo_viewer_applet_mode, tr("Photo viewer"), QStringLiteral()); + INSERT(Settings, offline_web_applet_mode, tr("Offline web"), QStringLiteral()); + INSERT(Settings, login_share_applet_mode, tr("Login share"), QStringLiteral()); + INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QStringLiteral()); + INSERT(Settings, my_page_applet_mode, tr("My page"), QStringLiteral()); + // Audio INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral()); INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral()); @@ -37,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 @@ -63,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 " @@ -104,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( @@ -140,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 @@ -170,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()); @@ -203,6 +296,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) { #define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)} // Intentionally skipping VSyncMode to let the UI fill that one out + translations->insert({Settings::EnumMetadata<Settings::AppletMode>::Index(), + { + PAIR(AppletMode, HLE, tr("Custom frontend")), + PAIR(AppletMode, LLE, tr("Real applet")), + }}); translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), { 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; } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 13381fea8..b2ae3db52 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -44,9 +44,6 @@ #include "core/frontend/applets/mii_edit.h" #include "core/frontend/applets/software_keyboard.h" #include "core/hle/service/acc/profile_manager.h" -#include "core/hle/service/am/applet_ae.h" -#include "core/hle/service/am/applet_message_queue.h" -#include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/set/system_settings_server.h" #include "frontend_common/content_manager.h" @@ -649,10 +646,10 @@ void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParamete std::shared_ptr<Service::NFC::NfcDevice> nfp_device) { cabinet_applet = new QtAmiiboSettingsDialog(this, parameters, input_subsystem.get(), nfp_device); - SCOPE_EXIT({ + SCOPE_EXIT { cabinet_applet->deleteLater(); cabinet_applet = nullptr; - }); + }; cabinet_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); @@ -676,10 +673,10 @@ void GMainWindow::ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters) { controller_applet = new QtControllerSelectorDialog(this, parameters, input_subsystem.get(), *system); - SCOPE_EXIT({ + SCOPE_EXIT { controller_applet->deleteLater(); controller_applet = nullptr; - }); + }; controller_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowTitleHint | @@ -706,10 +703,10 @@ void GMainWindow::ControllerSelectorRequestExit() { void GMainWindow::ProfileSelectorSelectProfile( const Core::Frontend::ProfileSelectParameters& parameters) { profile_select_applet = new QtProfileSelectionDialog(*system, this, parameters); - SCOPE_EXIT({ + SCOPE_EXIT { profile_select_applet->deleteLater(); profile_select_applet = nullptr; - }); + }; profile_select_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowTitleHint | @@ -1606,6 +1603,8 @@ void GMainWindow::ConnectMenuEvents() { // Help connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder); connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents); + connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware); + connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys); connect_menu(ui->action_About, &GMainWindow::OnAbout); } @@ -1634,6 +1633,9 @@ void GMainWindow::UpdateMenuState() { action->setEnabled(emulation_running); } + ui->action_Install_Firmware->setEnabled(!emulation_running); + ui->action_Install_Keys->setEnabled(!emulation_running); + for (QAction* action : applet_actions) { action->setEnabled(is_firmware_available && !emulation_running); } @@ -2885,17 +2887,19 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, LOG_ERROR(Frontend, "CoInitialize failed"); return false; } - SCOPE_EXIT({ CoUninitialize(); }); + SCOPE_EXIT { + CoUninitialize(); + }; IShellLinkW* ps1 = nullptr; IPersistFile* persist_file = nullptr; - SCOPE_EXIT({ + SCOPE_EXIT { if (persist_file != nullptr) { persist_file->Release(); } if (ps1 != nullptr) { ps1->Release(); } - }); + }; HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW, reinterpret_cast<void**>(&ps1)); if (FAILED(hres)) { @@ -3520,10 +3524,10 @@ void GMainWindow::OnSaveConfig() { void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { error_applet = new OverlayDialog(render_window, *system, error_code, error_text, QString{}, tr("OK"), Qt::AlignLeft | Qt::AlignVCenter); - SCOPE_EXIT({ + SCOPE_EXIT { error_applet->deleteLater(); error_applet = nullptr; - }); + }; error_applet->exec(); emit ErrorDisplayFinished(); @@ -4153,6 +4157,221 @@ void GMainWindow::OnVerifyInstalledContents() { } } +void GMainWindow::OnInstallFirmware() { + // Don't do this while emulation is running, that'd probably be a bad idea. + if (emu_thread != nullptr && emu_thread->IsRunning()) { + return; + } + + // Check for installed keys, error out, suggest restart? + if (!ContentManager::AreKeysPresent()) { + QMessageBox::information( + this, tr("Keys not installed"), + tr("Install decryption keys and restart yuzu before attempting to install firmware.")); + return; + } + + const QString firmware_source_location = QFileDialog::getExistingDirectory( + this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); + if (firmware_source_location.isEmpty()) { + return; + } + + QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this); + progress.setWindowModality(Qt::WindowModal); + progress.setMinimumDuration(100); + progress.setAutoClose(false); + progress.setAutoReset(false); + progress.show(); + + // Declare progress callback. + auto QtProgressCallback = [&](size_t total_size, size_t processed_size) { + progress.setValue(static_cast<int>((processed_size * 100) / total_size)); + return progress.wasCanceled(); + }; + + LOG_INFO(Frontend, "Installing firmware from {}", firmware_source_location.toStdString()); + + // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in + // there.) + std::filesystem::path firmware_source_path = firmware_source_location.toStdString(); + if (!Common::FS::IsDir(firmware_source_path)) { + progress.close(); + return; + } + + std::vector<std::filesystem::path> out; + const Common::FS::DirEntryCallable callback = + [&out](const std::filesystem::directory_entry& entry) { + if (entry.path().has_extension() && entry.path().extension() == ".nca") { + out.emplace_back(entry.path()); + } + + return true; + }; + + QtProgressCallback(100, 10); + + Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + if (out.size() <= 0) { + progress.close(); + QMessageBox::warning(this, tr("Firmware install failed"), + tr("Unable to locate potential firmware NCA files")); + return; + } + + // Locate and erase the content of nand/system/Content/registered/*.nca, if any. + auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory(); + if (!sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) { + progress.close(); + QMessageBox::critical(this, tr("Firmware install failed"), + tr("Failed to delete one or more firmware file.")); + return; + } + + LOG_INFO(Frontend, + "Cleaned nand/system/Content/registered folder in preparation for new firmware."); + + QtProgressCallback(100, 20); + + auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered"); + + bool success = true; + int i = 0; + for (const auto& firmware_src_path : out) { + i++; + auto firmware_src_vfile = + vfs->OpenFile(firmware_src_path.generic_string(), FileSys::OpenMode::Read); + auto firmware_dst_vfile = + firmware_vdir->CreateFileRelative(firmware_src_path.filename().string()); + + if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) { + LOG_ERROR(Frontend, "Failed to copy firmware file {} to {} in registered folder!", + firmware_src_path.generic_string(), firmware_src_path.filename().string()); + success = false; + } + + if (QtProgressCallback( + 100, 20 + static_cast<int>(((i) / static_cast<float>(out.size())) * 70.0))) { + progress.close(); + QMessageBox::warning( + this, tr("Firmware install failed"), + tr("Firmware installation cancelled, firmware may be in bad state, " + "restart yuzu or re-install firmware.")); + return; + } + } + + if (!success) { + progress.close(); + QMessageBox::critical(this, tr("Firmware install failed"), + tr("One or more firmware files failed to copy into NAND.")); + return; + } + + // Re-scan VFS for the newly placed firmware files. + system->GetFileSystemController().CreateFactories(*vfs); + + auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) { + progress.setValue(90 + static_cast<int>((processed_size * 10) / total_size)); + return progress.wasCanceled(); + }; + + auto result = + ContentManager::VerifyInstalledContents(*system, *provider, VerifyFirmwareCallback, true); + + if (result.size() > 0) { + const auto failed_names = + QString::fromStdString(fmt::format("{}", fmt::join(result, "\n"))); + progress.close(); + QMessageBox::critical( + this, tr("Firmware integrity verification failed!"), + tr("Verification failed for the following files:\n\n%1").arg(failed_names)); + return; + } + + progress.close(); + OnCheckFirmwareDecryption(); +} + +void GMainWindow::OnInstallDecryptionKeys() { + // Don't do this while emulation is running. + if (emu_thread != nullptr && emu_thread->IsRunning()) { + return; + } + + const QString key_source_location = QFileDialog::getOpenFileName( + this, tr("Select Dumped Keys Location"), {}, QStringLiteral("prod.keys (prod.keys)"), {}, + QFileDialog::ReadOnly); + if (key_source_location.isEmpty()) { + return; + } + + // Verify that it contains prod.keys, title.keys and optionally, key_retail.bin + LOG_INFO(Frontend, "Installing key files from {}", key_source_location.toStdString()); + + const std::filesystem::path prod_key_path = key_source_location.toStdString(); + const std::filesystem::path key_source_path = prod_key_path.parent_path(); + if (!Common::FS::IsDir(key_source_path)) { + return; + } + + bool prod_keys_found = false; + std::vector<std::filesystem::path> source_key_files; + + if (Common::FS::Exists(prod_key_path)) { + prod_keys_found = true; + source_key_files.emplace_back(prod_key_path); + } + + if (Common::FS::Exists(key_source_path / "title.keys")) { + source_key_files.emplace_back(key_source_path / "title.keys"); + } + + if (Common::FS::Exists(key_source_path / "key_retail.bin")) { + source_key_files.emplace_back(key_source_path / "key_retail.bin"); + } + + // There should be at least prod.keys. + if (source_key_files.empty() || !prod_keys_found) { + QMessageBox::warning(this, tr("Decryption Keys install failed"), + tr("prod.keys is a required decryption key file.")); + return; + } + + const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); + for (auto key_file : source_key_files) { + std::filesystem::path destination_key_file = yuzu_keys_dir / key_file.filename(); + if (!std::filesystem::copy_file(key_file, destination_key_file, + std::filesystem::copy_options::overwrite_existing)) { + LOG_ERROR(Frontend, "Failed to copy file {} to {}", key_file.string(), + destination_key_file.string()); + QMessageBox::critical(this, tr("Decryption Keys install failed"), + tr("One or more keys failed to copy.")); + return; + } + } + + // Reinitialize the key manager, re-read the vfs (for update/dlc files), + // and re-populate the game list in the UI if the user has already added + // game folders. + Core::Crypto::KeyManager::Instance().ReloadKeys(); + system->GetFileSystemController().CreateFactories(*vfs); + game_list->PopulateAsync(UISettings::values.game_dirs); + + if (ContentManager::AreKeysPresent()) { + QMessageBox::information(this, tr("Decryption Keys install succeeded"), + tr("Decryption Keys were successfully installed")); + } else { + QMessageBox::critical( + this, tr("Decryption Keys install failed"), + tr("Decryption Keys failed to initialize. Check that your dumping tools are " + "up to date and re-dump keys.")); + } + + OnCheckFirmwareDecryption(); +} + void GMainWindow::OnAbout() { AboutDialog aboutDialog(this); aboutDialog.exec(); @@ -5052,7 +5271,9 @@ int main(int argc, char* argv[]) { Common::DetachedTasks detached_tasks; MicroProfileOnThreadCreate("Frontend"); - SCOPE_EXIT({ MicroProfileShutdown(); }); + SCOPE_EXIT { + MicroProfileShutdown(); + }; Common::ConfigureNvidiaEnvironmentFlags(); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index aba61e388..fce643f3f 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -380,6 +380,8 @@ private slots: void OnLoadAmiibo(); void OnOpenYuzuFolder(); void OnVerifyInstalledContents(); + void OnInstallFirmware(); + void OnInstallDecryptionKeys(); void OnAbout(); void OnToggleFilterBar(); void OnToggleStatusBar(); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 6a6b0821f..85dc1f2f6 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -25,7 +25,16 @@ </property> <widget class="QWidget" name="centralwidget"> <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="margin" stdset="0"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> </layout> @@ -156,7 +165,8 @@ <addaction name="separator"/> <addaction name="action_Configure_Tas"/> </widget> - <addaction name="action_Rederive"/> + <addaction name="action_Install_Keys"/> + <addaction name="action_Install_Firmware"/> <addaction name="action_Verify_installed_contents"/> <addaction name="separator"/> <addaction name="menu_cabinet_applet"/> @@ -455,6 +465,16 @@ <string>Open &Controller Menu</string> </property> </action> + <action name="action_Install_Firmware"> + <property name="text"> + <string>Install Firmware</string> + </property> + </action> + <action name="action_Install_Keys"> + <property name="text"> + <string>Install Decryption Keys</string> + </property> + </action> </widget> <resources> <include location="yuzu.qrc"/> diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp index 995114510..6e0f254b6 100644 --- a/src/yuzu_cmd/sdl_config.cpp +++ b/src/yuzu_cmd/sdl_config.cpp @@ -103,6 +103,7 @@ void SdlConfig::ReadSdlPlayerValues(const std::size_t player_index) { if (profile_name.empty()) { // Use the global input config player = Settings::values.players.GetValue(true)[player_index]; + player.profile_name = ""; return; } } diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 3b321dad1..8a8cdbc44 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -327,7 +327,9 @@ int main(int argc, char** argv) { #endif MicroProfileOnThreadCreate("EmuThread"); - SCOPE_EXIT({ MicroProfileShutdown(); }); + SCOPE_EXIT { + MicroProfileShutdown(); + }; Common::ConfigureNvidiaEnvironmentFlags(); |