Compare commits

..

62 Commits

Author SHA1 Message Date
8fbc6871e4 Android 211 2024-01-26 01:00:28 +00:00
4895743f02 Merge yuzu-emu#12769 2024-01-26 01:00:28 +00:00
c2d6ad06e7 Merge yuzu-emu#12749 2024-01-26 01:00:28 +00:00
e04368ad7c Merge pull request #12759 from liamwhite/mp-misc
core: miscellaneous fixes
2024-01-25 16:21:38 -05:00
3e2d3548f2 Merge pull request #12777 from t895/firmware-warning
android: Add key warning
2024-01-25 16:21:29 -05:00
eb9036d75b Merge pull request #12783 from liamwhite/cmif-generation
service: add template serializer for method calls
2024-01-25 15:40:09 -05:00
01a2d978eb service: add template serializer for method calls 2024-01-25 14:35:51 -05:00
6e67b25af9 Merge pull request #12787 from t895/game-list-refresh
android: Only compare game contents for GameAdapter
2024-01-25 14:19:32 -05:00
e91667ba75 Merge pull request #12786 from t895/driver-overlay
android: Show driver vendor in FPS overlay
2024-01-25 14:19:25 -05:00
d45561ace0 Merge pull request #12499 from Kelebek1/time
Rework time services
2024-01-25 14:19:01 -05:00
0fdd6e8934 android: Fix waiting for driver install on startup 2024-01-25 13:04:04 -05:00
35794f4f18 android: Add current driver vendor to FPS overlay 2024-01-25 13:04:03 -05:00
b8be8dff69 android: Add key check 2024-01-25 12:58:19 -05:00
bc317a9807 android: Add option to make MessageDialogFragments non-dismissible
Additionally fixes an issue where its viewmodel could hold onto a stale positive action
2024-01-25 12:53:49 -05:00
97ca160b08 frontend_common: Consistently use references
Was swapping between references and pointers for no reason. Just unify them here since each of these utility functions will need their parameters to be alive.
2024-01-25 12:53:49 -05:00
1a3fc3724a frontend_common: Remove key rederivation and keep key check 2024-01-25 12:53:48 -05:00
7b01454d5f android: Only compare game contents for GameAdapter 2024-01-25 08:04:59 -05:00
f3749394ac Merge pull request #12781 from goldenx86/dozen
Demote dozen to the bottom of the device list
2024-01-25 03:58:09 -03:00
807f421752 Demote Mesa dozen to the bottom of the device list 2024-01-24 23:36:14 -03:00
e4915fb7d2 Rework time service to fix time passing offline. 2024-01-24 04:26:55 +00:00
a76f6a2775 Merge pull request #12763 from liamwhite/fix-hbl-again
loader: also register fs process for raw exefs partition
2024-01-23 13:31:41 -05:00
ba518f6899 Merge pull request #12768 from german77/wrong_conversion
service: properly convert buffers to strings
2024-01-23 13:31:27 -05:00
fc5d76e6e2 service: properly convert buffers to strings 2024-01-23 10:24:05 -06:00
5f9a45ada9 loader: also register fs process for raw exefs partition 2024-01-23 00:01:38 -05:00
a120f8ff4d nvservices: close map handles on session close 2024-01-22 21:18:52 -05:00
96833cd809 kernel: target invalidate to given process 2024-01-22 21:18:52 -05:00
8649a80071 Merge pull request #12753 from liamwhite/why
device_memory_manager: fix ScratchBuffer indexing
2024-01-22 14:55:07 -05:00
550cadbee4 device_memory_manager: fix ScratchBuffer indexing 2024-01-22 14:07:33 -05:00
8bd10473d6 Merge pull request #12579 from FernandoS27/smmu
Core: Implement Device Mapping & GPU SMMU
2024-01-22 10:55:39 -05:00
8d708b0c79 Merge pull request #12747 from t895/homescreen-widget
android: Add to launcher button
2024-01-22 10:55:25 -05:00
beaab10c8f android: Add to launcher button 2024-01-22 08:19:20 -05:00
889c5d2705 Merge pull request #12739 from t895/debug-keystore
android: Provide debug.keystore for debug and relWithDebInfo builds
2024-01-22 04:34:17 -05:00
17b0aac809 Merge pull request #12738 from t895/lock-drawer
android: Port "Lock drawer" feature from Citra
2024-01-22 04:34:08 -05:00
399220ddbc Merge pull request #12736 from t895/verify-contents
android: Add verify contents buttons
2024-01-22 04:33:56 -05:00
59080a3d1d android: Provide debug.keystore for debug and relWithDebInfo builds
Allows devs to share debug builds with testers without uninstalling the previous build
2024-01-21 22:08:07 -05:00
3a25a217e6 android: Port "Lock drawer" feature from Citra 2024-01-21 20:47:28 -05:00
961b5586a5 frontend_common: Remove default value for installer callbacks
We never used these without callbacks and these will break without them in their current state. I could write the default value to return false always but that's unnecessary for now.
2024-01-21 19:31:26 -05:00
dd36d43ea1 android: Add options to verify installed content 2024-01-21 19:15:11 -05:00
c725f3c86c frontend_common: Move integrity verification to content_manager 2024-01-21 16:36:37 -05:00
748465f5a5 device_memory_manager: use unique_lock for update 2024-01-18 21:12:30 -05:00
04867e2456 nvhost_vic: use map erase by key 2024-01-18 21:12:30 -05:00
32f623e029 nvdrv: clean up preallocation 2024-01-18 21:12:30 -05:00
b6c6534c30 nvdrv: use correct names for interface factory 2024-01-18 21:12:30 -05:00
beb438bb0b nvdrv: use static typing for SessionId, smmu Asid types 2024-01-18 21:12:30 -05:00
4b963ca8a5 Core: Invert guest memory depandancy 2024-01-18 21:12:30 -05:00
648ed55fe6 Core: Make sure GPU Dirty Managers ae shared by all processes. 2024-01-18 21:12:30 -05:00
23430e6772 Core: Eliminate core/memory dependancies. 2024-01-18 21:12:30 -05:00
0672847330 SMMU: Fix Right Shift UB. 2024-01-18 21:12:30 -05:00
a874ab0133 SMMU: Fix 8Gb layout. 2024-01-18 21:12:30 -05:00
590d9b7e1d Core: Clang format and other small issues. 2024-01-18 21:12:30 -05:00
b0bca0f8b0 SMMU: Fix software rendering and cleanup 2024-01-18 21:12:30 -05:00
d8f1ce2f76 SMMU: Add continuity tracking optimization. 2024-01-18 21:12:30 -05:00
9b11b9dce5 SMMU: Simplify and remove old code. 2024-01-18 21:12:30 -05:00
303cd31162 SMMU: Add Android compatibility 2024-01-18 21:12:30 -05:00
0adc09e0af GPU-SMMU: Estimate game leak and preallocate device region. 2024-01-18 21:12:30 -05:00
96fd1348ae GPU SMMU: Expand to 34 bits 2024-01-18 21:12:30 -05:00
bad705f245 SMMU: Fix Unregister on MultiAddress 2024-01-18 21:12:30 -05:00
34a8d0cc8e SMMU: Implement physical memory mirroring 2024-01-18 21:12:30 -05:00
0a2536a0df SMMU: Initial adaptation to video_core. 2024-01-18 21:12:30 -05:00
c85d7ccd79 SMMU: Implement backing CPU page protect/unprotect 2024-01-18 21:12:30 -05:00
7a9d1ad2f8 NVDRV: Implement sessions and initial implementation of SMMU 2024-01-18 21:12:30 -05:00
2f0418c101 Core: Initial implementation of device memory mapping 2024-01-18 21:12:30 -05:00
73 changed files with 1442 additions and 719 deletions

View File

@ -155,3 +155,7 @@ License: MIT
Files: externals/gamemode/*
Copyright: Copyright 2017-2019 Feral Interactive
License: BSD-3-Clause
Files: src/android/app/debug.keystore
Copyright: 2023 yuzu Emulator Project
License: GPL-3.0-or-later

View File

@ -1,8 +1,7 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [12499](https://github.com/yuzu-emu/yuzu-android//pull/12499) | [`ac964a067`](https://github.com/yuzu-emu/yuzu-android//pull/12499/files) | Rework time services | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [12579](https://github.com/yuzu-emu/yuzu-android//pull/12579) | [`748465f5a`](https://github.com/yuzu-emu/yuzu-android//pull/12579/files) | Core: Implement Device Mapping & GPU SMMU | [FernandoS27](https://github.com/FernandoS27/) | Yes |
| [12736](https://github.com/yuzu-emu/yuzu-android//pull/12736) | [`961b5586a`](https://github.com/yuzu-emu/yuzu-android//pull/12736/files) | android: Add verify contents buttons | [t895](https://github.com/t895/) | Yes |
| [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`e3171486d`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12769](https://github.com/yuzu-emu/yuzu-android//pull/12769) | [`ad4622da2`](https://github.com/yuzu-emu/yuzu-android//pull/12769/files) | core: hid: Reduce controller requests | [german77](https://github.com/german77/) | Yes |
End of merge log. You can find the original README.md below the break.

View File

@ -82,8 +82,8 @@ android {
}
val keystoreFile = System.getenv("ANDROID_KEYSTORE_FILE")
if (keystoreFile != null) {
signingConfigs {
signingConfigs {
if (keystoreFile != null) {
create("release") {
storeFile = file(keystoreFile)
storePassword = System.getenv("ANDROID_KEYSTORE_PASS")
@ -91,6 +91,12 @@ android {
keyPassword = System.getenv("ANDROID_KEYSTORE_PASS")
}
}
create("default") {
storeFile = file("$projectDir/debug.keystore")
storePassword = "android"
keyAlias = "androiddebugkey"
keyPassword = "android"
}
}
// Define build types, which are orthogonal to product flavors.
@ -101,7 +107,7 @@ android {
signingConfig = if (keystoreFile != null) {
signingConfigs.getByName("release")
} else {
signingConfigs.getByName("debug")
signingConfigs.getByName("default")
}
resValue("string", "app_name_suffixed", "yuzu")
@ -118,7 +124,7 @@ android {
register("relWithDebInfo") {
isDefault = true
resValue("string", "app_name_suffixed", "yuzu Debug Release")
signingConfig = signingConfigs.getByName("debug")
signingConfig = signingConfigs.getByName("default")
isMinifyEnabled = true
isDebuggable = true
proguardFiles(
@ -133,6 +139,7 @@ android {
// Signed by debug key disallowing distribution on Play Store.
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
debug {
signingConfig = signingConfigs.getByName("default")
resValue("string", "app_name_suffixed", "yuzu Debug")
isDebuggable = true
isJniDebuggable = true

Binary file not shown.

View File

@ -303,6 +303,11 @@ object NativeLibrary {
*/
external fun getCpuBackend(): String
/**
* Returns the current GPU Driver.
*/
external fun getGpuDriver(): String
external fun applySettings()
external fun logSettings()
@ -614,6 +619,11 @@ object NativeLibrary {
*/
external fun clearFilesystemProvider()
/**
* Checks if all necessary keys are present for decryption
*/
external fun areKeysPresent(): Boolean
/**
* Button type for use in onTouchEvent
*/

View File

@ -14,15 +14,20 @@ import androidx.recyclerview.widget.RecyclerView
* Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
* code used in every [RecyclerView].
* Type assigned to [Model] must inherit from [Object] in order to be compared properly.
* @param exact Decides whether each item will be compared by reference or by their contents
*/
abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>> :
ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>()).build()) {
abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>>(
exact: Boolean = true
) : ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>(exact)).build()) {
override fun onBindViewHolder(holder: Holder, position: Int) =
holder.bind(currentList[position])
private class DiffCallback<Model> : DiffUtil.ItemCallback<Model>() {
private class DiffCallback<Model>(val exact: Boolean) : DiffUtil.ItemCallback<Model>() {
override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
return oldItem === newItem
if (exact) {
return oldItem === newItem
}
return oldItem == newItem
}
@SuppressLint("DiffUtilEquals")

View File

@ -3,9 +3,6 @@
package org.yuzu.yuzu_emu.adapters
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.LayerDrawable
import android.net.Uri
import android.text.TextUtils
import android.view.LayoutInflater
@ -15,10 +12,6 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
@ -30,7 +23,6 @@ import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
@ -38,7 +30,7 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GameAdapter(private val activity: AppCompatActivity) :
AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() {
AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>(exact = false) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return GameViewHolder(it) }
@ -89,36 +81,13 @@ class GameAdapter(private val activity: AppCompatActivity) :
)
.apply()
val openIntent =
Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = Uri.parse(game.path)
}
activity.lifecycleScope.launch {
withContext(Dispatchers.IO) {
val layerDrawable = ResourcesCompat.getDrawable(
YuzuApplication.appContext.resources,
R.drawable.shortcut,
null
) as LayerDrawable
layerDrawable.setDrawableByLayerId(
R.id.shortcut_foreground,
GameIconUtils.getGameIcon(activity, game)
.toDrawable(YuzuApplication.appContext.resources)
)
val inset = YuzuApplication.appContext.resources
.getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut =
ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path)
.setShortLabel(game.title)
.setIcon(
IconCompat.createWithAdaptiveBitmap(
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
)
)
.setIntent(openIntent)
.setIcon(GameIconUtils.getShortcutIcon(activity, game))
.setIntent(game.launchIntent)
.build()
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
}

View File

@ -23,7 +23,8 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
THEME("theme"),
THEME_MODE("theme_mode"),
OVERLAY_SCALE("control_scale"),
OVERLAY_OPACITY("control_opacity");
OVERLAY_OPACITY("control_opacity"),
LOCK_DRAWER("lock_drawer");
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)

View File

@ -38,7 +38,6 @@ 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.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
@ -141,7 +140,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// So this fragment doesn't restart on configuration changes; i.e. rotation.
retainInstance = true
emulationState = EmulationState(game.path)
emulationState = EmulationState(game.path) {
return@EmulationState driverViewModel.isInteractionAllowed.value
}
}
/**
@ -182,11 +183,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
override fun onDrawerOpened(drawerView: View) {
// No op
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
}
override fun onDrawerClosed(drawerView: View) {
// No op
binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
}
override fun onDrawerStateChanged(newState: Int) {
@ -196,6 +197,28 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
game.title
binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply {
val lockMode = IntSetting.LOCK_DRAWER.getInt()
val titleId = if (lockMode == DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
R.string.unlock_drawer
} else {
R.string.lock_drawer
}
val iconId = if (lockMode == DrawerLayout.LOCK_MODE_UNLOCKED) {
R.drawable.ic_unlock
} else {
R.drawable.ic_lock
}
title = getString(titleId)
icon = ResourcesCompat.getDrawable(
resources,
iconId,
requireContext().theme
)
}
binding.inGameMenu.setNavigationItemSelectedListener {
when (it.itemId) {
R.id.menu_pause_emulation -> {
@ -242,6 +265,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true
}
R.id.menu_lock_drawer -> {
when (IntSetting.LOCK_DRAWER.getInt()) {
DrawerLayout.LOCK_MODE_UNLOCKED -> {
IntSetting.LOCK_DRAWER.setInt(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
it.title = resources.getString(R.string.unlock_drawer)
it.icon = ResourcesCompat.getDrawable(
resources,
R.drawable.ic_lock,
requireContext().theme
)
}
DrawerLayout.LOCK_MODE_LOCKED_CLOSED -> {
IntSetting.LOCK_DRAWER.setInt(DrawerLayout.LOCK_MODE_UNLOCKED)
it.title = resources.getString(R.string.lock_drawer)
it.icon = ResourcesCompat.getDrawable(
resources,
R.drawable.ic_unlock,
requireContext().theme
)
}
}
NativeConfig.saveGlobalConfig()
true
}
R.id.menu_exit -> {
emulationState.stop()
emulationViewModel.setIsEmulationStopping(true)
@ -322,11 +371,20 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isInteractionAllowed.collect {
if (it) {
startEmulation()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.emulationStarted.collectLatest {
if (it) {
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
ViewUtils.showView(binding.surfaceInputOverlay)
ViewUtils.hideView(binding.loadingIndicator)
@ -350,19 +408,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isInteractionAllowed.collect {
if (it) {
onEmulationStart()
}
}
}
}
}
}
private fun onEmulationStart() {
private fun startEmulation() {
if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
@ -437,12 +486,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val FRAMETIME = 2
val SPEED = 3
perfStatsUpdater = {
if (emulationViewModel.emulationStarted.value) {
if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
) {
val perfStats = NativeLibrary.getPerfStats()
val cpuBackend = NativeLibrary.getCpuBackend()
val gpuDriver = NativeLibrary.getGpuDriver()
if (_binding != null) {
binding.showFpsText.text =
String.format("FPS: %.1f\n%s", perfStats[FPS], cpuBackend)
String.format("FPS: %.1f\n%s/%s", perfStats[FPS], cpuBackend, gpuDriver)
}
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
}
@ -759,7 +811,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
private class EmulationState(private val gamePath: String) {
private class EmulationState(
private val gamePath: String,
private val emulationCanStart: () -> Boolean
) {
private var state: State
private var surface: Surface? = null
@ -853,6 +908,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
State.PAUSED -> Log.warning(
"[EmulationFragment] Surface cleared while emulation paused."
)
else -> Log.warning(
"[EmulationFragment] Surface cleared while emulation stopped."
)
@ -862,6 +918,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private fun runWithValidSurface() {
NativeLibrary.surfaceChanged(surface)
if (!emulationCanStart.invoke()) {
return
}
when (state) {
State.STOPPED -> {
val emulationThread = Thread({

View File

@ -4,6 +4,8 @@
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
@ -84,6 +86,24 @@ class GamePropertiesFragment : Fragment() {
view.findNavController().popBackStack()
}
val shortcutManager = requireActivity().getSystemService(ShortcutManager::class.java)
binding.buttonShortcut.isEnabled = shortcutManager.isRequestPinShortcutSupported
binding.buttonShortcut.setOnClickListener {
viewLifecycleOwner.lifecycleScope.launch {
withContext(Dispatchers.IO) {
val shortcut = ShortcutInfo.Builder(requireContext(), args.game.title)
.setShortLabel(args.game.title)
.setIcon(
GameIconUtils.getShortcutIcon(requireActivity(), args.game)
.toIcon(requireContext())
)
.setIntent(args.game.launchIntent)
.build()
shortcutManager.requestPinShortcut(shortcut, null)
}
}
}
GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen)
binding.title.text = args.game.title
binding.title.postDelayed(

View File

@ -26,9 +26,15 @@ class MessageDialogFragment : DialogFragment() {
val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!!
val helpLinkId = requireArguments().getInt(HELP_LINK)
val dismissible = requireArguments().getBoolean(DISMISSIBLE)
val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION)
val builder = MaterialAlertDialogBuilder(requireContext())
if (clearPositiveAction) {
messageDialogViewModel.positiveAction = null
}
if (messageDialogViewModel.positiveAction == null) {
builder.setPositiveButton(R.string.close, null)
} else {
@ -51,6 +57,8 @@ class MessageDialogFragment : DialogFragment() {
}
}
isCancelable = dismissible
return builder.show()
}
@ -67,6 +75,8 @@ class MessageDialogFragment : DialogFragment() {
private const val DESCRIPTION_ID = "DescriptionId"
private const val DESCRIPTION_STRING = "DescriptionString"
private const val HELP_LINK = "Link"
private const val DISMISSIBLE = "Dismissible"
private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction"
fun newInstance(
activity: FragmentActivity? = null,
@ -75,22 +85,28 @@ class MessageDialogFragment : DialogFragment() {
descriptionId: Int = 0,
descriptionString: String = "",
helpLinkId: Int = 0,
dismissible: Boolean = true,
positiveAction: (() -> Unit)? = null
): MessageDialogFragment {
val dialog = MessageDialogFragment()
val bundle = Bundle()
bundle.apply {
putInt(TITLE_ID, titleId)
putString(TITLE_STRING, titleString)
putInt(DESCRIPTION_ID, descriptionId)
putString(DESCRIPTION_STRING, descriptionString)
putInt(HELP_LINK, helpLinkId)
}
var clearPositiveAction = false
if (activity != null) {
ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
clear()
this.positiveAction = positiveAction
}
} else {
clearPositiveAction = true
}
val dialog = MessageDialogFragment()
val bundle = Bundle().apply {
putInt(TITLE_ID, titleId)
putString(TITLE_STRING, titleString)
putInt(DESCRIPTION_ID, descriptionId)
putString(DESCRIPTION_STRING, descriptionString)
putInt(HELP_LINK, helpLinkId)
putBoolean(DISMISSIBLE, dismissible)
putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction)
}
dialog.arguments = bundle
return dialog

View File

@ -31,6 +31,7 @@ import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.google.android.material.transition.MaterialFadeThrough
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.File
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@ -162,7 +163,7 @@ class SetupFragment : Fragment() {
R.string.install_prod_keys_warning_help,
{
val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
if (file.exists()) {
if (file.exists() && NativeLibrary.areKeysPresent()) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
@ -347,7 +348,8 @@ class SetupFragment : Fragment() {
val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
if (mainActivity.processKey(result)) {
mainActivity.processKey(result)
if (NativeLibrary.areKeysPresent()) {
keyCallback.onStepCompleted()
}
}

View File

@ -144,6 +144,7 @@ class DriverViewModel : ViewModel() {
val selectedDriverFile = File(StringSetting.DRIVER_PATH.getString())
val selectedDriverMetadata = GpuDriverHelper.customDriverSettingData
if (GpuDriverHelper.installedCustomDriverData == selectedDriverMetadata) {
setDriverReady()
return
}

View File

@ -3,6 +3,7 @@
package org.yuzu.yuzu_emu.model
import android.content.Intent
import android.net.Uri
import android.os.Parcelable
import java.util.HashSet
@ -11,6 +12,7 @@ import kotlinx.serialization.Serializable
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import java.time.LocalDateTime
@ -61,12 +63,26 @@ class Game(
val addonDir: String
get() = DirectoryInitialization.userDirectory + "/load/" + programIdHex + "/"
override fun equals(other: Any?): Boolean {
if (other !is Game) {
return false
val launchIntent: Intent
get() = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = Uri.parse(path)
}
return hashCode() == other.hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Game
if (title != other.title) return false
if (path != other.path) return false
if (programId != other.programId) return false
if (developer != other.developer) return false
if (version != other.version) return false
if (isHomebrew != other.isHomebrew) return false
return true
}
override fun hashCode(): Int {

View File

@ -31,6 +31,9 @@ class HomeViewModel : ViewModel() {
private val _reloadPropertiesList = MutableStateFlow(false)
val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
private val _checkKeys = MutableStateFlow(false)
val checkKeys = _checkKeys.asStateFlow()
var navigatedToSetup = false
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
@ -66,4 +69,8 @@ class HomeViewModel : ViewModel() {
fun reloadPropertiesList(reload: Boolean) {
_reloadPropertiesList.value = reload
}
fun setCheckKeys(value: Boolean) {
_checkKeys.value = value
}
}

View File

@ -64,6 +64,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
override var themeId: Int = 0
private val CHECKED_DECRYPTION = "CheckedDecryption"
private var checkedDecryption = false
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
@ -75,6 +78,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
if (savedInstanceState != null) {
checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION)
}
if (!checkedDecryption) {
val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext)
.getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true)
if (!firstTimeSetup) {
checkKeys()
}
checkedDecryption = true
}
WindowCompat.setDecorFitsSystemWindows(window, false)
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
@ -150,6 +165,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.checkKeys.collect {
if (it) {
checkKeys()
homeViewModel.setCheckKeys(false)
}
}
}
}
}
// Dismiss previous notifications (should not happen unless a crash occurred)
@ -158,6 +183,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
setInsets()
}
private fun checkKeys() {
if (!NativeLibrary.areKeysPresent()) {
MessageDialogFragment.newInstance(
titleId = R.string.keys_missing,
descriptionId = R.string.keys_missing_description,
helpLinkId = R.string.keys_missing_help
).show(supportFragmentManager, MessageDialogFragment.TAG)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(CHECKED_DECRYPTION, checkedDecryption)
}
fun finishSetup(navController: NavController) {
navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment)
(binding.navigationView as NavigationBarView).setupWithNavController(navController)
@ -349,6 +389,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
R.string.install_keys_success,
Toast.LENGTH_SHORT
).show()
homeViewModel.setCheckKeys(true)
gamesViewModel.reloadGames(true)
return true
} else {
@ -399,6 +440,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
firmwarePath.deleteRecursively()
cacheFirmwareDir.copyRecursively(firmwarePath, true)
NativeLibrary.initializeSystem(true)
homeViewModel.setCheckKeys(true)
getString(R.string.save_file_imported_success)
}
} catch (e: Exception) {

View File

@ -5,7 +5,10 @@ package org.yuzu.yuzu_emu.utils
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.LayerDrawable
import android.widget.ImageView
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable
import androidx.lifecycle.LifecycleOwner
@ -85,4 +88,22 @@ object GameIconUtils {
return imageLoader.execute(request)
.drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888)
}
suspend fun getShortcutIcon(lifecycleOwner: LifecycleOwner, game: Game): IconCompat {
val layerDrawable = ResourcesCompat.getDrawable(
YuzuApplication.appContext.resources,
R.drawable.shortcut,
null
) as LayerDrawable
layerDrawable.setDrawableByLayerId(
R.id.shortcut_foreground,
getGameIcon(lifecycleOwner, game).toDrawable(YuzuApplication.appContext.resources)
)
val inset = YuzuApplication.appContext.resources
.getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
return IconCompat.createWithAdaptiveBitmap(
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
)
}
}

View File

@ -63,6 +63,7 @@ struct Values {
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};
Settings::Setting<s32> lock_drawer{linkage, false, "lock_drawer", Settings::Category::Overlay};
};
extern Values values;

View File

@ -247,6 +247,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
m_system.GetCpuManager().OnGpuReady();
m_system.RegisterExitCallback([&] { HaltEmulation(); });
OnEmulationStarted();
return Core::SystemResultStatus::Success;
}
@ -463,8 +464,8 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
};
return static_cast<int>(
ContentManager::InstallNSP(&EmulationSession::GetInstance().System(),
EmulationSession::GetInstance().System().GetFilesystem().get(),
ContentManager::InstallNSP(EmulationSession::GetInstance().System(),
*EmulationSession::GetInstance().System().GetFilesystem(),
GetJString(env, j_file), callback));
}
@ -674,6 +675,11 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass
return ToJString(env, "JIT");
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) {
return ToJString(env,
EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor());
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
EmulationSession::GetInstance().System().ApplySettings();
}
@ -819,7 +825,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject job
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
jstring jprogramId) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
ContentManager::RemoveAllDLC(&EmulationSession::GetInstance().System(), program_id);
ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id);
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
@ -829,8 +835,9 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj,
program_id, GetJString(env, jname));
}
jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj,
jobject jcallback) {
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env,
jobject jobj,
jobject jcallback) {
auto jlambdaClass = env->GetObjectClass(jcallback);
auto jlambdaInvokeMethod = env->GetMethodID(
jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
@ -842,7 +849,7 @@ jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* en
auto& session = EmulationSession::GetInstance();
std::vector<std::string> result = ContentManager::VerifyInstalledContents(
&session.System(), session.GetContentProvider(), callback);
session.System(), *session.GetContentProvider(), callback);
jobjectArray jresult =
env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, ""));
for (size_t i = 0; i < result.size(); ++i) {
@ -863,7 +870,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje
};
auto& session = EmulationSession::GetInstance();
return static_cast<jint>(
ContentManager::VerifyGameContents(&session.System(), GetJString(env, jpath), callback));
ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback));
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
@ -912,4 +919,10 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env,
EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobject jobj) {
auto& system = EmulationSession::GetInstance().System();
system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
return ContentManager::AreKeysPresent();
}
} // extern "C"

View File

@ -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="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z" />
</vector>

View File

@ -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="M280,920q-33,0 -56.5,-23.5T200,840v-720q0,-33 23.5,-56.5T280,40h400q33,0 56.5,23.5T760,120v160h-80v-40L280,240v480h400v-40h80v160q0,33 -23.5,56.5T680,920L280,920ZM686,520L480,520v120h-80v-120q0,-33 23.5,-56.5T480,440h206l-62,-64 56,-56 160,160 -160,160 -56,-56 62,-64Z" />
</vector>

View File

@ -43,16 +43,35 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/button_back"
style="?attr/materialIconButtonStyle"
android:layout_width="wrap_content"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_margin="8dp"
app:icon="@drawable/ic_back"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />
android:orientation="horizontal">
<Button
android:id="@+id/button_back"
style="?attr/materialIconButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="@drawable/ic_back"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button_shortcut"
style="?attr/materialIconButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="@drawable/ic_shortcut"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewElevatedStyle"

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<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"
@ -22,16 +21,35 @@
android:orientation="vertical"
android:gravity="center_horizontal">
<Button
android:id="@+id/button_back"
style="?attr/materialIconButtonStyle"
android:layout_width="wrap_content"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_gravity="start"
app:icon="@drawable/ic_back"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />
android:orientation="horizontal">
<Button
android:id="@+id/button_back"
style="?attr/materialIconButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="@drawable/ic_back"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button_shortcut"
style="?attr/materialIconButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="@drawable/ic_shortcut"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewElevatedStyle"
@ -45,7 +63,7 @@
android:id="@+id/image_game_screen"
android:layout_width="175dp"
android:layout_height="175dp"
tools:src="@drawable/default_icon"/>
tools:src="@drawable/default_icon" />
</com.google.android.material.card.MaterialCardView>

View File

@ -21,6 +21,11 @@
android:icon="@drawable/ic_controller"
android:title="@string/emulation_input_overlay" />
<item
android:id="@+id/menu_lock_drawer"
android:icon="@drawable/ic_unlock"
android:title="@string/emulation_input_overlay" />
<item
android:id="@+id/menu_exit"
android:icon="@drawable/ic_exit"

View File

@ -144,6 +144,9 @@
<string name="no_save_data_found">No save data found</string>
<string name="verify_installed_content">Verify installed content</string>
<string name="verify_installed_content_description">Checks all installed content for corruption</string>
<string name="keys_missing">Encryption keys are missing</string>
<string name="keys_missing_description">Firmware and retail games cannot be decrypted</string>
<string name="keys_missing_help">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
<!-- Applet launcher strings -->
<string name="applets">Applet launcher</string>
@ -392,6 +395,8 @@
<string name="emulation_unpause">Unpause emulation</string>
<string name="emulation_input_overlay">Overlay options</string>
<string name="touchscreen">Touchscreen</string>
<string name="lock_drawer">Lock drawer</string>
<string name="unlock_drawer">Unlock drawer</string>
<string name="load_settings">Loading settings…</string>

View File

@ -106,6 +106,7 @@ add_library(common STATIC
precompiled_headers.h
quaternion.h
range_map.h
range_mutex.h
reader_writer_queue.h
ring_buffer.h
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp

View File

@ -29,10 +29,6 @@ NativeClock::NativeClock() {
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
}
void NativeClock::Reset() {
start_ticks = GetUptime();
}
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
}
@ -46,11 +42,11 @@ std::chrono::milliseconds NativeClock::GetTimeMS() const {
}
s64 NativeClock::GetCNTPCT() const {
return MultiplyHigh(GetUptime() - start_ticks, guest_cntfrq_factor);
return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
}
s64 NativeClock::GetGPUTick() const {
return MultiplyHigh(GetUptime() - start_ticks, gputick_cntfrq_factor);
return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
}
s64 NativeClock::GetUptime() const {

View File

@ -11,8 +11,6 @@ class NativeClock final : public WallClock {
public:
explicit NativeClock();
void Reset() override;
std::chrono::nanoseconds GetTimeNS() const override;
std::chrono::microseconds GetTimeUS() const override;
@ -42,7 +40,6 @@ private:
FactorType ms_cntfrq_factor;
FactorType guest_cntfrq_factor;
FactorType gputick_cntfrq_factor;
s64 start_ticks;
};
} // namespace Common::Arm64

93
src/common/range_mutex.h Normal file
View File

@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <condition_variable>
#include <mutex>
#include "common/intrusive_list.h"
namespace Common {
class ScopedRangeLock;
class RangeMutex {
public:
explicit RangeMutex() = default;
~RangeMutex() = default;
private:
friend class ScopedRangeLock;
void Lock(ScopedRangeLock& l);
void Unlock(ScopedRangeLock& l);
bool HasIntersectionLocked(ScopedRangeLock& l);
private:
std::mutex m_mutex;
std::condition_variable m_cv;
using LockList = Common::IntrusiveListBaseTraits<ScopedRangeLock>::ListType;
LockList m_list;
};
class ScopedRangeLock : public Common::IntrusiveListBaseNode<ScopedRangeLock> {
public:
explicit ScopedRangeLock(RangeMutex& mutex, u64 address, u64 size)
: m_mutex(mutex), m_address(address), m_size(size) {
if (m_size > 0) {
m_mutex.Lock(*this);
}
}
~ScopedRangeLock() {
if (m_size > 0) {
m_mutex.Unlock(*this);
}
}
u64 GetAddress() const {
return m_address;
}
u64 GetSize() const {
return m_size;
}
private:
RangeMutex& m_mutex;
const u64 m_address{};
const u64 m_size{};
};
inline void RangeMutex::Lock(ScopedRangeLock& l) {
std::unique_lock lk{m_mutex};
m_cv.wait(lk, [&] { return !HasIntersectionLocked(l); });
m_list.push_back(l);
}
inline void RangeMutex::Unlock(ScopedRangeLock& l) {
{
std::scoped_lock lk{m_mutex};
m_list.erase(m_list.iterator_to(l));
}
m_cv.notify_all();
}
inline bool RangeMutex::HasIntersectionLocked(ScopedRangeLock& l) {
const auto cur_begin = l.GetAddress();
const auto cur_last = l.GetAddress() + l.GetSize() - 1;
for (const auto& other : m_list) {
const auto other_begin = other.GetAddress();
const auto other_last = other.GetAddress() + other.GetSize() - 1;
if (cur_begin <= other_last && other_begin <= cur_last) {
return true;
}
}
return false;
}
} // namespace Common

View File

@ -420,8 +420,15 @@ struct Values {
SwitchableSetting<s64> custom_rtc{
linkage, 0, "custom_rtc", Category::System, Specialization::Time,
false, true, &custom_rtc_enabled};
SwitchableSetting<s64, false> custom_rtc_offset{
linkage, 0, "custom_rtc_offset", Category::System, Specialization::Countable, true, true};
SwitchableSetting<s64, true> custom_rtc_offset{linkage,
0,
std::numeric_limits<int>::min(),
std::numeric_limits<int>::max(),
"custom_rtc_offset",
Category::System,
Specialization::Countable,
true,
true};
SwitchableSetting<bool> rng_seed_enabled{
linkage, false, "rng_seed_enabled", Category::System, Specialization::Paired, true, true};
SwitchableSetting<u32> rng_seed{

View File

@ -20,10 +20,6 @@ class StandardWallClock final : public WallClock {
public:
explicit StandardWallClock() {}
void Reset() override {
start_time = std::chrono::system_clock::now();
}
std::chrono::nanoseconds GetTimeNS() const override {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now().time_since_epoch());
@ -49,16 +45,13 @@ public:
s64 GetUptime() const override {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now() - start_time)
std::chrono::steady_clock::now().time_since_epoch())
.count();
}
bool IsNative() const override {
return false;
}
private:
std::chrono::system_clock::time_point start_time{};
};
std::unique_ptr<WallClock> CreateOptimalClock() {

View File

@ -19,8 +19,6 @@ public:
virtual ~WallClock() = default;
virtual void Reset() = 0;
/// @returns The time in nanoseconds since the construction of this clock.
virtual std::chrono::nanoseconds GetTimeNS() const = 0;

View File

@ -15,10 +15,6 @@ NativeClock::NativeClock(u64 rdtsc_frequency_)
cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)},
gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {}
void NativeClock::Reset() {
start_ticks = FencedRDTSC();
}
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
}
@ -32,11 +28,11 @@ std::chrono::milliseconds NativeClock::GetTimeMS() const {
}
s64 NativeClock::GetCNTPCT() const {
return MultiplyHigh(GetUptime() - start_ticks, cntpct_rdtsc_factor);
return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
}
s64 NativeClock::GetGPUTick() const {
return MultiplyHigh(GetUptime() - start_ticks, gputick_rdtsc_factor);
return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
}
s64 NativeClock::GetUptime() const {

View File

@ -11,8 +11,6 @@ class NativeClock final : public WallClock {
public:
explicit NativeClock(u64 rdtsc_frequency_);
void Reset() override;
std::chrono::nanoseconds GetTimeNS() const override;
std::chrono::microseconds GetTimeUS() const override;
@ -28,7 +26,6 @@ public:
bool IsNative() const override;
private:
u64 start_ticks;
u64 rdtsc_frequency;
u64 ns_rdtsc_factor;

View File

@ -472,6 +472,8 @@ add_library(core STATIC
hle/service/caps/caps_types.h
hle/service/caps/caps_u.cpp
hle/service/caps/caps_u.h
hle/service/cmif_serialization.h
hle/service/cmif_types.h
hle/service/erpt/erpt.cpp
hle/service/erpt/erpt.h
hle/service/es/es.cpp

View File

@ -66,7 +66,6 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
event_fifo_id = 0;
shutting_down = false;
cpu_ticks = 0;
clock->Reset();
if (is_multicore) {
timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
}

View File

@ -10,6 +10,7 @@
#include <mutex>
#include "common/common_types.h"
#include "common/range_mutex.h"
#include "common/scratch_buffer.h"
#include "common/virtual_buffer.h"
@ -204,7 +205,7 @@ private:
(1ULL << (device_virtual_bits - page_bits)) / subentries;
using CachedPages = std::array<CounterEntry, num_counter_entries>;
std::unique_ptr<CachedPages> cached_pages;
std::mutex counter_guard;
Common::RangeMutex counter_guard;
std::mutex mapping_guard;
};

View File

@ -31,9 +31,8 @@ public:
buffer.resize(0);
size_t index = 0;
const auto add_value = [&](u32 value) {
buffer[index] = value;
index++;
buffer.resize(index);
buffer.resize(index + 1);
buffer[index++] = value;
};
u32 iter_entry = start_entry;
@ -509,12 +508,7 @@ void DeviceMemoryManager<Traits>::UnregisterProcess(Asid asid) {
template <typename Traits>
void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) {
std::unique_lock<std::mutex> lk(counter_guard, std::defer_lock);
const auto Lock = [&] {
if (!lk) {
lk.lock();
}
};
Common::ScopedRangeLock lk(counter_guard, addr, size);
u64 uncache_begin = 0;
u64 cache_begin = 0;
u64 uncache_bytes = 0;
@ -549,7 +543,6 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
}
uncache_bytes += Memory::YUZU_PAGESIZE;
} else if (uncache_bytes > 0) {
Lock();
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
uncache_bytes, false);
uncache_bytes = 0;
@ -560,7 +553,6 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
}
cache_bytes += Memory::YUZU_PAGESIZE;
} else if (cache_bytes > 0) {
Lock();
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
true);
cache_bytes = 0;
@ -568,12 +560,10 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
vpage++;
}
if (uncache_bytes > 0) {
Lock();
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes,
false);
}
if (cache_bytes > 0) {
Lock();
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
true);
}

View File

@ -67,25 +67,29 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI
}};
VirtualFile SynthesizeSystemArchive(const u64 title_id) {
if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id)
if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) {
return nullptr;
}
const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID];
LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id);
if (desc.supplier == nullptr)
if (desc.supplier == nullptr) {
return nullptr;
}
const auto dir = desc.supplier();
if (dir == nullptr)
if (dir == nullptr) {
return nullptr;
}
const auto romfs = CreateRomFS(dir);
if (romfs == nullptr)
if (romfs == nullptr) {
return nullptr;
}
LOG_INFO(Service_FS, " - System archive generation successful!");
return romfs;

View File

@ -69,9 +69,14 @@ public:
};
template <typename AddressType>
void InvalidateInstructionCache(KernelCore& kernel, AddressType addr, u64 size) {
void InvalidateInstructionCache(KernelCore& kernel, KPageTableBase* table, AddressType addr,
u64 size) {
// TODO: lock the process list
for (auto& process : kernel.GetProcessList()) {
if (std::addressof(process->GetPageTable().GetBasePageTable()) != table) {
continue;
}
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
auto* interface = process->GetArmInterface(i);
if (interface) {
@ -1302,7 +1307,7 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr
bool reprotected_pages = false;
SCOPE_EXIT({
if (reprotected_pages && any_code_pages) {
InvalidateInstructionCache(m_kernel, dst_address, size);
InvalidateInstructionCache(m_kernel, this, dst_address, size);
}
});
@ -2036,7 +2041,7 @@ Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t s
for (const auto& block : pg) {
StoreDataCache(GetHeapVirtualPointer(m_kernel, block.GetAddress()), block.GetSize());
}
InvalidateInstructionCache(m_kernel, addr, size);
InvalidateInstructionCache(m_kernel, this, addr, size);
}
R_SUCCEED();
@ -3277,7 +3282,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
R_TRY(PerformCopy());
// Invalidate the instruction cache, as this svc allows modifying executable pages.
InvalidateInstructionCache(m_kernel, dst_address, size);
InvalidateInstructionCache(m_kernel, this, dst_address, size);
R_SUCCEED();
}

View File

@ -0,0 +1,337 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/div_ceil.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/service.h"
namespace Service {
// clang-format off
struct RequestLayout {
u32 copy_handle_count;
u32 move_handle_count;
u32 cmif_raw_data_size;
u32 domain_interface_count;
};
template <ArgumentType Type1, ArgumentType Type2, typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0>
constexpr u32 GetArgumentRawDataSize() {
if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
return static_cast<u32>(DataOffset);
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == Type1 || ArgumentTraits<ArgType>::Type == Type2) {
constexpr size_t ArgAlign = alignof(ArgType);
constexpr size_t ArgSize = sizeof(ArgType);
static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
return GetArgumentRawDataSize<Type1, Type2, MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>();
} else {
return GetArgumentRawDataSize<Type1, Type2, MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>();
}
}
}
template <ArgumentType DataType, typename MethodArguments, size_t ArgCount = 0, size_t ArgIndex = 0>
constexpr u32 GetArgumentTypeCount() {
if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
return static_cast<u32>(ArgCount);
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == DataType) {
return GetArgumentTypeCount<DataType, MethodArguments, ArgCount + 1, ArgIndex + 1>();
} else {
return GetArgumentTypeCount<DataType, MethodArguments, ArgCount, ArgIndex + 1>();
}
}
}
template <typename MethodArguments>
constexpr RequestLayout GetNonDomainReplyInLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
.move_handle_count = 0,
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(),
.domain_interface_count = 0,
};
}
template <typename MethodArguments>
constexpr RequestLayout GetDomainReplyInLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
.move_handle_count = 0,
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(),
.domain_interface_count = GetArgumentTypeCount<ArgumentType::InInterface, MethodArguments>(),
};
}
template <typename MethodArguments>
constexpr RequestLayout GetNonDomainReplyOutLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
.move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>() + GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(),
.domain_interface_count = 0,
};
}
template <typename MethodArguments>
constexpr RequestLayout GetDomainReplyOutLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
.move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>(),
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(),
.domain_interface_count = GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
};
}
template <bool Domain, typename MethodArguments>
constexpr RequestLayout GetReplyInLayout() {
return Domain ? GetDomainReplyInLayout<MethodArguments>() : GetNonDomainReplyInLayout<MethodArguments>();
}
template <bool Domain, typename MethodArguments>
constexpr RequestLayout GetReplyOutLayout() {
return Domain ? GetDomainReplyOutLayout<MethodArguments>() : GetNonDomainReplyOutLayout<MethodArguments>();
}
using OutTemporaryBuffers = std::array<Common::ScratchBuffer<u8>, 3>;
template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t HandleIndex = 0, size_t InBufferIndex = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
void ReadInArgument(CallArguments& args, const u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
return;
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
constexpr size_t ArgAlign = alignof(ArgType);
constexpr size_t ArgSize = sizeof(ArgType);
static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");
static_assert(!RawDataFinished, "All input interface arguments must appear after raw data");
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
// TODO: abort parsing if PID is not provided?
// TODO: validate against raw data value?
std::get<ArgIndex>(args).pid = ctx.GetPID();
} else {
std::memcpy(&std::get<ArgIndex>(args), raw_data + ArgOffset, ArgSize);
}
return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InInterface) {
constexpr size_t ArgAlign = alignof(u32);
constexpr size_t ArgSize = sizeof(u32);
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
static_assert(Domain);
ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0);
u32 value{};
std::memcpy(&value, raw_data + ArgOffset, ArgSize);
std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1);
return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) {
std::get<ArgIndex>(args) = std::move(ctx.GetObjectFromHandle<typename ArgType::Type>(ctx.GetCopyHandle(HandleIndex)));
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex + 1, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InLargeData) {
constexpr size_t BufferSize = sizeof(ArgType);
// Clear the existing data.
std::memset(&std::get<ArgIndex>(args), 0, BufferSize);
std::span<const u8> buffer{};
ASSERT(ctx.CanReadBuffer(InBufferIndex));
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
buffer = ctx.ReadBuffer(InBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
buffer = ctx.ReadBufferA(InBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
buffer = ctx.ReadBufferX(InBufferIndex);
}
std::memcpy(&std::get<ArgIndex>(args), buffer.data(), std::min(BufferSize, buffer.size()));
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InBuffer) {
using ElementType = typename ArgType::Type;
std::span<const u8> buffer{};
if (ctx.CanReadBuffer(InBufferIndex)) {
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
buffer = ctx.ReadBuffer(InBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
buffer = ctx.ReadBufferA(InBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
buffer = ctx.ReadBufferX(InBufferIndex);
}
}
ElementType* ptr = (ElementType*) buffer.data();
size_t size = buffer.size() / sizeof(ElementType);
std::get<ArgIndex>(args) = std::span(ptr, size);
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
constexpr size_t BufferSize = sizeof(ArgType);
// Clear the existing data.
std::memset(&std::get<ArgIndex>(args), 0, BufferSize);
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
using ElementType = typename ArgType::Type;
// Set up scratch buffer.
auto& buffer = temp[OutBufferIndex];
if (ctx.CanWriteBuffer(OutBufferIndex)) {
buffer.resize_destructive(ctx.GetWriteBufferSize(OutBufferIndex));
} else {
buffer.resize_destructive(0);
}
ElementType* ptr = (ElementType*) buffer.data();
size_t size = buffer.size() / sizeof(ElementType);
std::get<ArgIndex>(args) = std::span(ptr, size);
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else {
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
}
}
}
template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
void WriteOutArgument(CallArguments& args, u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
return;
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) {
constexpr size_t ArgAlign = alignof(ArgType);
constexpr size_t ArgSize = sizeof(ArgType);
static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment");
static_assert(!RawDataFinished, "All output interface arguments must appear after raw data");
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
std::memcpy(raw_data + ArgOffset, &std::get<ArgIndex>(args), ArgSize);
return WriteOutArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutInterface) {
if constexpr (Domain) {
ctx.AddDomainObject(std::get<ArgIndex>(args));
} else {
ctx.AddMoveInterface(std::get<ArgIndex>(args));
}
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutCopyHandle) {
ctx.AddCopyObject(std::get<ArgIndex>(args).GetPointerUnsafe());
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutMoveHandle) {
ctx.AddMoveObject(std::get<ArgIndex>(args).GetPointerUnsafe());
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
constexpr size_t BufferSize = sizeof(ArgType);
ASSERT(ctx.CanWriteBuffer(OutBufferIndex));
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
ctx.WriteBuffer(std::get<ArgIndex>(args), OutBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
ctx.WriteBufferB(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
ctx.WriteBufferC(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
}
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
auto& buffer = temp[OutBufferIndex];
const size_t size = buffer.size();
if (ctx.CanWriteBuffer(OutBufferIndex)) {
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
ctx.WriteBuffer(buffer.data(), size, OutBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
ctx.WriteBufferB(buffer.data(), size, OutBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
ctx.WriteBufferC(buffer.data(), size, OutBufferIndex);
}
}
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>( args, raw_data, ctx, temp);
} else {
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
}
}
}
template <bool Domain, typename T, typename... A>
void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
// Verify domain state.
if constexpr (Domain) {
ASSERT_MSG(ctx.GetManager()->IsDomain(), "Domain reply used on non-domain session");
} else {
ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Non-domain reply used on domain session");
}
using MethodArguments = std::tuple<std::remove_reference_t<A>...>;
OutTemporaryBuffers buffers{};
auto call_arguments = std::tuple<typename RemoveOut<A>::Type...>();
// Read inputs.
const size_t offset_plus_command_id = ctx.GetDataPayloadOffset() + 2;
ReadInArgument<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + offset_plus_command_id), ctx, buffers);
// Call.
const auto Callable = [&]<typename... CallArgs>(CallArgs&... args) {
return (t.*f)(args...);
};
const Result res = std::apply(Callable, call_arguments);
// Write result.
constexpr RequestLayout layout = GetReplyOutLayout<Domain, MethodArguments>();
IPC::ResponseBuilder rb{ctx, 2 + Common::DivCeil(layout.cmif_raw_data_size, sizeof(u32)), layout.copy_handle_count, layout.move_handle_count + layout.domain_interface_count};
rb.Push(res);
// Write out arguments.
WriteOutArgument<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + rb.GetCurrentOffset()), ctx, buffers);
}
// clang-format on
template <typename Self>
template <bool Domain, auto F>
inline void ServiceFramework<Self>::CmifReplyWrap(HLERequestContext& ctx) {
return CmifReplyWrapImpl<Domain>(ctx, *static_cast<Self*>(this), F);
}
} // namespace Service

View File

@ -0,0 +1,234 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/hle_ipc.h"
namespace Service {
// clang-format off
template <typename T>
class Out {
public:
/* implicit */ Out(T& t) : raw(&t) {}
~Out() = default;
T* Get() const {
return raw;
}
T& operator*() {
return *raw;
}
private:
T* raw;
};
template <typename T>
using SharedPointer = std::shared_ptr<T>;
struct ClientProcessId {
explicit operator bool() const {
return pid != 0;
}
const u64& operator*() const {
return pid;
}
u64 pid;
};
using ClientAppletResourceUserId = ClientProcessId;
template <typename T>
class InCopyHandle : public Kernel::KScopedAutoObject<T> {
public:
using Type = T;
template <typename... Args>
/* implicit */ InCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
~InCopyHandle() = default;
InCopyHandle& operator=(InCopyHandle&& rhs) {
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
return *this;
}
};
template <typename T>
class OutCopyHandle : public Kernel::KScopedAutoObject<T> {
public:
using Type = T;
template <typename... Args>
/* implicit */ OutCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
~OutCopyHandle() = default;
OutCopyHandle& operator=(OutCopyHandle&& rhs) {
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
return *this;
}
};
template <typename T>
class OutMoveHandle : public Kernel::KScopedAutoObject<T> {
public:
using Type = T;
template <typename... Args>
/* implicit */ OutMoveHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
~OutMoveHandle() = default;
OutMoveHandle& operator=(OutMoveHandle&& rhs) {
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
return *this;
}
};
enum BufferAttr : int {
BufferAttr_In = (1U << 0),
BufferAttr_Out = (1U << 1),
BufferAttr_HipcMapAlias = (1U << 2),
BufferAttr_HipcPointer = (1U << 3),
BufferAttr_FixedSize = (1U << 4),
BufferAttr_HipcAutoSelect = (1U << 5),
BufferAttr_HipcMapTransferAllowsNonSecure = (1U << 6),
BufferAttr_HipcMapTransferAllowsNonDevice = (1U << 7),
};
template <typename T, int A>
struct Buffer : public std::span<T> {
static_assert(std::is_trivial_v<T>, "Buffer type must be trivial");
static_assert((A & BufferAttr_FixedSize) == 0, "Buffer attr must not contain FixedSize");
static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "Buffer attr must be In or Out");
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A);
using Type = T;
Buffer& operator=(const std::span<T>& rhs) {
std::span<T>::operator=(rhs);
return *this;
}
T& operator*() const {
return *this->data();
}
explicit operator bool() const {
return this->size() > 0;
}
};
template <BufferAttr A>
using InBuffer = Buffer<const u8, BufferAttr_In | A>;
template <typename T, BufferAttr A>
using InArray = Buffer<T, BufferAttr_In | A>;
template <BufferAttr A>
using OutBuffer = Buffer<u8, BufferAttr_Out | A>;
template <typename T, BufferAttr A>
using OutArray = Buffer<T, BufferAttr_Out | A>;
template <typename T, int A>
struct LargeData : public T {
static_assert(std::is_trivial_v<T>, "LargeData type must be trivial");
static_assert((A & BufferAttr_FixedSize) != 0, "LargeData attr must contain FixedSize");
static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "LargeData attr must be In or Out");
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A);
using Type = T;
};
template <typename T, BufferAttr A>
using InLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_In | A>;
template <typename T, BufferAttr A>
using OutLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_Out | A>;
template <typename T>
struct RemoveOut {
using Type = std::remove_reference_t<T>;
};
template <typename T>
struct RemoveOut<Out<T>> {
using Type = T;
};
enum class ArgumentType {
InProcessId,
InData,
InInterface,
InCopyHandle,
OutData,
OutInterface,
OutCopyHandle,
OutMoveHandle,
InBuffer,
InLargeData,
OutBuffer,
OutLargeData,
};
template <typename T>
struct ArgumentTraits;
template <>
struct ArgumentTraits<ClientProcessId> {
static constexpr ArgumentType Type = ArgumentType::InProcessId;
};
template <typename T>
struct ArgumentTraits<SharedPointer<T>> {
static constexpr ArgumentType Type = ArgumentType::InInterface;
};
template <typename T>
struct ArgumentTraits<InCopyHandle<T>> {
static constexpr ArgumentType Type = ArgumentType::InCopyHandle;
};
template <typename T>
struct ArgumentTraits<Out<SharedPointer<T>>> {
static constexpr ArgumentType Type = ArgumentType::OutInterface;
};
template <typename T>
struct ArgumentTraits<Out<T>> {
static constexpr ArgumentType Type = ArgumentType::OutData;
};
template <typename T>
struct ArgumentTraits<OutCopyHandle<T>> {
static constexpr ArgumentType Type = ArgumentType::OutCopyHandle;
};
template <typename T>
struct ArgumentTraits<OutMoveHandle<T>> {
static constexpr ArgumentType Type = ArgumentType::OutMoveHandle;
};
template <typename T, int A>
struct ArgumentTraits<Buffer<T, A>> {
static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutBuffer : ArgumentType::InBuffer;
};
template <typename T, int A>
struct ArgumentTraits<LargeData<T, A>> {
static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutLargeData : ArgumentType::InLargeData;
};
template <typename T>
struct ArgumentTraits {
static constexpr ArgumentType Type = ArgumentType::InData;
};
// clang-format on
} // namespace Service

View File

@ -89,7 +89,8 @@ Service::PSC::Time::LocationName GetTimeZoneString(Service::PSC::Time::LocationN
std::min(configured_name.name.size(), configured_zone.size()));
}
ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone!");
ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone {}!",
configured_name.name.data());
return configured_name;
}

View File

@ -284,9 +284,10 @@ void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestCont
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto clock_type{rp.PopEnum<Service::PSC::Time::TimeType>()};
[[maybe_unused]] auto alignment{rp.Pop<u32>()};
auto user_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
auto network_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
auto clock_type{rp.PopEnum<Service::PSC::Time::TimeType>()};
Service::PSC::Time::ClockSnapshot snapshot{};
auto res =

View File

@ -61,6 +61,16 @@ Result MountTimeZoneBinary(Core::System& system) {
g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS());
}
if (g_time_zone_binary_romfs) {
// Validate that the romfs is readable, using invalid firmware keys can cause this to get
// set but the files to be garbage. In that case, we want to hit the next path and
// synthesise them instead.
Service::PSC::Time::LocationName name{"Etc/GMT"};
if (!IsTimeZoneBinaryValid(name)) {
ResetTimeZoneBinary();
}
}
if (!g_time_zone_binary_romfs) {
g_time_zone_binary_romfs = FileSys::ExtractRomFS(
FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId));
@ -102,6 +112,7 @@ bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) {
auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
if (!vfs_file) {
LOG_INFO(Service_Time, "Could not find timezone file {}", path);
return false;
}
return vfs_file->GetSize() != 0;

View File

@ -501,6 +501,22 @@ bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const {
}
}
void HLERequestContext::AddMoveInterface(SessionRequestHandlerPtr s) {
ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(
Kernel::LimitableResource::SessionCountMax, 1));
auto* session = Kernel::KSession::Create(kernel);
session->Initialize(nullptr, 0);
Kernel::KSession::Register(kernel, session);
auto& server = manager.lock()->GetServerManager();
auto next_manager = std::make_shared<Service::SessionRequestManager>(kernel, server);
next_manager->SetSessionHandler(std::move(s));
server.RegisterSession(&session->GetServerSession(), next_manager);
AddMoveObject(&session->GetClientSession());
}
std::string HLERequestContext::Description() const {
if (!command_header) {
return "No command header available";

View File

@ -339,6 +339,8 @@ public:
outgoing_move_objects.emplace_back(object);
}
void AddMoveInterface(SessionRequestHandlerPtr s);
void AddCopyObject(Kernel::KAutoObject* object) {
outgoing_copy_objects.emplace_back(object);
}

View File

@ -6,12 +6,12 @@
#include "core/core.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/jit/jit.h"
#include "core/hle/service/jit/jit_code_memory.h"
#include "core/hle/service/jit/jit_context.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
namespace Service::JIT {
@ -21,6 +21,9 @@ struct CodeRange {
u64 size;
};
using Struct32 = std::array<u64, 4>;
static_assert(sizeof(Struct32) == 32, "Struct32 has wrong size");
class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
public:
explicit IJitEnvironment(Core::System& system_,
@ -29,12 +32,13 @@ public:
: ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)},
user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)},
context{system_.ApplicationMemory()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IJitEnvironment::GenerateCode, "GenerateCode"},
{1, &IJitEnvironment::Control, "Control"},
{1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"},
{1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"},
{0, C<&IJitEnvironment::GenerateCode>, "GenerateCode"},
{1, C<&IJitEnvironment::Control>, "Control"},
{1000, C<&IJitEnvironment::LoadPlugin>, "LoadPlugin"},
{1001, C<&IJitEnvironment::GetCodeAddress>, "GetCodeAddress"},
};
// clang-format on
@ -50,28 +54,10 @@ public:
configuration.sys_ro_memory = configuration.user_ro_memory;
}
void GenerateCode(HLERequestContext& ctx) {
LOG_DEBUG(Service_JIT, "called");
struct InputParameters {
u32 data_size;
u64 command;
std::array<CodeRange, 2> ranges;
Struct32 data;
};
struct OutputParameters {
s32 return_value;
std::array<CodeRange, 2> ranges;
};
IPC::RequestParser rp{ctx};
const auto parameters{rp.PopRaw<InputParameters>()};
// Optional input/output buffers
const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
Result GenerateCode(Out<s32> out_return_value, Out<CodeRange> out_range0,
Out<CodeRange> out_range1, OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
u32 data_size, u64 command, CodeRange range0, CodeRange range1,
Struct32 data, InBuffer<BufferAttr_HipcMapAlias> buffer) {
// Function call prototype:
// void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg,
// u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in,
@ -83,66 +69,36 @@ public:
// other arguments are used to transfer state between the game and the plugin.
const VAddr ret_ptr{context.AddHeap(0u)};
const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])};
const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])};
const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))};
const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))};
const VAddr c0_in_ptr{context.AddHeap(range0)};
const VAddr c1_in_ptr{context.AddHeap(range1)};
const VAddr c0_out_ptr{context.AddHeap(ClearSize(range0))};
const VAddr c1_out_ptr{context.AddHeap(ClearSize(range1))};
const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
const VAddr data_ptr{context.AddHeap(parameters.data)};
const VAddr input_ptr{context.AddHeap(buffer.data(), buffer.size())};
const VAddr output_ptr{context.AddHeap(out_buffer.data(), out_buffer.size())};
const VAddr data_ptr{context.AddHeap(data)};
const VAddr configuration_ptr{context.AddHeap(configuration)};
// The callback does not directly return a value, it only writes to the output pointer
context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr,
configuration_ptr, parameters.command, input_ptr, input_buffer.size(),
c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr,
output_buffer.size());
configuration_ptr, command, input_ptr, buffer.size(), c0_in_ptr,
c1_in_ptr, data_ptr, data_size, output_ptr, out_buffer.size());
const s32 return_value{context.GetHeap<s32>(ret_ptr)};
*out_return_value = context.GetHeap<s32>(ret_ptr);
*out_range0 = context.GetHeap<CodeRange>(c0_out_ptr);
*out_range1 = context.GetHeap<CodeRange>(c1_out_ptr);
context.GetHeap(output_ptr, out_buffer.data(), out_buffer.size());
if (return_value == 0) {
// The callback has written to the output executable code range,
// requiring an instruction cache invalidation
Core::InvalidateInstructionCacheRange(process.GetPointerUnsafe(),
configuration.user_rx_memory.offset,
configuration.user_rx_memory.size);
// Write back to the IPC output buffer, if provided
if (ctx.CanWriteBuffer()) {
context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
}
const OutputParameters out{
.return_value = return_value,
.ranges =
{
context.GetHeap<CodeRange>(c0_out_ptr),
context.GetHeap<CodeRange>(c1_out_ptr),
},
};
IPC::ResponseBuilder rb{ctx, 8};
rb.Push(ResultSuccess);
rb.PushRaw(out);
} else {
if (*out_return_value != 0) {
LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
R_THROW(ResultUnknown);
}
};
void Control(HLERequestContext& ctx) {
LOG_DEBUG(Service_JIT, "called");
IPC::RequestParser rp{ctx};
const auto command{rp.PopRaw<u64>()};
// Optional input/output buffers
const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
R_SUCCEED();
}
Result Control(Out<s32> out_return_value, InBuffer<BufferAttr_HipcMapAlias> in_data,
OutBuffer<BufferAttr_HipcMapAlias> out_data, u64 command) {
// Function call prototype:
// u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size,
// u8* output_buf, size_t output_size);
@ -152,53 +108,30 @@ public:
const VAddr ret_ptr{context.AddHeap(0u)};
const VAddr configuration_ptr{context.AddHeap(configuration)};
const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
const VAddr input_ptr{context.AddHeap(in_data.data(), in_data.size())};
const VAddr output_ptr{context.AddHeap(out_data.data(), out_data.size())};
const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr,
command, input_ptr, input_buffer.size(),
output_ptr, output_buffer.size())};
command, input_ptr, in_data.size(), output_ptr,
out_data.size())};
const s32 return_value{context.GetHeap<s32>(ret_ptr)};
*out_return_value = context.GetHeap<s32>(ret_ptr);
context.GetHeap(output_ptr, out_data.data(), out_data.size());
if (wrapper_value == 0 && return_value == 0) {
// Write back to the IPC output buffer, if provided
if (ctx.CanWriteBuffer()) {
context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(return_value);
} else {
LOG_WARNING(Service_JIT, "plugin Control callback failed");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
if (wrapper_value == 0 && *out_return_value == 0) {
R_SUCCEED();
}
LOG_WARNING(Service_JIT, "plugin Control callback failed");
R_THROW(ResultUnknown);
}
void LoadPlugin(HLERequestContext& ctx) {
LOG_DEBUG(Service_JIT, "called");
IPC::RequestParser rp{ctx};
const auto tmem_size{rp.PopRaw<u64>()};
const auto tmem_handle{ctx.GetCopyHandle(0)};
const auto nro_plugin{ctx.ReadBuffer(1)};
if (tmem_size == 0) {
LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)};
Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory>& tmem,
InBuffer<BufferAttr_HipcMapAlias> nrr,
InBuffer<BufferAttr_HipcMapAlias> nro) {
if (tmem.IsNull()) {
LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
LOG_ERROR(Service_JIT, "Invalid transfer memory handle!");
R_THROW(ResultUnknown);
}
// Set up the configuration with the required TransferMemory address
@ -206,7 +139,7 @@ public:
configuration.transfer_memory.size = tmem_size;
// Gather up all the callbacks from the loaded plugin
auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)};
auto symbols{Core::Symbols::GetSymbols(nro, true)};
const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }};
callbacks.rtld_fini = GetSymbol("_fini");
@ -223,16 +156,12 @@ public:
if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
callbacks.OnPrepared == 0) {
LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
R_THROW(ResultUnknown);
}
if (!context.LoadNRO(nro_plugin)) {
if (!context.LoadNRO(nro)) {
LOG_ERROR(Service_JIT, "failed to load plugin");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
R_THROW(ResultUnknown);
}
context.MapProcessMemory(configuration.sys_ro_memory.offset,
@ -252,9 +181,7 @@ public:
const auto version{context.CallFunction(callbacks.GetVersion)};
if (version != 1) {
LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
R_THROW(ResultUnknown);
}
// Function prototype:
@ -280,22 +207,19 @@ public:
const auto configuration_ptr{context.AddHeap(configuration)};
context.CallFunction(callbacks.OnPrepared, configuration_ptr);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
R_SUCCEED();
}
void GetCodeAddress(HLERequestContext& ctx) {
Result GetCodeAddress(Out<u64> rx_offset, Out<u64> ro_offset) {
LOG_DEBUG(Service_JIT, "called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.Push(configuration.user_rx_memory.offset);
rb.Push(configuration.user_ro_memory.offset);
*rx_offset = configuration.user_rx_memory.offset;
*ro_offset = configuration.user_ro_memory.offset;
R_SUCCEED();
}
private:
using Struct32 = std::array<u8, 32>;
struct GuestCallbacks {
VAddr rtld_fini;
VAddr rtld_init;
@ -335,7 +259,7 @@ public:
explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"},
{0, C<&JITU::CreateJitEnvironment>, "CreateJitEnvironment"},
};
// clang-format on
@ -343,76 +267,33 @@ public:
}
private:
void CreateJitEnvironment(HLERequestContext& ctx) {
LOG_DEBUG(Service_JIT, "called");
struct Parameters {
u64 rx_size;
u64 ro_size;
};
IPC::RequestParser rp{ctx};
const auto parameters{rp.PopRaw<Parameters>()};
const auto process_handle{ctx.GetCopyHandle(0)};
const auto rx_mem_handle{ctx.GetCopyHandle(1)};
const auto ro_mem_handle{ctx.GetCopyHandle(2)};
if (parameters.rx_size == 0 || parameters.ro_size == 0) {
LOG_ERROR(Service_JIT, "attempted to init with empty code regions");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment,
u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess>& process,
InCopyHandle<Kernel::KCodeMemory>& rx_mem,
InCopyHandle<Kernel::KCodeMemory>& ro_mem) {
if (process.IsNull()) {
LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
LOG_ERROR(Service_JIT, "process is null");
R_THROW(ResultUnknown);
}
auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)};
if (rx_mem.IsNull()) {
LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
LOG_ERROR(Service_JIT, "rx_mem is null");
R_THROW(ResultUnknown);
}
auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)};
if (ro_mem.IsNull()) {
LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
if (rx_mem.IsNull()) {
LOG_ERROR(Service_JIT, "ro_mem is null");
R_THROW(ResultUnknown);
}
CodeMemory rx, ro;
Result res;
res = rx.Initialize(*process, *rx_mem, parameters.rx_size,
Kernel::Svc::MemoryPermission::ReadExecute, generate_random);
if (R_FAILED(res)) {
LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
return;
}
R_TRY(rx.Initialize(*process, *rx_mem, rx_size, Kernel::Svc::MemoryPermission::ReadExecute,
generate_random));
R_TRY(ro.Initialize(*process, *ro_mem, ro_size, Kernel::Svc::MemoryPermission::Read,
generate_random));
res = ro.Initialize(*process, *ro_mem, parameters.ro_size,
Kernel::Svc::MemoryPermission::Read, generate_random);
if (R_FAILED(res)) {
LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IJitEnvironment>(system, std::move(process), std::move(rx),
std::move(ro));
*out_jit_environment = std::make_shared<IJitEnvironment>(system, std::move(process),
std::move(rx), std::move(ro));
R_SUCCEED();
}
private:

View File

@ -112,6 +112,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
void Container::CloseSession(SessionId session_id) {
std::scoped_lock lk(impl->session_guard);
impl->file.UnmapAllHandles(session_id);
auto& session = impl->sessions[session_id.id];
auto& smmu = impl->host1x.MemoryManager();
if (session.has_preallocated_area) {

View File

@ -326,4 +326,17 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
return freeInfo;
}
void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
auto handles_copy = [&] {
std::scoped_lock lk{handles_lock};
return handles;
}();
for (auto& [id, handle] : handles_copy) {
if (handle->session_id.id == session_id.id) {
FreeHandle(id, false);
}
}
}
} // namespace Service::Nvidia::NvCore

View File

@ -152,6 +152,8 @@ public:
*/
std::optional<FreeInfo> FreeHandle(Handle::Id handle, bool internal_session);
void UnmapAllHandles(NvCore::SessionId session_id);
private:
std::list<std::shared_ptr<Handle>> unmap_queue{};
std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue`

View File

@ -4,6 +4,7 @@
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_process.h"
@ -29,7 +30,7 @@ void NVDRV::Open(HLERequestContext& ctx) {
}
const auto& buffer = ctx.ReadBuffer();
const std::string device_name(buffer.begin(), buffer.end());
const std::string device_name(Common::StringFromBuffer(buffer));
if (device_name == "/dev/nvhost-prof-gpu") {
rb.Push<DeviceFD>(0);

View File

@ -119,15 +119,21 @@ void ServiceManager::Handle_GetStaticServiceAsServiceManager(HLERequestContext&
void ServiceManager::Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto clock_source_id{rp.PopRaw<Common::UUID>()};
auto rtc_offset{rp.Pop<s64>()};
auto internal_offset{rp.Pop<s64>()};
auto test_offset{rp.Pop<s64>()};
auto is_rtc_reset_detected{rp.Pop<bool>()};
struct Parameters {
bool reset_detected;
Common::UUID clock_source_id;
s64 rtc_offset;
s64 internal_offset;
s64 test_offset;
};
static_assert(sizeof(Parameters) == 0x30);
auto res = SetupStandardSteadyClockCore(clock_source_id, rtc_offset, internal_offset,
test_offset, is_rtc_reset_detected);
IPC::RequestParser rp{ctx};
auto params{rp.PopRaw<Parameters>()};
auto res = SetupStandardSteadyClockCore(params.clock_source_id, params.rtc_offset,
params.internal_offset, params.test_offset,
params.reset_detected);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
@ -162,11 +168,16 @@ void ServiceManager::Handle_SetupStandardNetworkSystemClockCore(HLERequestContex
void ServiceManager::Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto time_point{rp.PopRaw<SteadyClockTimePoint>()};
auto automatic_correction{rp.Pop<bool>()};
struct Parameters {
bool automatic_correction;
SteadyClockTimePoint time_point;
};
static_assert(sizeof(Parameters) == 0x20);
auto res = SetupStandardUserSystemClockCore(time_point, automatic_correction);
IPC::RequestParser rp{ctx};
auto params{rp.PopRaw<Parameters>()};
auto res = SetupStandardUserSystemClockCore(params.time_point, params.automatic_correction);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
@ -175,16 +186,21 @@ void ServiceManager::Handle_SetupStandardUserSystemClockCore(HLERequestContext&
void ServiceManager::Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called.");
struct Parameters {
u32 location_count;
LocationName name;
SteadyClockTimePoint time_point;
RuleVersion rule_version;
};
static_assert(sizeof(Parameters) == 0x50);
IPC::RequestParser rp{ctx};
auto name{rp.PopRaw<LocationName>()};
auto time_point{rp.PopRaw<SteadyClockTimePoint>()};
auto rule_version{rp.PopRaw<RuleVersion>()};
auto location_count{rp.Pop<u32>()};
auto params{rp.PopRaw<Parameters>()};
auto rule_buffer{ctx.ReadBuffer()};
auto res =
SetupTimeZoneServiceCore(name, time_point, rule_version, location_count, rule_buffer);
auto res = SetupTimeZoneServiceCore(params.name, params.time_point, params.rule_version,
params.location_count, rule_buffer);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
@ -286,11 +302,22 @@ void ServiceManager::Handle_GetClosestAlarmInfo(HLERequestContext& ctx) {
s64 time{};
auto res = GetClosestAlarmInfo(is_valid, alarm_info, time);
IPC::ResponseBuilder rb{ctx, 5 + sizeof(AlarmInfo) / sizeof(u32)};
struct OutParameters {
bool is_valid;
AlarmInfo alarm_info;
s64 time;
};
static_assert(sizeof(OutParameters) == 0x20);
OutParameters out_params{
.is_valid = is_valid,
.alarm_info = alarm_info,
.time = time,
};
IPC::ResponseBuilder rb{ctx, 2 + sizeof(OutParameters) / sizeof(u32)};
rb.Push(res);
rb.PushRaw<AlarmInfo>(alarm_info);
rb.Push<bool>(is_valid);
rb.Push<s64>(time);
rb.PushRaw<OutParameters>(out_params);
}
// =============================== Implementations ===========================

View File

@ -277,9 +277,10 @@ void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestCont
LOG_DEBUG(Service_Time, "called.");
IPC::RequestParser rp{ctx};
auto clock_type{rp.PopEnum<TimeType>()};
[[maybe_unused]] auto alignment{rp.Pop<u32>()};
auto user_context{rp.PopRaw<SystemClockContext>()};
auto network_context{rp.PopRaw<SystemClockContext>()};
auto clock_type{rp.PopEnum<TimeType>()};
ClockSnapshot snapshot{};
auto res =

View File

@ -15,12 +15,12 @@ SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr<TimeManager> man
// clang-format off
static const FunctionInfo functions[] = {
{0, &SteadyClock::Handle_GetCurrentTimePoint, "GetCurrentTimePoint"},
{1, &SteadyClock::Handle_GetTestOffset, "GetTestOffset"},
{2, &SteadyClock::Handle_SetTestOffset, "SetTestOffset"},
{3, &SteadyClock::Handle_GetRtcValue, "GetRtcValue"},
{4, &SteadyClock::Handle_IsRtcResetDetected, "IsRtcResetDetected"},
{5, &SteadyClock::Handle_GetSetupResultValue, "GetSetupResultValue"},
{6, &SteadyClock::Handle_GetInternalOffset, "GetInternalOffset"},
{2, &SteadyClock::Handle_GetTestOffset, "GetTestOffset"},
{3, &SteadyClock::Handle_SetTestOffset, "SetTestOffset"},
{100, &SteadyClock::Handle_GetRtcValue, "GetRtcValue"},
{101, &SteadyClock::Handle_IsRtcResetDetected, "IsRtcResetDetected"},
{102, &SteadyClock::Handle_GetSetupResultValue, "GetSetupResultValue"},
{200, &SteadyClock::Handle_GetInternalOffset, "GetInternalOffset"},
};
// clang-format on
RegisterHandlers(functions);

View File

@ -6,13 +6,13 @@
#include "common/scope_exit.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ro/ro.h"
#include "core/hle/service/ro/ro_nro_utils.h"
#include "core/hle/service/ro/ro_results.h"
#include "core/hle/service/ro/ro_types.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::RO {
@ -500,46 +500,65 @@ private:
}
};
class RoInterface {
class RoInterface : public ServiceFramework<RoInterface> {
public:
explicit RoInterface(std::shared_ptr<RoContext> ro, NrrKind nrr_kind)
: m_ro(ro), m_context_id(InvalidContextId), m_nrr_kind(nrr_kind) {}
explicit RoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro,
NrrKind nrr_kind)
: ServiceFramework{system_, name_}, m_ro(ro), m_context_id(InvalidContextId),
m_nrr_kind(nrr_kind) {
// clang-format off
static const FunctionInfo functions[] = {
{0, C<&RoInterface::MapManualLoadModuleMemory>, "MapManualLoadModuleMemory"},
{1, C<&RoInterface::UnmapManualLoadModuleMemory>, "UnmapManualLoadModuleMemory"},
{2, C<&RoInterface::RegisterModuleInfo>, "RegisterModuleInfo"},
{3, C<&RoInterface::UnregisterModuleInfo>, "UnregisterModuleInfo"},
{4, C<&RoInterface::RegisterProcessHandle>, "RegisterProcessHandle"},
{10, C<&RoInterface::RegisterProcessModuleInfo>, "RegisterProcessModuleInfo"},
};
// clang-format on
RegisterHandlers(functions);
}
~RoInterface() {
m_ro->UnregisterProcess(m_context_id);
}
Result MapManualLoadModuleMemory(u64* out_load_address, u64 client_pid, u64 nro_address,
u64 nro_size, u64 bss_address, u64 bss_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address, m_context_id, nro_address,
Result MapManualLoadModuleMemory(Out<u64> out_load_address, ClientProcessId client_pid,
u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address,
nro_size, bss_address, bss_size));
}
Result UnmapManualLoadModuleMemory(u64 client_pid, u64 nro_address) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
Result UnmapManualLoadModuleMemory(ClientProcessId client_pid, u64 nro_address) {
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(m_ro->UnmapManualLoadModuleMemory(m_context_id, nro_address));
}
Result RegisterModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
Result RegisterModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(
m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, NrrKind::User, true));
}
Result UnregisterModuleInfo(u64 client_pid, u64 nrr_address) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
Result UnregisterModuleInfo(ClientProcessId client_pid, u64 nrr_address) {
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(m_ro->UnregisterModuleInfo(m_context_id, nrr_address));
}
Result RegisterProcessHandle(u64 client_pid, Kernel::KProcess* process) {
Result RegisterProcessHandle(ClientProcessId client_pid,
InCopyHandle<Kernel::KProcess>& process) {
// Register the process.
R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process, client_pid));
R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.GetPointerUnsafe(),
*client_pid));
}
Result RegisterProcessModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size,
Kernel::KProcess* process) {
Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size,
InCopyHandle<Kernel::KProcess>& process) {
// Validate the process.
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
// Register the module.
R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind,
@ -552,137 +571,6 @@ private:
NrrKind m_nrr_kind{};
};
class IRoInterface : public ServiceFramework<IRoInterface> {
public:
explicit IRoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro,
NrrKind nrr_kind)
: ServiceFramework{system_, name_}, interface {
ro, nrr_kind
} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IRoInterface::MapManualLoadModuleMemory, "MapManualLoadModuleMemory"},
{1, &IRoInterface::UnmapManualLoadModuleMemory, "UnmapManualLoadModuleMemory"},
{2, &IRoInterface::RegisterModuleInfo, "RegisterModuleInfo"},
{3, &IRoInterface::UnregisterModuleInfo, "UnregisterModuleInfo"},
{4, &IRoInterface::RegisterProcessHandle, "RegisterProcessHandle"},
{10, &IRoInterface::RegisterProcessModuleInfo, "RegisterProcessModuleInfo"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void MapManualLoadModuleMemory(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nro_address;
u64 nro_size;
u64 bss_address;
u64 bss_size;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
u64 load_address = 0;
auto result = interface.MapManualLoadModuleMemory(&load_address, ctx.GetPID(),
params.nro_address, params.nro_size,
params.bss_address, params.bss_size);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(result);
rb.Push(load_address);
}
void UnmapManualLoadModuleMemory(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nro_address;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto result = interface.UnmapManualLoadModuleMemory(ctx.GetPID(), params.nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void RegisterModuleInfo(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nrr_address;
u64 nrr_size;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto result =
interface.RegisterModuleInfo(ctx.GetPID(), params.nrr_address, params.nrr_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void UnregisterModuleInfo(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nrr_address;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto result = interface.UnregisterModuleInfo(ctx.GetPID(), params.nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void RegisterProcessHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
auto client_pid = ctx.GetPID();
auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void RegisterProcessModuleInfo(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nrr_address;
u64 nrr_size;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
auto client_pid = ctx.GetPID();
auto result = interface.RegisterProcessModuleInfo(
client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
RoInterface interface;
};
} // namespace
void LoopProcess(Core::System& system) {
@ -691,11 +579,11 @@ void LoopProcess(Core::System& system) {
auto ro = std::make_shared<RoContext>();
const auto RoInterfaceFactoryForUser = [&, ro] {
return std::make_shared<IRoInterface>(system, "ldr:ro", ro, NrrKind::User);
return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User);
};
const auto RoInterfaceFactoryForJitPlugin = [&, ro] {
return std::make_shared<IRoInterface>(system, "ro:1", ro, NrrKind::JitPlugin);
return std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin);
};
server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser));

View File

@ -206,6 +206,22 @@ protected:
RegisterHandlersBaseTipc(functions, n);
}
protected:
template <bool Domain, auto F>
void CmifReplyWrap(HLERequestContext& ctx);
/**
* Wraps the template pointer-to-member function for use in a domain session.
*/
template <auto F>
static constexpr HandlerFnP<Self> D = &Self::template CmifReplyWrap<true, F>;
/**
* Wraps the template pointer-to-member function for use in a non-domain session.
*/
template <auto F>
static constexpr HandlerFnP<Self> C = &Self::template CmifReplyWrap<false, F>;
private:
/**
* This function is used to allow invocation of pointers to handlers stored in the base class

View File

@ -709,12 +709,12 @@ void ISystemSettingsServer::GetSettingsItemValueSize(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{setting_category_buf.begin(), setting_category_buf.end()};
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{setting_name_buf.begin(), setting_name_buf.end()};
const std::string setting_name{Common::StringFromBuffer(setting_name_buf)};
auto settings{GetSettings()};
u64 response_size{0};
@ -732,12 +732,12 @@ 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{setting_category_buf.begin(), setting_category_buf.end()};
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{setting_name_buf.begin(), setting_name_buf.end()};
const std::string setting_name{Common::StringFromBuffer(setting_name_buf)};
std::vector<u8> value;
auto response = GetSettingsItemValue(value, setting_category, setting_name);

View File

@ -15,6 +15,7 @@
#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"
@ -694,9 +695,7 @@ private:
void OpenLayer(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
const std::string display_name(name_buf.begin(), end);
const std::string display_name(Common::StringFromBuffer(name_buf));
const u64 layer_id = rp.Pop<u64>();
const u64 aruid = rp.Pop<u64>();

View File

@ -10,6 +10,7 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/submission_package.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/filesystem/filesystem.h"
@ -109,6 +110,13 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
return result;
}
if (nsp->IsExtractedType()) {
system.GetFileSystemController().RegisterProcess(
process.GetProcessId(), {},
std::make_shared<FileSys::RomFSFactory>(*this, system.GetContentProvider(),
system.GetFileSystemController()));
}
FileSys::VirtualFile update_raw;
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(),

View File

@ -1093,6 +1093,20 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
[&] { rasterizer = true; });
if (rasterizer) {
impl->InvalidateGPUMemory(ptr, size);
const auto type = impl->current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type();
if (type == Common::PageType::RasterizerCachedMemory) {
// Check if device mapped. If not, this bugged and we can unmark.
DAddr addr{};
Common::ScratchBuffer<u32> buffer;
impl->gpu_device_memory->ApplyOpOnPointer(ptr, buffer,
[&](DAddr address) { addr = address; });
if (addr == 0) {
LOG_ERROR(HW_Memory, "Fixing unmapped cached region {:#x}", GetInteger(vaddr));
impl->RasterizerMarkRegionCached(GetInteger(vaddr), size, false);
}
}
}
#ifdef __linux__

View File

@ -47,14 +47,14 @@ inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_contro
/**
* \brief Removes all DLC for a game
* \param system Raw pointer to the system instance
* \param system Reference to the system instance
* \param program_id Program ID for the game that will have all of its DLC removed
* \return Number of DLC removed
*/
inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) {
inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) {
size_t count{};
const auto& fs_controller = system->GetFileSystemController();
const auto dlc_entries = system->GetContentProvider().ListEntriesFilter(
const auto& fs_controller = system.GetFileSystemController();
const auto dlc_entries = system.GetContentProvider().ListEntriesFilter(
FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
std::vector<u64> program_dlc_entries;
@ -124,15 +124,15 @@ inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_contro
/**
* \brief Installs an NSP
* \param system Raw pointer to the system instance
* \param vfs Raw pointer to the VfsFilesystem instance in Core::System
* \param system Reference to the system instance
* \param vfs Reference to the VfsFilesystem instance in Core::System
* \param filename Path to the NSP file
* \param callback Callback to report the progress of the installation. The first size_t
* parameter is the total size of the virtual file and the second is the current progress. If you
* return true to the callback, it will cancel the installation as soon as possible.
* \return [InstallResult] representing how the installation finished
*/
inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vfs,
inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vfs,
const std::string& filename,
const std::function<bool(size_t, size_t)>& callback) {
const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
@ -159,7 +159,7 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf
};
std::shared_ptr<FileSys::NSP> nsp;
FileSys::VirtualFile file = vfs->OpenFile(filename, FileSys::Mode::Read);
FileSys::VirtualFile file = vfs.OpenFile(filename, FileSys::Mode::Read);
if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) {
nsp = std::make_shared<FileSys::NSP>(file);
if (nsp->IsExtractedType()) {
@ -173,7 +173,7 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf
return InstallResult::Failure;
}
const auto res =
system->GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy);
system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy);
switch (res) {
case FileSys::InstallResult::Success:
return InstallResult::Success;
@ -188,17 +188,17 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf
/**
* \brief Installs an NCA
* \param vfs Raw pointer to the VfsFilesystem instance in Core::System
* \param vfs Reference to the VfsFilesystem instance in Core::System
* \param filename Path to the NCA file
* \param registered_cache Raw pointer to the registered cache that the NCA will be installed to
* \param registered_cache Reference to the registered cache that the NCA will be installed to
* \param title_type Type of NCA package to install
* \param callback Callback to report the progress of the installation. The first size_t
* parameter is the total size of the virtual file and the second is the current progress. If you
* return true to the callback, it will cancel the installation as soon as possible.
* \return [InstallResult] representing how the installation finished
*/
inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& filename,
FileSys::RegisteredCache* registered_cache,
inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string& filename,
FileSys::RegisteredCache& registered_cache,
const FileSys::TitleType title_type,
const std::function<bool(size_t, size_t)>& callback) {
const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
@ -224,7 +224,7 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string&
return true;
};
const auto nca = std::make_shared<FileSys::NCA>(vfs->OpenFile(filename, FileSys::Mode::Read));
const auto nca = std::make_shared<FileSys::NCA>(vfs.OpenFile(filename, FileSys::Mode::Read));
const auto id = nca->GetStatus();
// Game updates necessary are missing base RomFS
@ -233,7 +233,7 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string&
return InstallResult::Failure;
}
const auto res = registered_cache->InstallEntry(*nca, title_type, true, copy);
const auto res = registered_cache.InstallEntry(*nca, title_type, true, copy);
if (res == FileSys::InstallResult::Success) {
return InstallResult::Success;
} else if (res == FileSys::InstallResult::OverwriteExisting) {
@ -245,19 +245,19 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string&
/**
* \brief Verifies the installed contents for a given ManualContentProvider
* \param system Raw pointer to the system instance
* \param provider Raw pointer to the content provider that's tracking indexed games
* \param system Reference to the system instance
* \param provider Reference to the content provider that's tracking indexed games
* \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.
* \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,
Core::System& system, FileSys::ManualContentProvider& provider,
const std::function<bool(size_t, size_t)>& callback) {
// Get content registries.
auto bis_contents = system->GetFileSystemController().GetSystemNANDContents();
auto user_contents = system->GetFileSystemController().GetUserNANDContents();
auto bis_contents = system.GetFileSystemController().GetSystemNANDContents();
auto user_contents = system.GetFileSystemController().GetUserNANDContents();
std::vector<FileSys::RegisteredCache*> content_providers;
if (bis_contents) {
@ -309,11 +309,11 @@ inline std::vector<std::string> VerifyInstalledContents(
const auto title_id = nca.GetTitleId();
std::string title_name = "unknown";
const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id),
FileSys::ContentRecordType::Control);
const auto control = provider.GetEntry(FileSys::GetBaseTitleID(title_id),
FileSys::ContentRecordType::Control);
if (control && control->GetStatus() == Loader::ResultStatus::Success) {
const FileSys::PatchManager pm{title_id, system->GetFileSystemController(),
*provider};
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
provider};
const auto [nacp, logo] = pm.ParseControlNCA(*control);
if (nacp) {
title_name = nacp->GetApplicationName();
@ -335,7 +335,7 @@ inline std::vector<std::string> VerifyInstalledContents(
/**
* \brief Verifies the contents of a given game
* \param system Raw pointer to the system instance
* \param system Reference to the system instance
* \param game_path Patch to the game file
* \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
@ -343,10 +343,10 @@ inline std::vector<std::string> VerifyInstalledContents(
* \return GameVerificationResult representing how the verification process finished
*/
inline GameVerificationResult VerifyGameContents(
Core::System* system, const std::string& game_path,
Core::System& system, const std::string& game_path,
const std::function<bool(size_t, size_t)>& callback) {
const auto loader = Loader::GetLoader(
*system, system->GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read));
const auto loader =
Loader::GetLoader(system, system.GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read));
if (loader == nullptr) {
return GameVerificationResult::NotImplemented;
}
@ -368,4 +368,11 @@ inline GameVerificationResult VerifyGameContents(
return GameVerificationResult::Success;
}
/**
* Checks if the keys required for decrypting firmware and games are available
*/
inline bool AreKeysPresent() {
return !Core::Crypto::KeyManager::Instance().BaseDeriveNecessary();
}
} // namespace ContentManager

View File

@ -110,7 +110,11 @@ void EmulatedController::ReloadFromSettings() {
original_npad_type = npad_type;
}
SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Active);
// Disable special features before disconnecting
if (controller.right_polling_mode != Common::Input::PollingMode::Active) {
SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Active);
}
Disconnect();
if (player.connected) {
Connect();
@ -1241,7 +1245,12 @@ bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationV
return false;
}
last_vibration_value = vibration;
// Skip duplicated vibrations
if (last_vibration_value[index] == vibration) {
return Settings::values.vibration_enabled.GetValue();
}
last_vibration_value[index] = vibration;
if (!Settings::values.vibration_enabled) {
return false;
@ -1272,7 +1281,10 @@ bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationV
}
VibrationValue EmulatedController::GetActualVibrationValue(DeviceIndex device_index) const {
return last_vibration_value;
if (device_index >= DeviceIndex::MaxDeviceIndex) {
return Core::HID::DEFAULT_VIBRATION_VALUE;
}
return last_vibration_value[static_cast<std::size_t>(device_index)];
}
bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {

View File

@ -581,7 +581,8 @@ private:
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
u32 turbo_button_state{0};
std::size_t nfc_handles{0};
VibrationValue last_vibration_value{DEFAULT_VIBRATION_VALUE};
std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE,
DEFAULT_VIBRATION_VALUE};
// Temporary values to avoid doing changes while the controller is in configuring mode
NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};

View File

@ -639,6 +639,15 @@ struct VibrationValue {
f32 low_frequency{};
f32 high_amplitude{};
f32 high_frequency{};
bool operator==(const VibrationValue& b) {
if (low_amplitude != b.low_amplitude || high_amplitude != b.high_amplitude) {
return false;
}
if (low_frequency != b.low_amplitude || high_frequency != b.high_frequency) {
return false;
}
return true;
}
};
static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");

View File

@ -1546,7 +1546,10 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
std::span<const u8> upload_span;
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
if (IsRangeGranular(device_addr, copy.size)) {
upload_span = std::span(device_memory.GetPointer<u8>(device_addr), copy.size);
auto* const ptr = device_memory.GetPointer<u8>(device_addr);
if (ptr != nullptr) {
upload_span = std::span(ptr, copy.size);
}
} else {
if (immediate_buffer.empty()) {
immediate_buffer = ImmediateBuffer(largest_copy);

View File

@ -243,10 +243,12 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
const u64 size_in_bytes{Tegra::Texture::CalculateSize(
true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
const std::span<const u8> input_data(host_ptr, size_in_bytes);
Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
framebuffer.width, framebuffer.height, 1, block_height_log2,
0);
if (host_ptr != nullptr) {
const std::span<const u8> input_data(host_ptr, size_in_bytes);
Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
framebuffer.width, framebuffer.height, 1,
block_height_log2, 0);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));

View File

@ -230,9 +230,11 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
const u64 tiled_size{Tegra::Texture::CalculateSize(true, bytes_per_pixel,
framebuffer.stride, framebuffer.height,
1, block_height_log2, 0)};
Tegra::Texture::UnswizzleTexture(
mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
if (host_ptr != nullptr) {
Tegra::Texture::UnswizzleTexture(
mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
}
const VkBufferImageCopy copy{
.bufferOffset = image_offset,

View File

@ -1064,8 +1064,6 @@ public:
}
});
}
auto* ptr = device_memory.GetPointer<u8>(new_query->dependant_address);
ASSERT(ptr != nullptr);
new_query->dependant_manage = must_manage_dependance;
pending_flush_queries.push_back(index);
@ -1104,9 +1102,11 @@ public:
tfb_streamer.Free(query->dependant_index);
} else {
u8* pointer = device_memory.GetPointer<u8>(query->dependant_address);
u32 result;
std::memcpy(&result, pointer, sizeof(u32));
num_vertices = static_cast<u64>(result) / query->stride;
if (pointer != nullptr) {
u32 result;
std::memcpy(&result, pointer, sizeof(u32));
num_vertices = static_cast<u64>(result) / query->stride;
}
}
query->value = [&]() -> u64 {
switch (query->topology) {
@ -1360,7 +1360,9 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
const auto check_value = [&](DAddr address) {
u8* ptr = impl->device_memory.GetPointer<u8>(address);
u64 value{};
std::memcpy(&value, ptr, sizeof(value));
if (ptr != nullptr) {
std::memcpy(&value, ptr, sizeof(value));
}
return value == 0;
};
std::array<VideoCommon::LookupData*, 2> objects{&object_1, &object_2};

View File

@ -39,6 +39,10 @@ void SortPhysicalDevicesPerVendor(std::vector<VkPhysicalDevice>& devices,
}
}
bool IsMicrosoftDozen(const char* device_name) {
return std::strstr(device_name, "Microsoft") != nullptr;
}
void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceDispatch& dld) {
// Sort by name, this will set a base and make GPUs with higher numbers appear first
// (e.g. GTX 1650 will intentionally be listed before a GTX 1080).
@ -52,6 +56,12 @@ void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceD
});
// Prefer Nvidia over AMD, AMD over Intel, Intel over the rest.
SortPhysicalDevicesPerVendor(devices, dld, {0x10DE, 0x1002, 0x8086});
// Demote Microsoft's Dozen devices to the bottom.
SortPhysicalDevices(
devices, dld,
[](const VkPhysicalDeviceProperties& lhs, const VkPhysicalDeviceProperties& rhs) {
return IsMicrosoftDozen(rhs.deviceName) && !IsMicrosoftDozen(lhs.deviceName);
});
}
template <typename T>

View File

@ -423,7 +423,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
RemoveCachedContents();
// Gen keys if necessary
OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
OnCheckFirmwareDecryption();
game_list->LoadCompatibilityList();
game_list->PopulateAsync(UISettings::values.game_dirs);
@ -1574,8 +1574,6 @@ void GMainWindow::ConnectMenuEvents() {
connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig);
// Tools
connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this,
ReinitializeKeyBehavior::Warning));
connect_menu(ui->action_Load_Album, &GMainWindow::OnAlbum);
connect_menu(ui->action_Load_Cabinet_Nickname_Owner,
[this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); });
@ -2501,7 +2499,7 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
}
void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
const size_t count = ContentManager::RemoveAllDLC(system.get(), program_id);
const size_t count = ContentManager::RemoveAllDLC(*system, program_id);
if (count == 0) {
QMessageBox::warning(this, GetGameListErrorRemoving(type),
tr("There are no DLC installed for this title."));
@ -2798,8 +2796,7 @@ void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
return progress.wasCanceled();
};
const auto result =
ContentManager::VerifyGameContents(system.get(), game_path, QtProgressCallback);
const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback);
progress.close();
switch (result) {
case ContentManager::GameVerificationResult::Success:
@ -3268,7 +3265,7 @@ void GMainWindow::OnMenuInstallToNAND() {
return false;
};
future = QtConcurrent::run([this, &file, progress_callback] {
return ContentManager::InstallNSP(system.get(), vfs.get(), file.toStdString(),
return ContentManager::InstallNSP(*system, *vfs, file.toStdString(),
progress_callback);
});
@ -3371,7 +3368,7 @@ ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) {
}
return false;
};
return ContentManager::InstallNCA(vfs.get(), filename.toStdString(), registered_cache,
return ContentManager::InstallNCA(*vfs, filename.toStdString(), *registered_cache,
static_cast<FileSys::TitleType>(index), progress_callback);
}
@ -4121,7 +4118,7 @@ void GMainWindow::OnVerifyInstalledContents() {
};
const std::vector<std::string> result =
ContentManager::VerifyInstalledContents(system.get(), provider.get(), QtProgressCallback);
ContentManager::VerifyInstalledContents(*system, *provider, QtProgressCallback);
progress.close();
if (result.empty()) {
@ -4551,122 +4548,20 @@ void GMainWindow::OnMouseActivity() {
}
}
void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
if (behavior == ReinitializeKeyBehavior::Warning) {
const auto res = QMessageBox::information(
this, tr("Confirm Key Rederivation"),
tr("You are about to force rederive all of your keys. \nIf you do not know what "
"this "
"means or what you are doing, \nthis is a potentially destructive action. "
"\nPlease "
"make sure this is what you want \nand optionally make backups.\n\nThis will "
"delete "
"your autogenerated key files and re-run the key derivation module."),
QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
if (res == QMessageBox::Cancel)
return;
const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated");
Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated");
Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated");
}
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
bool all_keys_present{true};
if (keys.BaseDeriveNecessary()) {
Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory("", FileSys::Mode::Read)};
const auto function = [this, &keys, &pdm] {
keys.PopulateFromPartitionData(pdm);
system->GetFileSystemController().CreateFactories(*vfs);
keys.DeriveETicket(pdm, system->GetContentProvider());
};
QString errors;
if (!pdm.HasFuses()) {
errors += tr("Missing fuses");
}
if (!pdm.HasBoot0()) {
errors += tr(" - Missing BOOT0");
}
if (!pdm.HasPackage2()) {
errors += tr(" - Missing BCPKG2-1-Normal-Main");
}
if (!pdm.HasProdInfo()) {
errors += tr(" - Missing PRODINFO");
}
if (!errors.isEmpty()) {
all_keys_present = false;
QMessageBox::warning(
this, tr("Derivation Components Missing"),
tr("Encryption keys are missing. "
"<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
"quickstart guide</a> to get all your keys, firmware and "
"games.<br><br><small>(%1)</small>")
.arg(errors));
}
QProgressDialog prog(this);
prog.setRange(0, 0);
prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your "
"system's performance."));
prog.setWindowTitle(tr("Deriving Keys"));
prog.show();
auto future = QtConcurrent::run(function);
while (!future.isFinished()) {
QCoreApplication::processEvents();
}
prog.close();
}
void GMainWindow::OnCheckFirmwareDecryption() {
system->GetFileSystemController().CreateFactories(*vfs);
if (all_keys_present && !this->CheckSystemArchiveDecryption()) {
LOG_WARNING(Frontend, "Mii model decryption failed");
if (!ContentManager::AreKeysPresent()) {
QMessageBox::warning(
this, tr("System Archive Decryption Failed"),
tr("Encryption keys failed to decrypt firmware. "
this, tr("Derivation Components Missing"),
tr("Encryption keys are missing. "
"<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
"quickstart guide</a> to get all your keys, firmware and "
"games."));
}
SetFirmwareVersion();
if (behavior == ReinitializeKeyBehavior::Warning) {
game_list->PopulateAsync(UISettings::values.game_dirs);
}
UpdateMenuState();
}
bool GMainWindow::CheckSystemArchiveDecryption() {
constexpr u64 MiiModelId = 0x0100000000000802;
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
// Not having system BIS files is not an error.
return true;
}
auto mii_nca = bis_system->GetEntry(MiiModelId, FileSys::ContentRecordType::Data);
if (!mii_nca) {
// Not having the Mii model is not an error.
return true;
}
// Return whether we are able to decrypt the RomFS of the Mii model.
return mii_nca->GetRomFS().get() != nullptr;
}
bool GMainWindow::CheckFirmwarePresence() {
constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);

View File

@ -125,11 +125,6 @@ enum class EmulatedDirectoryTarget {
SDMC,
};
enum class ReinitializeKeyBehavior {
NoWarning,
Warning,
};
namespace VkDeviceInfo {
class Record;
}
@ -400,7 +395,7 @@ private slots:
void OnMiiEdit();
void OnOpenControllerMenu();
void OnCaptureScreenshot();
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
void OnCheckFirmwareDecryption();
void OnLanguageChanged(const QString& locale);
void OnMouseActivity();
bool OnShutdownBegin();
@ -441,7 +436,6 @@ private:
void LoadTranslation();
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
bool CheckDarkMode();
bool CheckSystemArchiveDecryption();
bool CheckFirmwarePresence();
void SetFirmwareVersion();
void ConfigureFilesystemProvider(const std::string& filepath);

View File

@ -224,11 +224,6 @@
<string>&amp;Stop</string>
</property>
</action>
<action name="action_Rederive">
<property name="text">
<string>&amp;Reinitialize keys...</string>
</property>
</action>
<action name="action_Verify_installed_contents">
<property name="text">
<string>&amp;Verify Installed Contents</string>