diff options
505 files changed, 20686 insertions, 12933 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..a84ac77a2 --- /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]" && it.isNotEmpty()) { +                hasMapping = true +            } +        } +        analogs.forEach { +            if (it != "[empty]" && it.isNotEmpty()) { +                hasMapping = true +            } +        } +        motions.forEach { +            if (it != "[empty]" && it.isNotEmpty()) { +                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/fragments/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt index 60e029f34..7f562a1f4 100644 --- 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/features/settings/ui/SettingsDialogFragment.kt @@ -1,7 +1,7 @@  // SPDX-FileCopyrightText: 2023 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later -package org.yuzu.yuzu_emu.fragments +package org.yuzu.yuzu_emu.features.settings.ui  import android.app.Dialog  import android.content.DialogInterface @@ -11,19 +11,23 @@ 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.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.model.SettingsViewModel +import org.yuzu.yuzu_emu.utils.ParamPackage +import org.yuzu.yuzu_emu.utils.collect  class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener {      private var type = 0 @@ -35,6 +39,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener      private val settingsViewModel: SettingsViewModel by activityViewModels()      private lateinit var sliderBinding: DialogSliderBinding +    private lateinit var stringInputBinding: DialogEditTextBinding      override fun onCreate(savedInstanceState: Bundle?) {          super.onCreate(savedInstanceState) @@ -50,8 +55,49 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener                  MaterialAlertDialogBuilder(requireContext())                      .setMessage(R.string.reset_setting_confirmation)                      .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> -                        settingsViewModel.clickedItem!!.setting.reset() -                        settingsViewModel.setAdapterItemChanged(position) +                        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() @@ -61,7 +107,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener                  val item = settingsViewModel.clickedItem as SingleChoiceSetting                  val value = getSelectionForSingleChoiceValue(item)                  MaterialAlertDialogBuilder(requireContext()) -                    .setTitle(item.nameId) +                    .setTitle(item.title)                      .setSingleChoiceItems(item.choicesId, value, this)                      .create()              } @@ -81,18 +127,38 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener                  }                  MaterialAlertDialogBuilder(requireContext()) -                    .setTitle(item.nameId) +                    .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.nameId) -                    .setSingleChoiceItems(item.choices, item.selectValueIndex, this) +                    .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()              } @@ -107,6 +173,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener      ): View? {          return when (type) {              SettingsItem.TYPE_SLIDER -> sliderBinding.root +            SettingsItem.TYPE_STRING_INPUT -> stringInputBinding.root              else -> super.onCreateView(inflater, container, savedInstanceState)          }      } @@ -115,17 +182,11 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener          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() -                        } -                    } +                settingsViewModel.sliderTextValue.collect(viewLifecycleOwner) { +                    sliderBinding.textValue.text = it +                } +                settingsViewModel.sliderProgress.collect(viewLifecycleOwner) { +                    sliderBinding.slider.value = it.toFloat()                  }              }          } @@ -145,10 +206,23 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener                  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()      } 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..3ea5f5008 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: Float +    ): AbstractIntSetting = +        object : AbstractIntSetting { +            val params get() = NativeInput.getStickParam(playerIndex, stick) + +            override val key = "" + +            override fun getInt(needsGlobal: Boolean): Int = +                (params.get(paramName, defaultValue) * 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 * 100).toInt() + +            override fun getValueAsString(needsGlobal: Boolean): String = +                getInt(needsGlobal).toString() + +            override fun reset() = setInt(this.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, 0.5f) +        val stickRangeSetting = +            getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 0.95f) +        val stickDeadzoneSetting = +            getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 0.15f) + +        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/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt index a135b80b4..ed60cf34f 100644 --- 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/features/settings/ui/SettingsSearchFragment.kt @@ -1,7 +1,7 @@  // SPDX-FileCopyrightText: 2023 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later -package org.yuzu.yuzu_emu.fragments +package org.yuzu.yuzu_emu.features.settings.ui  import android.content.Context  import android.os.Bundle @@ -15,21 +15,17 @@ 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.setVisible  import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins +import org.yuzu.yuzu_emu.utils.collect  class SettingsSearchFragment : Fragment() {      private var _binding: FragmentSettingsSearchBinding? = null @@ -85,14 +81,10 @@ class SettingsSearchFragment : Fragment() {              search()              binding.settingsList.smoothScrollToPosition(0)          } -        viewLifecycleOwner.lifecycleScope.launch { -            repeatOnLifecycle(Lifecycle.State.CREATED) { -                settingsViewModel.shouldReloadSettingsList.collect { -                    if (it) { -                        settingsViewModel.setShouldReloadSettingsList(false) -                        search() -                    } -                } +        settingsViewModel.shouldReloadSettingsList.collect(viewLifecycleOwner) { +            if (it) { +                settingsViewModel.setShouldReloadSettingsList(false) +                search()              }          } @@ -108,10 +100,9 @@ class SettingsSearchFragment : Fragment() {      private fun search() {          val searchTerm = binding.searchText.text.toString().lowercase() -        binding.clearButton.visibility = -            if (searchTerm.isEmpty()) View.INVISIBLE else View.VISIBLE +        binding.clearButton.setVisible(visible = searchTerm.isNotEmpty(), gone = false)          if (searchTerm.isEmpty()) { -            binding.noResultsView.visibility = View.VISIBLE +            binding.noResultsView.setVisible(visible = false, gone = false)              settingsAdapter?.submitList(emptyList())              return          } @@ -119,7 +110,7 @@ class SettingsSearchFragment : Fragment() {          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 title = item.value.title.lowercase()              val similarity = similarityAlgorithm.similarity(searchTerm, title)              if (similarity > 0.08) {                  Pair(similarity, item) @@ -138,8 +129,7 @@ class SettingsSearchFragment : Fragment() {              optionalSetting          }          settingsAdapter?.submitList(sortedList) -        binding.noResultsView.visibility = -            if (sortedList.isEmpty()) View.VISIBLE else View.INVISIBLE +        binding.noResultsView.setVisible(visible = sortedList.isEmpty(), gone = false)      }      private fun focusSearch() { 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/features/settings/ui/SettingsViewModel.kt index 5cb6a5d57..fbdca04e9 100644 --- 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/features/settings/ui/SettingsViewModel.kt @@ -1,20 +1,26 @@  // SPDX-FileCopyrightText: 2023 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later -package org.yuzu.yuzu_emu.model +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) @@ -36,6 +42,18 @@ class SettingsViewModel : ViewModel() {      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      } @@ -68,4 +86,27 @@ class SettingsViewModel : ViewModel() {      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/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/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..1226219ad 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>(); @@ -289,6 +292,9 @@ void EmulationSession::ShutdownEmulation() {      // Unload user input.      m_system.HIDCore().UnloadInputDevices(); +    // Enable all controllers +    m_system.HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); +      // Shutdown the main emulated process      if (m_load_result == Core::SystemResultStatus::Success) {          m_system.DetachDebugger(); @@ -357,60 +363,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 +407,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 +418,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 +532,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 +580,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 +620,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..4935a4607 --- /dev/null +++ b/src/android/app/src/main/jni/native_input.cpp @@ -0,0 +1,645 @@ +// 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(); +    } +} + +std::vector<s32> GetSupportedStyles(int 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<s32>(Core::HID::NpadStyleIndex::Fullkey)); +    } + +    if (npad_style_set.joycon_dual == 1) { +        supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconDual)); +    } + +    if (npad_style_set.joycon_left == 1) { +        supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconLeft)); +    } + +    if (npad_style_set.joycon_right == 1) { +        supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconRight)); +    } + +    if (player_index == 0 && npad_style_set.handheld == 1) { +        supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Handheld)); +    } + +    if (npad_style_set.gamecube == 1) { +        supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::GameCube)); +    } + +    return supported_indexes; +} + +void ConnectController(size_t player_index, bool connected) { +    auto& hid_core = EmulationSession::GetInstance().System().HIDCore(); +    ApplyControllerConfig(player_index, [&](Core::HID::EmulatedController* controller) { +        auto supported_styles = GetSupportedStyles(player_index); +        auto controller_style = controller->GetNpadStyleIndex(true); +        auto style = std::find(supported_styles.begin(), supported_styles.end(), +                               static_cast<int>(controller_style)); +        if (style == supported_styles.end() && !supported_styles.empty()) { +            controller->SetNpadStyleIndex( +                static_cast<Core::HID::NpadStyleIndex>(supported_styles[0])); +        } +    }); + +    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 supported_styles = GetSupportedStyles(j_player_index); +    jintArray j_supported_indexes = env->NewIntArray(supported_styles.size()); +    env->SetIntArrayRegion(j_supported_indexes, 0, supported_styles.size(), +                           supported_styles.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.h b/src/common/settings.h index aa054dc24..b2b071e7e 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -384,6 +384,12 @@ struct Values {                                                                    AstcRecompression::Bc3,                                                                    "astc_recompression",                                                                    Category::RendererAdvanced}; +    SwitchableSetting<VramUsageMode, true> vram_usage_mode{linkage, +                                                           VramUsageMode::Conservative, +                                                           VramUsageMode::Conservative, +                                                           VramUsageMode::Aggressive, +                                                           "vram_usage_mode", +                                                           Category::RendererAdvanced};      SwitchableSetting<bool> async_presentation{linkage,  #ifdef ANDROID                                                 true, diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index f42367e67..6e247e930 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -122,6 +122,8 @@ ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);  ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed); +ENUM(VramUsageMode, Conservative, Aggressive); +  ENUM(RendererBackend, OpenGL, Vulkan, Null);  ENUM(ShaderBackend, Glsl, Glasm, SpirV); 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 d636621de..f104d495b 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/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_aes_xts_storage.h b/src/core/file_sys/fssystem/fssystem_aes_xts_storage.h index f342efb57..0e83ca1b9 100644 --- a/src/core/file_sys/fssystem/fssystem_aes_xts_storage.h +++ b/src/core/file_sys/fssystem/fssystem_aes_xts_storage.h @@ -3,6 +3,7 @@  #pragma once +#include <mutex>  #include <optional>  #include "core/crypto/aes_util.h" 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 1bcc42890..cb9a11a63 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -77,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) { @@ -466,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))); @@ -964,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, @@ -1155,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{}; 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 8c33feb15..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,6 +127,40 @@ 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,      PartialForeground = 1, @@ -136,6 +169,11 @@ enum class LibraryAppletMode : u32 {      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_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_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/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 034c62f32..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 { 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.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.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.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 992c50713..388d2045c 100644 --- a/src/core/hle/service/am/process.cpp +++ b/src/core/hle/service/am/process.cpp @@ -68,7 +68,9 @@ bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_k      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_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/self_controller.cpp b/src/core/hle/service/am/self_controller.cpp deleted file mode 100644 index 65e249c0c..000000000 --- a/src/core/hle/service/am/self_controller.cpp +++ /dev/null @@ -1,470 +0,0 @@ -// 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/self_controller.h" -#include "core/hle/service/caps/caps_su.h" -#include "core/hle/service/hle_ipc.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, &ISelfController::SetMediaPlaybackState, "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, -                                                 applet->library_applet_mode)) { -        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::SetMediaPlaybackState(HLERequestContext& ctx) { -    IPC::RequestParser rp{ctx}; -    const u8 state = rp.Pop<u8>(); - -    LOG_WARNING(Service_AM, "(STUBBED) called, state={}", state); - -    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 ab21a1881..000000000 --- a/src/core/hle/service/am/self_controller.h +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/hle_ipc.h" -#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 SetMediaPlaybackState(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/applet_common_functions.cpp b/src/core/hle/service/am/service/applet_common_functions.cpp index 130614ae5..0f29ab285 100644 --- a/src/core/hle/service/am/applet_common_functions.cpp +++ b/src/core/hle/service/am/service/applet_common_functions.cpp @@ -2,8 +2,8 @@  // 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" +#include "core/hle/service/am/service/applet_common_functions.h" +#include "core/hle/service/cmif_serialization.h"  namespace Service::AM { @@ -20,18 +20,18 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,          {40, nullptr, "GetDisplayLogicalResolution"},          {42, nullptr, "SetDisplayMagnification"},          {50, nullptr, "SetHomeButtonDoubleClickEnabled"}, -        {51, nullptr, "GetHomeButtonDoubleClickEnabled"}, +        {51, D<&IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled>, "GetHomeButtonDoubleClickEnabled"},          {52, nullptr, "IsHomeButtonShortPressedBlocked"},          {60, nullptr, "IsVrModeCurtainRequired"},          {61, nullptr, "IsSleepRequiredByHighTemperature"},          {62, nullptr, "IsSleepRequiredByLowBattery"}, -        {70, &IAppletCommonFunctions::SetCpuBoostRequestPriority, "SetCpuBoostRequestPriority"}, +        {70, D<&IAppletCommonFunctions::SetCpuBoostRequestPriority>, "SetCpuBoostRequestPriority"},          {80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"},          {81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"},          {90, nullptr, "OpenNamedChannelAsParent"},          {91, nullptr, "OpenNamedChannelAsChild"},          {100, nullptr, "SetApplicationCoreUsageMode"}, -        {300, &IAppletCommonFunctions::GetCurrentApplicationId, "GetCurrentApplicationId"}, +        {300, D<&IAppletCommonFunctions::GetCurrentApplicationId>, "GetCurrentApplicationId"},      };      // clang-format on @@ -40,24 +40,24 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,  IAppletCommonFunctions::~IAppletCommonFunctions() = default; -void IAppletCommonFunctions::SetCpuBoostRequestPriority(HLERequestContext& ctx) { +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(); +} -    IPC::RequestParser rp{ctx}; - +Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) { +    LOG_WARNING(Service_AM, "(STUBBED) called");      std::scoped_lock lk{applet->lock}; -    applet->cpu_boost_request_priority = rp.Pop<s32>(); - -    IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ResultSuccess); +    applet->cpu_boost_request_priority = priority; +    R_SUCCEED();  } -void IAppletCommonFunctions::GetCurrentApplicationId(HLERequestContext& ctx) { +Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_id) {      LOG_WARNING(Service_AM, "(STUBBED) called"); - -    IPC::ResponseBuilder rb{ctx, 4}; -    rb.Push(ResultSuccess); -    rb.Push<u64>(system.GetApplicationProcessProgramID() & ~0xFFFULL); +    *out_application_id = system.GetApplicationProcessProgramID() & ~0xFFFULL; +    R_SUCCEED();  }  } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_common_functions.h b/src/core/hle/service/am/service/applet_common_functions.h index b86adf5cb..4424fc83d 100644 --- a/src/core/hle/service/am/applet_common_functions.h +++ b/src/core/hle/service/am/service/applet_common_functions.h @@ -3,6 +3,7 @@  #pragma once +#include "core/hle/service/cmif_types.h"  #include "core/hle/service/service.h"  namespace Service::AM { @@ -15,8 +16,9 @@ public:      ~IAppletCommonFunctions() override;  private: -    void SetCpuBoostRequestPriority(HLERequestContext& ctx); -    void GetCurrentApplicationId(HLERequestContext& ctx); +    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;  }; 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/application_creator.cpp b/src/core/hle/service/am/service/application_creator.cpp index 79ea045a3..568bb0122 100644 --- a/src/core/hle/service/am/application_creator.cpp +++ b/src/core/hle/service/am/service/application_creator.cpp @@ -1,8 +1,12 @@  // 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" +#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 { @@ -10,7 +14,7 @@ IApplicationCreator::IApplicationCreator(Core::System& system_)      : ServiceFramework{system_, "IApplicationCreator"} {      // clang-format off      static const FunctionInfo functions[] = { -        {0, nullptr, "CreateApplication"}, +        {0, D<&IApplicationCreator::CreateApplication>, "CreateApplication"},          {1, nullptr, "PopLaunchRequestedApplication"},          {10, nullptr, "CreateSystemApplication"},          {100, nullptr, "PopFloatingApplicationForDevelopment"}, @@ -22,4 +26,10 @@ IApplicationCreator::IApplicationCreator(Core::System& system_)  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/application_creator.h b/src/core/hle/service/am/service/application_creator.h index 375a3c476..9f939ebf6 100644 --- a/src/core/hle/service/am/application_creator.h +++ b/src/core/hle/service/am/service/application_creator.h @@ -3,14 +3,21 @@  #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/debug_functions.cpp b/src/core/hle/service/am/service/debug_functions.cpp index f80b970f2..fcac4776d 100644 --- a/src/core/hle/service/am/debug_functions.cpp +++ b/src/core/hle/service/am/service/debug_functions.cpp @@ -1,8 +1,7 @@  // 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" +#include "core/hle/service/am/service/debug_functions.h"  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/library_applet_creator.cpp b/src/core/hle/service/am/service/library_applet_creator.cpp index 00d5a0705..c97358d81 100644 --- a/src/core/hle/service/am/library_applet_creator.cpp +++ b/src/core/hle/service/am/service/library_applet_creator.cpp @@ -6,11 +6,11 @@  #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/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 { @@ -130,13 +130,13 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,      case LibraryAppletMode::PartialForegroundIndirectDisplay:          applet->hid_registration.EnableAppletToGetInput(true);          applet->focus_state = FocusState::InFocus; -        applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); +        applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground);          break;      case LibraryAppletMode::AllForegroundInitiallyHidden:          applet->hid_registration.EnableAppletToGetInput(false);          applet->focus_state = FocusState::NotInFocus; -        applet->system_buffer_manager.SetWindowVisibility(false); -        applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground); +        applet->display_layer_manager.SetWindowVisibility(false); +        applet->message_queue.PushMessage(AppletMessage::ChangeIntoBackground);          break;      } @@ -172,139 +172,97 @@ std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& syste  } // namespace -ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet_) -    : ServiceFramework{system_, "ILibraryAppletCreator"}, applet{std::move(applet_)} { +ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet) +    : ServiceFramework{system_, "ILibraryAppletCreator"}, m_applet{std::move(applet)} {      static const FunctionInfo functions[] = { -        {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, +        {0, D<&ILibraryAppletCreator::CreateLibraryApplet>, "CreateLibraryApplet"},          {1, nullptr, "TerminateAllLibraryApplets"},          {2, nullptr, "AreAnyLibraryAppletsLeft"}, -        {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, -        {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"}, -        {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"}, +        {10, D<&ILibraryAppletCreator::CreateStorage>, "CreateStorage"}, +        {11, D<&ILibraryAppletCreator::CreateTransferMemoryStorage>, "CreateTransferMemoryStorage"}, +        {12, D<&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); +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, applet, applet_id, applet_mode); +        library_applet = CreateGuestApplet(system, m_applet, applet_id, library_applet_mode);      }      if (!library_applet) { -        library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode); +        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); - -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultUnknown); -        return; +        R_THROW(ResultUnknown);      }      // 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); +    m_applet->library_applet_launchable_event.Signal(); +    *out_library_applet_accessor = library_applet; +    R_SUCCEED();  } -void ILibraryAppletCreator::CreateStorage(HLERequestContext& ctx) { -    IPC::RequestParser rp{ctx}; - -    const s64 size{rp.Pop<s64>()}; - +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"); -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultUnknown); -        return; +        R_THROW(ResultUnknown);      } -    std::vector<u8> data(size); - -    IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -    rb.Push(ResultSuccess); -    rb.PushIpcInterface<IStorage>(system, AM::CreateStorage(std::move(data))); +    *out_storage = std::make_shared<IStorage>(system, AM::CreateStorage(std::vector<u8>(size))); +    R_SUCCEED();  } -void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx) { -    IPC::RequestParser rp{ctx}; - -    struct Parameters { -        bool is_writable; -        s64 size; -    }; +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); -    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) { +    if (size <= 0) {          LOG_ERROR(Service_AM, "size is less than or equal to 0"); -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultUnknown); -        return; +        R_THROW(ResultUnknown);      } -    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; +    if (!transfer_memory_handle) { +        LOG_ERROR(Service_AM, "transfer_memory_handle is null"); +        R_THROW(ResultUnknown);      } -    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)); +    *out_storage = std::make_shared<IStorage>( +        system, AM::CreateTransferMemoryStorage(transfer_memory_handle->GetOwner()->GetMemory(), +                                                transfer_memory_handle.Get(), is_writable, size)); +    R_SUCCEED();  } -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); +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"); -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultUnknown); -        return; +        R_THROW(ResultUnknown);      } -    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; +    if (!transfer_memory_handle) { +        LOG_ERROR(Service_AM, "transfer_memory_handle is null"); +        R_THROW(ResultUnknown);      } -    IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -    rb.Push(ResultSuccess); -    rb.PushIpcInterface<IStorage>( -        system, AM::CreateHandleStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(), size)); +    *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/process_winding_controller.h b/src/core/hle/service/am/service/process_winding_controller.h index 71ae4c4f5..4408af1f1 100644 --- a/src/core/hle/service/am/process_winding_controller.h +++ b/src/core/hle/service/am/service/process_winding_controller.h @@ -3,11 +3,14 @@  #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: @@ -15,10 +18,11 @@ public:      ~IProcessWindingController() override;  private: -    void GetLaunchReason(HLERequestContext& ctx); -    void OpenCallingLibraryApplet(HLERequestContext& ctx); +    Result GetLaunchReason(Out<AppletProcessLaunchReason> out_launch_reason); +    Result OpenCallingLibraryApplet( +        Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet); -    const std::shared_ptr<Applet> 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/storage.h b/src/core/hle/service/am/service/storage.h index 10d00b141..cde2ed0ea 100644 --- a/src/core/hle/service/am/storage.h +++ b/src/core/hle/service/am/service/storage.h @@ -3,29 +3,33 @@  #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::shared_ptr<LibraryAppletStorage> impl);      explicit IStorage(Core::System& system_, std::vector<u8>&& buffer);      ~IStorage() override;      std::shared_ptr<LibraryAppletStorage> GetImpl() const { -        return impl; +        return m_impl;      }      std::vector<u8> GetData() const;  private: -    void Open(HLERequestContext& ctx); -    void OpenTransferStorage(HLERequestContext& ctx); +    Result Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor); +    Result OpenTransferStorage( +        Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor); -    const std::shared_ptr<LibraryAppletStorage> impl; +    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/storage_accessor.h b/src/core/hle/service/am/service/storage_accessor.h index b9aa85a66..1a01730e0 100644 --- a/src/core/hle/service/am/storage_accessor.h +++ b/src/core/hle/service/am/service/storage_accessor.h @@ -3,35 +3,36 @@  #pragma once -#include "core/hle/service/am/storage.h" +#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_); +    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); +    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> impl; +    const std::shared_ptr<LibraryAppletStorage> m_impl;  };  class ITransferStorageAccessor final : public ServiceFramework<ITransferStorageAccessor> {  public:      explicit ITransferStorageAccessor(Core::System& system_, -                                      std::shared_ptr<LibraryAppletStorage> impl_); +                                      std::shared_ptr<LibraryAppletStorage> impl);      ~ITransferStorageAccessor() override;  private: -    void GetSize(HLERequestContext& ctx); -    void GetHandle(HLERequestContext& ctx); +    Result GetSize(Out<s64> out_size); +    Result GetHandle(Out<s64> out_size, OutCopyHandle<Kernel::KTransferMemory> out_handle); -    const std::shared_ptr<LibraryAppletStorage> impl; +    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.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_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/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 48923fe41..000000000 --- a/src/core/hle/service/am/system_buffer_manager.cpp +++ /dev/null @@ -1,80 +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) { -        m_nvnflinger->GetSystemBufferManager().Finalize(m_process); -    } -} - -bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process, -                                     AppletId applet_id, LibraryAppletMode mode) { -    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; -    } - -    Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None; -    if (mode == LibraryAppletMode::PartialForeground || -        mode == LibraryAppletMode::PartialForegroundIndirectDisplay) { -        blending = Nvnflinger::LayerBlending::Coverage; -    } - -    const auto display_id = m_nvnflinger->OpenDisplay("Default").value(); -    const auto res = m_nvnflinger->GetSystemBufferManager().Initialize( -        m_process, &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id, blending); - -    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) { -    if (!m_buffer_sharing_enabled) { -        return VI::ResultPermissionDenied; -    } - -    return m_nvnflinger->GetSystemBufferManager().WriteAppletCaptureBuffer(out_was_written, -                                                                           out_fbshare_layer_index); -} - -} // 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 0690f68b6..000000000 --- a/src/core/hle/service/am/system_buffer_manager.h +++ /dev/null @@ -1,52 +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, -                    LibraryAppletMode mode); - -    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 c07ef228b..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->focus_state = FocusState::InFocus; -        applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); -    } else { -        applet->focus_state = FocusState::NotInFocus; -        applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground); -    } - -    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/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..7a91727f9 --- /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, +    u32 flags, u64 application_id, Uid account_id) { +    LOG_WARNING(Service_NS, "(STUBBED) called, flags={}, application_id={:016X}, account_id={}", +                flags, application_id, account_id.uuid.FormattedString()); + +    if (!out_rights.empty()) { +        ApplicationRightsOnClient rights{}; +        rights.application_id = application_id; +        rights.uid = account_id.uuid; +        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..f33d269b3 --- /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, +        u32 flags, u64 application_id, Uid account_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/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..2dd664c4e --- /dev/null +++ b/src/core/hle/service/ns/ns_types.h @@ -0,0 +1,116 @@ +// 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."); + +struct Uid { +    alignas(8) Common::UUID uuid; +}; +static_assert(sizeof(Uid) == 0x10, "Uid 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/iplatform_service_manager.cpp b/src/core/hle/service/ns/platform_service_manager.cpp index 46268be95..23cf05005 100644 --- a/src/core/hle/service/ns/iplatform_service_manager.cpp +++ b/src/core/hle/service/ns/platform_service_manager.cpp @@ -18,9 +18,9 @@  #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/ipc_helpers.h" -#include "core/hle/service/ns/iplatform_service_manager.h" +#include "core/hle/service/ns/platform_service_manager.h"  namespace Service::NS { @@ -37,11 +37,6 @@ constexpr u32 EXPECTED_MAGIC{0x36f81a1e};  // What we expect the encrypted bfttf  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, @@ -138,13 +133,13 @@ IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const ch      : 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"}, +        {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"}, @@ -208,47 +203,33 @@ IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const ch  IPlatformServiceManager::~IPlatformServiceManager() = default; -void IPlatformServiceManager::RequestLoad(HLERequestContext& ctx) { -    IPC::RequestParser rp{ctx}; -    const u32 shared_font_type{rp.Pop<u32>()}; +Result IPlatformServiceManager::RequestLoad(SharedFontType type) {      // 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); +    LOG_DEBUG(Service_NS, "called, shared_font_type={}", type); +    R_SUCCEED();  } -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)); +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();  } -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); +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();  } -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); +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();  } -void IPlatformServiceManager::GetSharedMemoryNativeHandle(HLERequestContext& ctx) { +Result IPlatformServiceManager::GetSharedMemoryNativeHandle( +    OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_native_handle) {      // Map backing memory for the font data      LOG_DEBUG(Service_NS, "called"); @@ -256,50 +237,37 @@ void IPlatformServiceManager::GetSharedMemoryNativeHandle(HLERequestContext& ctx      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()); +    // FIXME: this shouldn't belong to the kernel +    *out_shared_memory_native_handle = &kernel.GetFontSharedMem(); +    R_SUCCEED();  } -void IPlatformServiceManager::GetSharedFontInOrderOfPriority(HLERequestContext& ctx) { +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 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; +    constexpr size_t MaxElementCount = 6;      // 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); -    } +    const auto max_size = std::min({MaxElementCount, out_font_codes.size(), out_font_offsets.size(), +                                    out_font_sizes.size(), impl->shared_font_regions.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)); +    for (size_t i = 0; i < max_size; i++) { +        auto region = impl->GetSharedFontRegion(i); -    ctx.WriteBuffer(font_codes, 0); -    ctx.WriteBuffer(font_offsets, 1); -    ctx.WriteBuffer(font_sizes, 2); +        out_font_codes[i] = static_cast<u32>(i); +        out_font_offsets[i] = region.offset; +        out_font_sizes[i] = region.size; +    } -    rb.Push(ResultSuccess); -    rb.Push<u8>(static_cast<u8>(LoadState::Done)); // Fonts Loaded -    rb.Push<u32>(static_cast<u32>(font_codes.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/iplatform_service_manager.h b/src/core/hle/service/ns/platform_service_manager.h index 03071e02b..b82c385a6 100644 --- a/src/core/hle/service/ns/iplatform_service_manager.h +++ b/src/core/hle/service/ns/platform_service_manager.h @@ -5,7 +5,9 @@  #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 { @@ -23,6 +25,20 @@ enum class FontArchives : u64 {      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"), @@ -42,12 +58,17 @@ public:      ~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); +    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; 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..138400541 --- /dev/null +++ b/src/core/hle/service/ns/query_service.cpp @@ -0,0 +1,56 @@ +// 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, u64 application_id, Uid account_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.uuid.FormattedString()); +    R_SUCCEED(); +} + +} // namespace Service::NS diff --git a/src/core/hle/service/ns/pdm_qry.h b/src/core/hle/service/ns/query_service.h index c98e01660..c4c82b752 100644 --- a/src/core/hle/service/ns/pdm_qry.h +++ b/src/core/hle/service/ns/query_service.h @@ -3,6 +3,9 @@  #pragma once +#include "common/uuid.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/ns/ns_types.h"  #include "core/hle/service/service.h"  namespace Service::NS { @@ -20,13 +23,14 @@ struct PlayStatistics {  };  static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size"); -class PDM_QRY final : public ServiceFramework<PDM_QRY> { +class IQueryService final : public ServiceFramework<IQueryService> {  public: -    explicit PDM_QRY(Core::System& system_); -    ~PDM_QRY() override; +    explicit IQueryService(Core::System& system_); +    ~IQueryService() override;  private: -    void QueryPlayStatisticsByApplicationIdAndUserAccountId(HLERequestContext& ctx); +    Result QueryPlayStatisticsByApplicationIdAndUserAccountId( +        Out<PlayStatistics> out_play_statistics, bool unknown, u64 application_id, Uid account_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/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/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp index be7eb97a3..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,24 +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, -            .blending = layer.GetBlending(), -            .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. @@ -135,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;          }      } @@ -146,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;      } @@ -178,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) { @@ -203,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/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index 687ccc9f9..9e3b68b8a 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -1,335 +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, LayerBlending blending) { -    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, blending); -    return layer_id; -} - -void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending) { -    const auto buffer_id = next_buffer_queue_id++; -    display.CreateLayer(layer_id, buffer_id, nvdrv->container); -    display.FindLayer(layer_id)->SetBlending(blending); -} - -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 4cf4f069d..5c41f3013 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h @@ -3,170 +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" -#include "core/hle/service/nvnflinger/hwc_layer.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, -                                                 LayerBlending blending = LayerBlending::None); - -    /// 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, LayerBlending blending); - -    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/am/omm.cpp b/src/core/hle/service/omm/operation_mode_manager.cpp index 66824e495..fe7ed84a7 100644 --- a/src/core/hle/service/am/omm.cpp +++ b/src/core/hle/service/omm/operation_mode_manager.cpp @@ -1,11 +1,12 @@  // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/hle/service/am/omm.h" +#include "core/hle/service/omm/operation_mode_manager.h" -namespace Service::AM { +namespace Service::OMM { -OMM::OMM(Core::System& system_) : ServiceFramework{system_, "omm"} { +IOperationModeManager::IOperationModeManager(Core::System& system_) +    : ServiceFramework{system_, "omm"} {      // clang-format off      static const FunctionInfo functions[] = {          {0, nullptr, "GetOperationMode"}, @@ -43,6 +44,6 @@ OMM::OMM(Core::System& system_) : ServiceFramework{system_, "omm"} {      RegisterHandlers(functions);  } -OMM::~OMM() = default; +IOperationModeManager::~IOperationModeManager() = default; -} // namespace Service::AM +} // 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/am/idle.cpp b/src/core/hle/service/omm/policy_manager_system.cpp index 603515284..1cd6fd807 100644 --- a/src/core/hle/service/am/idle.cpp +++ b/src/core/hle/service/omm/policy_manager_system.cpp @@ -1,11 +1,12 @@  // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/hle/service/am/idle.h" +#include "core/hle/service/omm/policy_manager_system.h" -namespace Service::AM { +namespace Service::OMM { -IdleSys::IdleSys(Core::System& system_) : ServiceFramework{system_, "idle:sys"} { +IPolicyManagerSystem::IPolicyManagerSystem(Core::System& system_) +    : ServiceFramework{system_, "idle:sys"} {      // clang-format off      static const FunctionInfo functions[] = {          {0, nullptr, "GetAutoPowerDownEvent"}, @@ -20,6 +21,6 @@ IdleSys::IdleSys(Core::System& system_) : ServiceFramework{system_, "idle:sys"}      RegisterHandlers(functions);  } -IdleSys::~IdleSys() = default; +IPolicyManagerSystem::~IPolicyManagerSystem() = default; -} // namespace Service::AM +} // 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/am/spsm.cpp b/src/core/hle/service/omm/power_state_interface.cpp index ec581e32b..22cac8259 100644 --- a/src/core/hle/service/am/spsm.cpp +++ b/src/core/hle/service/omm/power_state_interface.cpp @@ -1,11 +1,12 @@  // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/hle/service/am/spsm.h" +#include "core/hle/service/omm/power_state_interface.h" -namespace Service::AM { +namespace Service::OMM { -SPSM::SPSM(Core::System& system_) : ServiceFramework{system_, "spsm"} { +IPowerStateInterface::IPowerStateInterface(Core::System& system_) +    : ServiceFramework{system_, "spsm"} {      // clang-format off      static const FunctionInfo functions[] = {          {0, nullptr, "GetState"}, @@ -26,6 +27,6 @@ SPSM::SPSM(Core::System& system_) : ServiceFramework{system_, "spsm"} {      RegisterHandlers(functions);  } -SPSM::~SPSM() = default; +IPowerStateInterface::~IPowerStateInterface() = default; -} // namespace Service::AM +} // 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 eca35d82a..000000000 --- a/src/core/hle/service/vi/layer/vi_layer.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/nvnflinger/hwc_layer.h" -#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_)}, -      blending{Nvnflinger::LayerBlending::None}, 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 14e229903..000000000 --- a/src/core/hle/service/vi/layer/vi_layer.h +++ /dev/null @@ -1,118 +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::Nvnflinger { -enum class LayerBlending : u32; -} - -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); -    } - -    Nvnflinger::LayerBlending GetBlending() { -        return blending; -    } - -    void SetBlending(Nvnflinger::LayerBlending b) { -        blending = b; -    } - -private: -    const u64 layer_id; -    const u32 binder_id; -    android::BufferQueueCore& core; -    android::BufferQueueProducer& binder; -    std::shared_ptr<android::BufferItemConsumer> consumer; -    Service::Nvnflinger::LayerBlending blending; -    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/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/vi/shared_buffer_manager.cpp index 90f7248a0..12cba16fa 100644 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp +++ b/src/core/hle/service/vi/shared_buffer_manager.cpp @@ -9,15 +9,15 @@  #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/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::Nvnflinger { +namespace Service::VI {  namespace { @@ -26,7 +26,6 @@ Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_      using Core::Memory::YUZU_PAGESIZE;      // Allocate memory for the system shared buffer. -    // FIXME: This memory belongs to vi's .data section.      auto& kernel = system.Kernel();      // Hold a temporary page group reference while we try to map it. @@ -204,15 +203,15 @@ void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 han  } // 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)) {} +SharedBufferManager::SharedBufferManager(Core::System& system, Container& container, +                                         std::shared_ptr<Nvidia::Module> nvdrv) +    : m_system(system), m_container(container), m_nvdrv(std::move(nvdrv)) {} -FbShareBufferManager::~FbShareBufferManager() = default; +SharedBufferManager::~SharedBufferManager() = default; -Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id, -                                        u64* out_layer_handle, u64 display_id, -                                        LayerBlending blending) { +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. @@ -237,7 +236,7 @@ Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* ou                                                   owner_process, m_system));      // Create new session. -    auto [it, was_emplaced] = m_sessions.emplace(aruid, FbShareSession{}); +    auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});      auto& session = it->second;      auto& container = m_nvdrv->GetContainer(); @@ -249,17 +248,18 @@ Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* ou                                    session.nvmap_fd, map_address, SharedBufferSize));      // Create and open a layer for the display. -    session.layer_id = m_flinger.CreateLayer(m_display_id, blending).value(); -    m_flinger.OpenLayer(session.layer_id); +    s32 producer_binder_id; +    R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id), +                                       std::addressof(session.layer_id), display_id)); -    // Get the layer. -    VI::Layer* layer = m_flinger.FindLayer(m_display_id, session.layer_id); -    ASSERT(layer != nullptr); +    // Configure blending. +    R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending));      // Get the producer and set preallocated buffers. -    auto& producer = layer->GetBufferQueue(); -    MakeGraphicBuffer(producer, 0, session.buffer_nvmap_handle); -    MakeGraphicBuffer(producer, 1, session.buffer_nvmap_handle); +    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; @@ -269,7 +269,7 @@ Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* ou      R_SUCCEED();  } -void FbShareBufferManager::Finalize(Kernel::KProcess* owner_process) { +void SharedBufferManager::DestroySession(Kernel::KProcess* owner_process) {      std::scoped_lock lk{m_guard};      if (m_buffer_id == 0) { @@ -285,7 +285,7 @@ void FbShareBufferManager::Finalize(Kernel::KProcess* owner_process) {      auto& session = it->second;      // Destroy the layer. -    m_flinger.DestroyLayer(session.layer_id); +    m_container.DestroyStrayLayer(session.layer_id);      // Close nvmap handle.      FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd); @@ -301,11 +301,11 @@ void FbShareBufferManager::Finalize(Kernel::KProcess* owner_process) {      m_sessions.erase(it);  } -Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, -                                                           s32* out_nvmap_handle, -                                                           SharedMemoryPoolLayout* out_pool_layout, -                                                           u64 buffer_id, -                                                           u64 applet_resource_user_id) { +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); @@ -319,36 +319,18 @@ Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,      R_SUCCEED();  } -Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) { -    // Ensure the layer id is valid. -    R_UNLESS(layer_id > 0, 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)); - +Result SharedBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence, +                                                     std::array<s32, 4>& out_slot_indexes, +                                                     s64* out_target_slot, u64 layer_id) {      // Get the producer. -    auto& producer = layer->GetBufferQueue(); +    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, +    R_UNLESS(producer->DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0, +                                     SharedBufferWidth, SharedBufferHeight, +                                     SharedBufferBlockLinearFormat, 0) == android::Status::NoError,               VI::ResultOperationFailed);      // Assign remaining outputs. @@ -359,27 +341,22 @@ Result FbShareBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,      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)); - +Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence, +                                                     Common::Rectangle<s32> crop_region, +                                                     u32 transform, s32 swap_interval, u64 layer_id, +                                                     s64 slot) {      // Get the producer. -    auto& producer = layer->GetBufferQueue(); +    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)) == +    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); +        producer->CancelBuffer(static_cast<s32>(slot), fence);      };      // Queue the buffer to the producer. @@ -389,7 +366,7 @@ Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,      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)) == +    R_UNLESS(producer->QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) ==                   android::Status::NoError,               VI::ResultOperationFailed); @@ -397,25 +374,32 @@ Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,      R_SUCCEED();  } -Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, -                                                                 u64 layer_id) { -    std::scoped_lock lk{m_guard}; +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)); -    // Get the layer. -    VI::Layer* layer; -    R_TRY(this->GetLayerFromId(std::addressof(layer), 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. -    auto& producer = layer->GetBufferQueue(); +    std::shared_ptr<android::BufferQueueProducer> producer; +    R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));      // Set the event. -    *out_event = std::addressof(producer.GetNativeHandle()); +    *out_event = producer->GetNativeHandle({});      // We succeeded.      R_SUCCEED();  } -Result FbShareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) { +Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {      std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer());      Common::ScratchBuffer<u32> scratch; @@ -444,4 +428,4 @@ Result FbShareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32      R_SUCCEED();  } -} // namespace Service::Nvnflinger +} // namespace Service::VI diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h b/src/core/hle/service/vi/shared_buffer_manager.h index b79a7d23a..7c9bb7199 100644 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h +++ b/src/core/hle/service/vi/shared_buffer_manager.h @@ -8,15 +8,27 @@  #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/hwc_layer.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;  } -namespace Service::Nvnflinger { +union Result; + +namespace Service::VI { + +class Container;  struct SharedMemorySlot {      u64 buffer_offset; @@ -32,17 +44,17 @@ struct SharedMemoryPoolLayout {  };  static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); -struct FbShareSession; +struct SharedBufferSession; -class FbShareBufferManager final { +class SharedBufferManager final {  public: -    explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger, -                                  std::shared_ptr<Nvidia::Module> nvdrv); -    ~FbShareBufferManager(); +    explicit SharedBufferManager(Core::System& system, Container& container, +                                 std::shared_ptr<Nvidia::Module> nvdrv); +    ~SharedBufferManager(); -    Result Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle, -                      u64 display_id, LayerBlending blending); -    void Finalize(Kernel::KProcess* owner_process); +    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, @@ -51,32 +63,30 @@ public:                                      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: -    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;      SharedMemoryPoolLayout m_pool_layout = {}; -    std::map<u64, FbShareSession> m_sessions; +    std::map<u64, SharedBufferSession> m_sessions;      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; +    Container& m_container; +    const std::shared_ptr<Nvidia::Module> m_nvdrv;  }; -struct FbShareSession { +struct SharedBufferSession {      Nvidia::DeviceFD nvmap_fd = {};      Nvidia::NvCore::SessionId session_id = {};      u64 layer_id = {};      u32 buffer_nvmap_handle = 0;  }; -} // namespace Service::Nvnflinger +} // 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 2bebfeef9..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; 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.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 0031fa5fb..3f9698d6b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -261,7 +261,9 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {      case Stage::Geometry:          execution_model = spv::ExecutionModel::Geometry;          ctx.AddCapability(spv::Capability::Geometry); -        ctx.AddCapability(spv::Capability::GeometryStreams); +        if (ctx.profile.support_geometry_streams) { +            ctx.AddCapability(spv::Capability::GeometryStreams); +        }          switch (ctx.runtime_info.input_topology) {          case InputTopology::Points:              ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints); 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/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index 9f7b6bb4b..f60da758e 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp @@ -129,7 +129,9 @@ void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {      if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) {          ConvertDepthMode(ctx);      } -    if (stream.IsImmediate()) { +    if (!ctx.profile.support_geometry_streams) { +        throw NotImplementedException("Geometry streams"); +    } else if (stream.IsImmediate()) {          ctx.OpEmitStreamVertex(ctx.Def(stream));      } else {          LOG_WARNING(Shader_SPIRV, "Stream is not immediate"); @@ -140,7 +142,9 @@ void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {  }  void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) { -    if (stream.IsImmediate()) { +    if (!ctx.profile.support_geometry_streams) { +        throw NotImplementedException("Geometry streams"); +    } else if (stream.IsImmediate()) {          ctx.OpEndStreamPrimitive(ctx.Def(stream));      } else {          LOG_WARNING(Shader_SPIRV, "Stream is not immediate"); diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 7578d41cc..90e46bb1b 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -44,6 +44,7 @@ struct Profile {      bool support_gl_derivative_control{};      bool support_scaled_attributes{};      bool support_multi_viewport{}; +    bool support_geometry_streams{};      bool warp_size_potentially_larger_than_guest{}; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 6d3d933c5..ed7a5b27e 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -35,7 +35,7 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R      const s64 min_spacing_critical = device_local_memory - 512_MiB;      const s64 mem_threshold = std::min(device_local_memory, TARGET_THRESHOLD);      const s64 min_vacancy_expected = (6 * mem_threshold) / 10; -    const s64 min_vacancy_critical = (3 * mem_threshold) / 10; +    const s64 min_vacancy_critical = (2 * mem_threshold) / 10;      minimum_memory = static_cast<u64>(          std::max(std::min(device_local_memory - min_vacancy_expected, min_spacing_expected),                   DEFAULT_EXPECTED_MEMORY)); @@ -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/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/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/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/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_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp index 3847a9a13..4e41afe5b 100644 --- a/src/video_core/renderer_vulkan/present/layer.cpp +++ b/src/video_core/renderer_vulkan/present/layer.cpp @@ -82,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); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index d50417116..c553f5b3d 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -144,7 +144,9 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu          return;      } -    SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); +    SCOPE_EXIT { +        render_window.OnFrameDisplayed(); +    };      RenderAppletCaptureLayer(framebuffers); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 20f7a9702..d34b585d6 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -352,6 +352,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,          .support_native_ndc = device.IsExtDepthClipControlSupported(),          .support_scaled_attributes = !device.MustEmulateScaledFormats(),          .support_multi_viewport = device.SupportsMultiViewport(), +        .support_geometry_streams = device.AreTransformFeedbackGeometryStreamsSupported(),          .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), 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/surface.cpp b/src/video_core/surface.cpp index 5b3c7aa5a..9055b1b92 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -3,6 +3,7 @@  #include "common/common_types.h"  #include "common/math_util.h" +#include "common/settings.h"  #include "video_core/surface.h"  namespace VideoCore::Surface { @@ -400,11 +401,20 @@ std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {      return {DefaultBlockWidth(format), DefaultBlockHeight(format)};  } -u64 EstimatedDecompressedSize(u64 base_size, PixelFormat format) { +u64 TranscodedAstcSize(u64 base_size, PixelFormat format) {      constexpr u64 RGBA8_PIXEL_SIZE = 4;      const u64 base_block_size = static_cast<u64>(DefaultBlockWidth(format)) *                                  static_cast<u64>(DefaultBlockHeight(format)) * RGBA8_PIXEL_SIZE; -    return (base_size * base_block_size) / BytesPerBlock(format); +    const u64 uncompressed_size = (base_size * base_block_size) / BytesPerBlock(format); + +    switch (Settings::values.astc_recompression.GetValue()) { +    case Settings::AstcRecompression::Bc1: +        return uncompressed_size / 8; +    case Settings::AstcRecompression::Bc3: +        return uncompressed_size / 4; +    default: +        return uncompressed_size; +    }  }  } // namespace VideoCore::Surface diff --git a/src/video_core/surface.h b/src/video_core/surface.h index a5e8e2f62..ec9cd2fbf 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -517,6 +517,6 @@ size_t PixelComponentSizeBitsInteger(PixelFormat format);  std::pair<u32, u32> GetASTCBlockSize(PixelFormat format); -u64 EstimatedDecompressedSize(u64 base_size, PixelFormat format); +u64 TranscodedAstcSize(u64 base_size, PixelFormat format);  } // namespace VideoCore::Surface diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index b72788c6d..9444becce 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -42,6 +42,7 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {          };      }      rescaleable = false; +    is_sparse = config.is_sparse != 0;      tile_width_spacing = config.tile_width_spacing;      if (config.texture_type != TextureType::Texture2D &&          config.texture_type != TextureType::Texture2DNoMipmap) { diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index 8a4cb0cbd..eb490a642 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -41,6 +41,7 @@ struct ImageInfo {      bool downscaleable = false;      bool forced_flushed = false;      bool dma_downloaded = false; +    bool is_sparse = false;  };  } // namespace VideoCommon diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 3a1cc060e..53b4876f2 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -55,7 +55,7 @@ TextureCache<P>::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag          const s64 min_spacing_critical = device_local_memory - 512_MiB;          const s64 mem_threshold = std::min(device_local_memory, TARGET_THRESHOLD);          const s64 min_vacancy_expected = (6 * mem_threshold) / 10; -        const s64 min_vacancy_critical = (3 * mem_threshold) / 10; +        const s64 min_vacancy_critical = (2 * mem_threshold) / 10;          expected_memory = static_cast<u64>(              std::max(std::min(device_local_memory - min_vacancy_expected, min_spacing_expected),                       DEFAULT_EXPECTED_MEMORY)); @@ -600,17 +600,17 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz                              [&](ImageId id, Image&) { deleted_images.push_back(id); });      for (const ImageId id : deleted_images) {          Image& image = slot_images[id]; -        if (True(image.flags & ImageFlagBits::CpuModified)) { -            continue; +        if (False(image.flags & ImageFlagBits::CpuModified)) { +            image.flags |= ImageFlagBits::CpuModified; +            if (True(image.flags & ImageFlagBits::Tracked)) { +                UntrackImage(image, id); +            }          } -        image.flags |= ImageFlagBits::CpuModified; +          if (True(image.flags & ImageFlagBits::Remapped)) {              continue;          }          image.flags |= ImageFlagBits::Remapped; -        if (True(image.flags & ImageFlagBits::Tracked)) { -            UntrackImage(image, id); -        }      }  } @@ -1469,7 +1469,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA      const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr);      Image& new_image = slot_images[new_image_id]; -    if (!gpu_memory->IsContinuousRange(new_image.gpu_addr, new_image.guest_size_bytes)) { +    if (!gpu_memory->IsContinuousRange(new_image.gpu_addr, new_image.guest_size_bytes) && +        new_info.is_sparse) {          new_image.flags |= ImageFlagBits::Sparse;      } @@ -1978,7 +1979,7 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {      if ((IsPixelFormatASTC(image.info.format) &&           True(image.flags & ImageFlagBits::AcceleratedUpload)) ||          True(image.flags & ImageFlagBits::Converted)) { -        tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); +        tentative_size = TranscodedAstcSize(tentative_size, image.info.format);      }      total_used_memory += Common::AlignUp(tentative_size, 1024);      image.lru_index = lru_cache.Insert(image_id, frame_tick); @@ -2148,7 +2149,7 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {      if ((IsPixelFormatASTC(image.info.format) &&           True(image.flags & ImageFlagBits::AcceleratedUpload)) ||          True(image.flags & ImageFlagBits::Converted)) { -        tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); +        tentative_size = TranscodedAstcSize(tentative_size, image.info.format);      }      total_used_memory -= Common::AlignUp(tentative_size, 1024);      const GPUVAddr gpu_addr = image.gpu_addr; 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/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index d7216d349..b94924a58 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1297,10 +1297,6 @@ u64 Device::GetDeviceMemoryUsage() const {  }  void Device::CollectPhysicalMemoryInfo() { -    // Account for resolution scaling in memory limits -    const size_t normal_memory = 6_GiB; -    const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1); -      // Calculate limits using memory budget      VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};      budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; @@ -1331,7 +1327,15 @@ void Device::CollectPhysicalMemoryInfo() {      if (!is_integrated) {          const u64 reserve_memory = std::min<u64>(device_access_memory / 8, 1_GiB);          device_access_memory -= reserve_memory; -        device_access_memory = std::min<u64>(device_access_memory, normal_memory + scaler_memory); + +        if (Settings::values.vram_usage_mode.GetValue() != Settings::VramUsageMode::Aggressive) { +            // Account for resolution scaling in memory limits +            const size_t normal_memory = 6_GiB; +            const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1); +            device_access_memory = +                std::min<u64>(device_access_memory, normal_memory + scaler_memory); +        } +          return;      }      const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index a2ec26697..e3abe8ddf 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -499,6 +499,11 @@ public:          return extensions.transform_feedback;      } +    /// Returns true if the device supports VK_EXT_transform_feedback properly. +    bool AreTransformFeedbackGeometryStreamsSupported() const { +        return features.transform_feedback.geometryStreams; +    } +      /// Returns true if the device supports VK_EXT_custom_border_color.      bool IsExtCustomBorderColorSupported() const {          return extensions.custom_border_color; 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 ce65b2bf1..0549e8ae4 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -54,13 +54,28 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {             QStringLiteral());      // Core -    INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), QStringLiteral()); -    INSERT(Settings, memory_layout_mode, tr("Memory Layout"), QStringLiteral()); +    INSERT( +        Settings, use_multi_core, tr("Multicore CPU Emulation"), +        tr("This option increases CPU emulation thread use from 1 to the Switch’s maximum of 4.\n" +           "This is mainly a debug option and shouldn’t be disabled.")); +    INSERT( +        Settings, memory_layout_mode, tr("Memory Layout"), +        tr("Increases the amount of emulated RAM from the stock 4GB of the retail Switch to the " +           "developer kit's 8/6GB.\nIt’s doesn’t improve stability or performance and is intended " +           "to let big texture mods fit in emulated RAM.\nEnabling it will increase memory " +           "use. It is not recommended to enable unless a specific game with a texture mod needs " +           "it."));      INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral()); -    INSERT(Settings, speed_limit, tr("Limit Speed Percent"), QStringLiteral()); +    INSERT(Settings, speed_limit, tr("Limit Speed Percent"), +           tr("Controls the game's maximum rendering speed, but it’s up to each game if it runs " +              "faster or not.\n200% for a 30 FPS game is 60 FPS, and for a " +              "60 FPS game it will be 120 FPS.\nDisabling it means unlocking the framerate to the " +              "maximum your PC can reach."));      // Cpu -    INSERT(Settings, cpu_accuracy, tr("Accuracy:"), QStringLiteral()); +    INSERT(Settings, cpu_accuracy, tr("Accuracy:"), +           tr("This setting controls the accuracy of the emulated CPU.\nDon't change this unless " +              "you know what you are doing."));      INSERT(Settings, cpu_backend, tr("Backend:"), QStringLiteral());      // Cpu Debug @@ -80,34 +95,80 @@ 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, vram_usage_mode, tr("VRAM Usage Mode:"), +           tr("Selects whether the emulator should prefer to conserve memory or make maximum usage " +              "of available video memory for performance. Has no effect on integrated graphics. " +              "Aggressive mode may severely impact the performance of other applications such as " +              "recording software."));      INSERT(          Settings, vsync_mode, tr("VSync Mode:"),          tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " @@ -121,22 +182,29 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {      // Renderer (Advanced Graphics)      INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"), -           QStringLiteral()); +           tr("Slightly improves performance by moving presentation to a separate CPU thread."));      INSERT(          Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"),          tr("Runs work in the background while waiting for graphics commands to keep the GPU from "             "lowering its clock speed.")); -    INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral()); -    INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral()); -    INSERT( -        Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"), -        tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature " -           "is experimental.")); +    INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), +           tr("Controls the quality of texture rendering at oblique angles.\nIt’s a light setting " +              "and safe to set at 16x on most GPUs.")); +    INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), +           tr("GPU emulation accuracy.\nMost games render fine with Normal, but High is still " +              "required for some.\nParticles tend to only render correctly with High " +              "accuracy.\nExtreme should only be used for debugging.\nThis option can " +              "be changed while playing.\nSome games may require booting on high to render " +              "properly.")); +    INSERT(Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"), +           tr("Enables asynchronous shader compilation, which may reduce shader stutter.\nThis " +              "feature " +              "is experimental."));      INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"),             tr("Enables Fast GPU Time. This option will force most games to run at their highest "                "native resolution."));      INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"), -           tr("Enables GPU vendor-specific pipeline cache. This option can improve shader loading " +           tr("Enables GPU vendor-specific pipeline cache.\nThis option can improve shader loading "                "time significantly in cases where the Vulkan driver does not store pipeline cache "                "files internally."));      INSERT( @@ -157,19 +225,27 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {      // Renderer (Debug)      // System -    INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral()); +    INSERT(Settings, rng_seed, tr("RNG Seed"), +           tr("Controls the seed of the random number generator.\nMainly used for speedrunning " +              "purposes."));      INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral()); -    INSERT(Settings, device_name, tr("Device Name"), QStringLiteral()); -    INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), QStringLiteral()); +    INSERT(Settings, device_name, tr("Device Name"), tr("The name of the emulated Switch.")); +    INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), +           tr("This option allows to change the emulated clock of the Switch.\n" +              "Can be used to manipulate time in games."));      INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral());      INSERT(Settings, custom_rtc_offset, QStringLiteral(" "),             QStringLiteral("The number of seconds from the current unix time"));      INSERT(Settings, language_index, tr("Language:"),             tr("Note: this can be overridden when region setting is auto-select")); -    INSERT(Settings, region_index, tr("Region:"), QStringLiteral()); -    INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral()); +    INSERT(Settings, region_index, tr("Region:"), tr("The region of the emulated Switch.")); +    INSERT(Settings, time_zone_index, tr("Time Zone:"), +           tr("The time zone of the emulated Switch."));      INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral()); -    INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral()); +    INSERT(Settings, use_docked_mode, tr("Console Mode:"), +           tr("Selects if the console is emulated in Docked or Handheld mode.\nGames will change " +              "their resolution, details and supported controllers and depending on this setting.\n" +              "Setting to Handheld can help improve performance for low end systems."));      INSERT(Settings, current_user, QStringLiteral(), QStringLiteral());      // Controls @@ -187,14 +263,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()); @@ -239,6 +320,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {               PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),               PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),           }}); +    translations->insert({Settings::EnumMetadata<Settings::VramUsageMode>::Index(), +                          { +                              PAIR(VramUsageMode, Conservative, tr("Conservative")), +                              PAIR(VramUsageMode, Aggressive, tr("Aggressive")), +                          }});      translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),                            {  #ifdef HAS_OPENGL 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();  | 
