summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------externals/dynarmic0
m---------externals/nx_tzdb/tzdb_to_nx0
-rw-r--r--src/android/app/src/main/AndroidManifest.xml6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt55
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt24
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt42
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt70
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt25
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt6
-rw-r--r--src/android/app/src/main/jni/android_settings.h2
-rw-r--r--src/android/app/src/main/jni/emu_window/emu_window.cpp4
-rw-r--r--src/android/app/src/main/jni/game_metadata.cpp22
-rw-r--r--src/android/app/src/main/jni/native.cpp125
-rw-r--r--src/android/app/src/main/jni/native.h6
-rw-r--r--src/android/app/src/main/jni/native_config.cpp121
-rw-r--r--src/android/app/src/main/jni/native_log.cpp13
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml15
-rw-r--r--src/android/app/src/main/res/menu/menu_overlay_options.xml5
-rw-r--r--src/android/app/src/main/res/values-ar/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-ckb/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-cs/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-de/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-es/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-fr/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-he/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-hu/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-it/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-ja/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-ko/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-nb/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-pl/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-pt-rBR/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-pt-rPT/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-ru/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-uk/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-vi/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-zh-rCN/strings.xml3
-rw-r--r--src/android/app/src/main/res/values-zh-rTW/strings.xml3
-rw-r--r--src/android/app/src/main/res/values/strings.xml5
-rw-r--r--src/common/CMakeLists.txt8
-rw-r--r--src/common/android/android_common.cpp (renamed from src/android/app/src/main/jni/android_common/android_common.cpp)23
-rw-r--r--src/common/android/android_common.h (renamed from src/android/app/src/main/jni/android_common/android_common.h)4
-rw-r--r--src/common/android/applets/software_keyboard.cpp (renamed from src/android/app/src/main/jni/applets/software_keyboard.cpp)24
-rw-r--r--src/common/android/applets/software_keyboard.h (renamed from src/android/app/src/main/jni/applets/software_keyboard.h)4
-rw-r--r--src/common/android/id_cache.cpp (renamed from src/android/app/src/main/jni/id_cache.cpp)12
-rw-r--r--src/common/android/id_cache.h (renamed from src/android/app/src/main/jni/id_cache.h)26
-rw-r--r--src/common/fs/fs_android.cpp167
-rw-r--r--src/common/fs/fs_android.h58
-rw-r--r--src/core/CMakeLists.txt42
-rw-r--r--src/core/file_sys/errors.h1
-rw-r--r--src/core/hle/service/am/applet.h2
-rw-r--r--src/core/hle/service/am/applet_data_broker.h2
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp30
-rw-r--r--src/core/hle/service/bcat/backend/backend.h54
-rw-r--r--src/core/hle/service/bcat/bcat.cpp42
-rw-r--r--src/core/hle/service/bcat/bcat.h9
-rw-r--r--src/core/hle/service/bcat/bcat_module.cpp606
-rw-r--r--src/core/hle/service/bcat/bcat_module.h46
-rw-r--r--src/core/hle/service/bcat/bcat_result.h15
-rw-r--r--src/core/hle/service/bcat/bcat_service.cpp132
-rw-r--r--src/core/hle/service/bcat/bcat_service.h45
-rw-r--r--src/core/hle/service/bcat/bcat_types.h66
-rw-r--r--src/core/hle/service/bcat/bcat_util.h39
-rw-r--r--src/core/hle/service/bcat/delivery_cache_directory_service.cpp80
-rw-r--r--src/core/hle/service/bcat/delivery_cache_directory_service.h33
-rw-r--r--src/core/hle/service/bcat/delivery_cache_file_service.cpp82
-rw-r--r--src/core/hle/service/bcat/delivery_cache_file_service.h33
-rw-r--r--src/core/hle/service/bcat/delivery_cache_progress_service.cpp41
-rw-r--r--src/core/hle/service/bcat/delivery_cache_progress_service.h35
-rw-r--r--src/core/hle/service/bcat/delivery_cache_storage_service.cpp57
-rw-r--r--src/core/hle/service/bcat/delivery_cache_storage_service.h36
-rw-r--r--src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp34
-rw-r--r--src/core/hle/service/bcat/news/newly_arrived_event_holder.h33
-rw-r--r--src/core/hle/service/bcat/news/news_data_service.cpp25
-rw-r--r--src/core/hle/service/bcat/news/news_data_service.h20
-rw-r--r--src/core/hle/service/bcat/news/news_database_service.cpp53
-rw-r--r--src/core/hle/service/bcat/news/news_database_service.h32
-rw-r--r--src/core/hle/service/bcat/news/news_service.cpp57
-rw-r--r--src/core/hle/service/bcat/news/news_service.h28
-rw-r--r--src/core/hle/service/bcat/news/overwrite_event_holder.cpp33
-rw-r--r--src/core/hle/service/bcat/news/overwrite_event_holder.h33
-rw-r--r--src/core/hle/service/bcat/news/service_creator.cpp64
-rw-r--r--src/core/hle/service/bcat/news/service_creator.h35
-rw-r--r--src/core/hle/service/bcat/service_creator.cpp62
-rw-r--r--src/core/hle/service/bcat/service_creator.h40
-rw-r--r--src/core/hle/service/cmif_serialization.h2
-rw-r--r--src/core/hle/service/cmif_types.h10
-rw-r--r--src/core/hle/service/glue/time/worker.cpp130
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp9
-rw-r--r--src/core/hle/service/hle_ipc.cpp8
-rw-r--r--src/core/hle/service/os/event.cpp (renamed from src/core/hle/service/event.cpp)2
-rw-r--r--src/core/hle/service/os/event.h (renamed from src/core/hle/service/event.h)0
-rw-r--r--src/core/hle/service/os/multi_wait.cpp59
-rw-r--r--src/core/hle/service/os/multi_wait.h36
-rw-r--r--src/core/hle/service/os/multi_wait_holder.cpp25
-rw-r--r--src/core/hle/service/os/multi_wait_holder.h44
-rw-r--r--src/core/hle/service/os/multi_wait_utils.h109
-rw-r--r--src/core/hle/service/os/mutex.cpp (renamed from src/core/hle/service/mutex.cpp)2
-rw-r--r--src/core/hle/service/os/mutex.h (renamed from src/core/hle/service/mutex.h)0
-rw-r--r--src/core/hle/service/server_manager.cpp438
-rw-r--r--src/core/hle/service/server_manager.h59
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/memory/cheat_engine.cpp15
-rw-r--r--src/core/memory/cheat_engine.h2
-rw-r--r--src/core/memory/dmnt_cheat_vm.cpp4
-rw-r--r--src/core/memory/dmnt_cheat_vm.h3
-rw-r--r--src/frontend_common/config.cpp10
-rw-r--r--src/hid_core/resource_manager.cpp2
-rw-r--r--src/hid_core/resources/npad/npad.cpp4
-rw-r--r--src/hid_core/resources/npad/npad.h2
-rw-r--r--src/hid_core/resources/npad/npad_resource.cpp10
-rw-r--r--src/hid_core/resources/npad/npad_resource.h2
-rw-r--r--src/video_core/host_shaders/vulkan_present.vert10
-rw-r--r--src/yuzu/multiplayer/lobby_p.h13
130 files changed, 2464 insertions, 1772 deletions
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject ca0e264f4f962e29baa23a3282ce484625866b9 Subproject ba8192d89078af51ae6f97c9352e3683612cdff
diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx
Subproject 404d39004570a26c734a9d1fa29ab4d63089c59 Subproject 97929690234f2b4add36b33657fe3fe09bd57df
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index f011bd696..7890b30ca 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -12,8 +12,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
12 <uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" /> 12 <uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" />
13 13
14 <uses-permission android:name="android.permission.INTERNET" /> 14 <uses-permission android:name="android.permission.INTERNET" />
15 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
16 <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
17 <uses-permission android:name="android.permission.NFC" /> 15 <uses-permission android:name="android.permission.NFC" />
18 <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> 16 <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
19 17
@@ -80,10 +78,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
80 android:resource="@xml/nfc_tech_filter" /> 78 android:resource="@xml/nfc_tech_filter" />
81 </activity> 79 </activity>
82 80
83 <service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse">
84 <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/>
85 </service>
86
87 <provider 81 <provider
88 android:name=".features.DocumentProvider" 82 android:name=".features.DocumentProvider"
89 android:authorities="${applicationId}.user" 83 android:authorities="${applicationId}.user"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
index d114bd53d..76778c10a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
@@ -17,17 +17,6 @@ fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
17 17
18class YuzuApplication : Application() { 18class YuzuApplication : Application() {
19 private fun createNotificationChannels() { 19 private fun createNotificationChannels() {
20 val emulationChannel = NotificationChannel(
21 getString(R.string.emulation_notification_channel_id),
22 getString(R.string.emulation_notification_channel_name),
23 NotificationManager.IMPORTANCE_LOW
24 )
25 emulationChannel.description = getString(
26 R.string.emulation_notification_channel_description
27 )
28 emulationChannel.setSound(null, null)
29 emulationChannel.vibrationPattern = null
30
31 val noticeChannel = NotificationChannel( 20 val noticeChannel = NotificationChannel(
32 getString(R.string.notice_notification_channel_id), 21 getString(R.string.notice_notification_channel_id),
33 getString(R.string.notice_notification_channel_name), 22 getString(R.string.notice_notification_channel_name),
@@ -39,7 +28,6 @@ class YuzuApplication : Application() {
39 // Register the channel with the system; you can't change the importance 28 // Register the channel with the system; you can't change the importance
40 // or other notification behaviors after this 29 // or other notification behaviors after this
41 val notificationManager = getSystemService(NotificationManager::class.java) 30 val notificationManager = getSystemService(NotificationManager::class.java)
42 notificationManager.createNotificationChannel(emulationChannel)
43 notificationManager.createNotificationChannel(noticeChannel) 31 notificationManager.createNotificationChannel(noticeChannel)
44 } 32 }
45 33
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 564aaf305..7a8d03610 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.activities 4package org.yuzu.yuzu_emu.activities
5 5
6import android.annotation.SuppressLint 6import android.annotation.SuppressLint
7import android.app.Activity
8import android.app.PendingIntent 7import android.app.PendingIntent
9import android.app.PictureInPictureParams 8import android.app.PictureInPictureParams
10import android.app.RemoteAction 9import android.app.RemoteAction
@@ -45,7 +44,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
45import org.yuzu.yuzu_emu.features.settings.model.Settings 44import org.yuzu.yuzu_emu.features.settings.model.Settings
46import org.yuzu.yuzu_emu.model.EmulationViewModel 45import org.yuzu.yuzu_emu.model.EmulationViewModel
47import org.yuzu.yuzu_emu.model.Game 46import org.yuzu.yuzu_emu.model.Game
48import org.yuzu.yuzu_emu.utils.ForegroundService
49import org.yuzu.yuzu_emu.utils.InputHandler 47import org.yuzu.yuzu_emu.utils.InputHandler
50import org.yuzu.yuzu_emu.utils.Log 48import org.yuzu.yuzu_emu.utils.Log
51import org.yuzu.yuzu_emu.utils.MemoryUtil 49import org.yuzu.yuzu_emu.utils.MemoryUtil
@@ -74,11 +72,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
74 72
75 private val emulationViewModel: EmulationViewModel by viewModels() 73 private val emulationViewModel: EmulationViewModel by viewModels()
76 74
77 override fun onDestroy() {
78 stopForegroundService(this)
79 super.onDestroy()
80 }
81
82 override fun onCreate(savedInstanceState: Bundle?) { 75 override fun onCreate(savedInstanceState: Bundle?) {
83 Log.gameLaunched = true 76 Log.gameLaunched = true
84 ThemeHelper.setTheme(this) 77 ThemeHelper.setTheme(this)
@@ -125,10 +118,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
125 .apply() 118 .apply()
126 } 119 }
127 } 120 }
128
129 // Start a foreground service to prevent the app from getting killed in the background
130 val startIntent = Intent(this, ForegroundService::class.java)
131 startForegroundService(startIntent)
132 } 121 }
133 122
134 override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { 123 override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
@@ -481,12 +470,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
481 activity.startActivity(launcher) 470 activity.startActivity(launcher)
482 } 471 }
483 472
484 fun stopForegroundService(activity: Activity) {
485 val startIntent = Intent(activity, ForegroundService::class.java)
486 startIntent.action = ForegroundService.ACTION_STOP
487 activity.startForegroundService(startIntent)
488 }
489
490 private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean { 473 private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean {
491 if (view == null) { 474 if (view == null) {
492 return true 475 return true
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index 86bd33672..664478472 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -25,7 +25,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
25 HAPTIC_FEEDBACK("haptic_feedback"), 25 HAPTIC_FEEDBACK("haptic_feedback"),
26 SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"), 26 SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
27 SHOW_INPUT_OVERLAY("show_input_overlay"), 27 SHOW_INPUT_OVERLAY("show_input_overlay"),
28 TOUCHSCREEN("touchscreen"); 28 TOUCHSCREEN("touchscreen"),
29 SHOW_THERMAL_OVERLAY("show_thermal_overlay");
29 30
30 override fun getBoolean(needsGlobal: Boolean): Boolean = 31 override fun getBoolean(needsGlobal: Boolean): Boolean =
31 NativeConfig.getBoolean(key, needsGlobal) 32 NativeConfig.getBoolean(key, needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index d7ab0b5d9..6f6e7be10 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -8,7 +8,6 @@ import android.os.Bundle
8import android.view.LayoutInflater 8import android.view.LayoutInflater
9import android.view.View 9import android.view.View
10import android.view.ViewGroup 10import android.view.ViewGroup
11import android.view.ViewGroup.MarginLayoutParams
12import androidx.core.view.ViewCompat 11import androidx.core.view.ViewCompat
13import androidx.core.view.WindowInsetsCompat 12import androidx.core.view.WindowInsetsCompat
14import androidx.core.view.updatePadding 13import androidx.core.view.updatePadding
@@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.R
27import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding 26import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
28import org.yuzu.yuzu_emu.features.settings.model.Settings 27import org.yuzu.yuzu_emu.features.settings.model.Settings
29import org.yuzu.yuzu_emu.model.SettingsViewModel 28import org.yuzu.yuzu_emu.model.SettingsViewModel
29import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
30 30
31class SettingsFragment : Fragment() { 31class SettingsFragment : Fragment() {
32 private lateinit var presenter: SettingsFragmentPresenter 32 private lateinit var presenter: SettingsFragmentPresenter
@@ -125,18 +125,10 @@ class SettingsFragment : Fragment() {
125 val leftInsets = barInsets.left + cutoutInsets.left 125 val leftInsets = barInsets.left + cutoutInsets.left
126 val rightInsets = barInsets.right + cutoutInsets.right 126 val rightInsets = barInsets.right + cutoutInsets.right
127 127
128 val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams 128 binding.listSettings.updateMargins(left = leftInsets, right = rightInsets)
129 mlpSettingsList.leftMargin = leftInsets 129 binding.listSettings.updatePadding(bottom = barInsets.bottom)
130 mlpSettingsList.rightMargin = rightInsets 130
131 binding.listSettings.layoutParams = mlpSettingsList 131 binding.appbarSettings.updateMargins(left = leftInsets, right = rightInsets)
132 binding.listSettings.updatePadding(
133 bottom = barInsets.bottom
134 )
135
136 val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams
137 mlpAppBar.leftMargin = leftInsets
138 mlpAppBar.rightMargin = rightInsets
139 binding.appbarSettings.layoutParams = mlpAppBar
140 windowInsets 132 windowInsets
141 } 133 }
142 } 134 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
index 5ab38ffda..ff4f0e5df 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
@@ -13,7 +13,6 @@ import android.os.Bundle
13import android.view.LayoutInflater 13import android.view.LayoutInflater
14import android.view.View 14import android.view.View
15import android.view.ViewGroup 15import android.view.ViewGroup
16import android.view.ViewGroup.MarginLayoutParams
17import android.widget.Toast 16import android.widget.Toast
18import androidx.core.view.ViewCompat 17import androidx.core.view.ViewCompat
19import androidx.core.view.WindowInsetsCompat 18import androidx.core.view.WindowInsetsCompat
@@ -26,6 +25,7 @@ import org.yuzu.yuzu_emu.BuildConfig
26import org.yuzu.yuzu_emu.R 25import org.yuzu.yuzu_emu.R
27import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding 26import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
28import org.yuzu.yuzu_emu.model.HomeViewModel 27import org.yuzu.yuzu_emu.model.HomeViewModel
28import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
29 29
30class AboutFragment : Fragment() { 30class AboutFragment : Fragment() {
31 private var _binding: FragmentAboutBinding? = null 31 private var _binding: FragmentAboutBinding? = null
@@ -114,15 +114,8 @@ class AboutFragment : Fragment() {
114 val leftInsets = barInsets.left + cutoutInsets.left 114 val leftInsets = barInsets.left + cutoutInsets.left
115 val rightInsets = barInsets.right + cutoutInsets.right 115 val rightInsets = barInsets.right + cutoutInsets.right
116 116
117 val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams 117 binding.toolbarAbout.updateMargins(left = leftInsets, right = rightInsets)
118 mlpToolbar.leftMargin = leftInsets 118 binding.scrollAbout.updateMargins(left = leftInsets, right = rightInsets)
119 mlpToolbar.rightMargin = rightInsets
120 binding.toolbarAbout.layoutParams = mlpToolbar
121
122 val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
123 mlpScrollAbout.leftMargin = leftInsets
124 mlpScrollAbout.rightMargin = rightInsets
125 binding.scrollAbout.layoutParams = mlpScrollAbout
126 119
127 binding.contentAbout.updatePadding(bottom = barInsets.bottom) 120 binding.contentAbout.updatePadding(bottom = barInsets.bottom)
128 121
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
index adb65812c..f5647fa95 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
@@ -31,6 +31,7 @@ import org.yuzu.yuzu_emu.model.AddonViewModel
31import org.yuzu.yuzu_emu.model.HomeViewModel 31import org.yuzu.yuzu_emu.model.HomeViewModel
32import org.yuzu.yuzu_emu.utils.AddonUtil 32import org.yuzu.yuzu_emu.utils.AddonUtil
33import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo 33import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
34import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
34import java.io.File 35import java.io.File
35 36
36class AddonsFragment : Fragment() { 37class AddonsFragment : Fragment() {
@@ -202,27 +203,19 @@ class AddonsFragment : Fragment() {
202 val leftInsets = barInsets.left + cutoutInsets.left 203 val leftInsets = barInsets.left + cutoutInsets.left
203 val rightInsets = barInsets.right + cutoutInsets.right 204 val rightInsets = barInsets.right + cutoutInsets.right
204 205
205 val mlpToolbar = binding.toolbarAddons.layoutParams as ViewGroup.MarginLayoutParams 206 binding.toolbarAddons.updateMargins(left = leftInsets, right = rightInsets)
206 mlpToolbar.leftMargin = leftInsets 207 binding.listAddons.updateMargins(left = leftInsets, right = rightInsets)
207 mlpToolbar.rightMargin = rightInsets
208 binding.toolbarAddons.layoutParams = mlpToolbar
209
210 val mlpAddonsList = binding.listAddons.layoutParams as ViewGroup.MarginLayoutParams
211 mlpAddonsList.leftMargin = leftInsets
212 mlpAddonsList.rightMargin = rightInsets
213 binding.listAddons.layoutParams = mlpAddonsList
214 binding.listAddons.updatePadding( 208 binding.listAddons.updatePadding(
215 bottom = barInsets.bottom + 209 bottom = barInsets.bottom +
216 resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab) 210 resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
217 ) 211 )
218 212
219 val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) 213 val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
220 val mlpFab = 214 binding.buttonInstall.updateMargins(
221 binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams 215 left = leftInsets + fabSpacing,
222 mlpFab.leftMargin = leftInsets + fabSpacing 216 right = rightInsets + fabSpacing,
223 mlpFab.rightMargin = rightInsets + fabSpacing 217 bottom = barInsets.bottom + fabSpacing
224 mlpFab.bottomMargin = barInsets.bottom + fabSpacing 218 )
225 binding.buttonInstall.layoutParams = mlpFab
226 219
227 windowInsets 220 windowInsets
228 } 221 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt
index 1f66b440d..73ca40484 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AppletLauncherFragment.kt
@@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding
21import org.yuzu.yuzu_emu.model.Applet 21import org.yuzu.yuzu_emu.model.Applet
22import org.yuzu.yuzu_emu.model.AppletInfo 22import org.yuzu.yuzu_emu.model.AppletInfo
23import org.yuzu.yuzu_emu.model.HomeViewModel 23import org.yuzu.yuzu_emu.model.HomeViewModel
24import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
24 25
25class AppletLauncherFragment : Fragment() { 26class AppletLauncherFragment : Fragment() {
26 private var _binding: FragmentAppletLauncherBinding? = null 27 private var _binding: FragmentAppletLauncherBinding? = null
@@ -95,16 +96,8 @@ class AppletLauncherFragment : Fragment() {
95 val leftInsets = barInsets.left + cutoutInsets.left 96 val leftInsets = barInsets.left + cutoutInsets.left
96 val rightInsets = barInsets.right + cutoutInsets.right 97 val rightInsets = barInsets.right + cutoutInsets.right
97 98
98 val mlpAppBar = binding.toolbarApplets.layoutParams as ViewGroup.MarginLayoutParams 99 binding.toolbarApplets.updateMargins(left = leftInsets, right = rightInsets)
99 mlpAppBar.leftMargin = leftInsets 100 binding.listApplets.updateMargins(left = leftInsets, right = rightInsets)
100 mlpAppBar.rightMargin = rightInsets
101 binding.toolbarApplets.layoutParams = mlpAppBar
102
103 val mlpListApplets =
104 binding.listApplets.layoutParams as ViewGroup.MarginLayoutParams
105 mlpListApplets.leftMargin = leftInsets
106 mlpListApplets.rightMargin = rightInsets
107 binding.listApplets.layoutParams = mlpListApplets
108 101
109 binding.listApplets.updatePadding(bottom = barInsets.bottom) 102 binding.listApplets.updatePadding(bottom = barInsets.bottom)
110 103
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
index bf017cd7c..41cff46c1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
@@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
34import org.yuzu.yuzu_emu.utils.FileUtil 34import org.yuzu.yuzu_emu.utils.FileUtil
35import org.yuzu.yuzu_emu.utils.GpuDriverHelper 35import org.yuzu.yuzu_emu.utils.GpuDriverHelper
36import org.yuzu.yuzu_emu.utils.NativeConfig 36import org.yuzu.yuzu_emu.utils.NativeConfig
37import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
37import java.io.File 38import java.io.File
38import java.io.IOException 39import java.io.IOException
39 40
@@ -141,23 +142,15 @@ class DriverManagerFragment : Fragment() {
141 val leftInsets = barInsets.left + cutoutInsets.left 142 val leftInsets = barInsets.left + cutoutInsets.left
142 val rightInsets = barInsets.right + cutoutInsets.right 143 val rightInsets = barInsets.right + cutoutInsets.right
143 144
144 val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams 145 binding.toolbarDrivers.updateMargins(left = leftInsets, right = rightInsets)
145 mlpAppBar.leftMargin = leftInsets 146 binding.listDrivers.updateMargins(left = leftInsets, right = rightInsets)
146 mlpAppBar.rightMargin = rightInsets
147 binding.toolbarDrivers.layoutParams = mlpAppBar
148
149 val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
150 mlplistDrivers.leftMargin = leftInsets
151 mlplistDrivers.rightMargin = rightInsets
152 binding.listDrivers.layoutParams = mlplistDrivers
153 147
154 val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) 148 val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
155 val mlpFab = 149 binding.buttonInstall.updateMargins(
156 binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams 150 left = leftInsets + fabSpacing,
157 mlpFab.leftMargin = leftInsets + fabSpacing 151 right = rightInsets + fabSpacing,
158 mlpFab.rightMargin = rightInsets + fabSpacing 152 bottom = barInsets.bottom + fabSpacing
159 mlpFab.bottomMargin = barInsets.bottom + fabSpacing 153 )
160 binding.buttonInstall.layoutParams = mlpFab
161 154
162 binding.listDrivers.updatePadding( 155 binding.listDrivers.updatePadding(
163 bottom = barInsets.bottom + 156 bottom = barInsets.bottom +
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
index dbc16da4a..0534b68ce 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
@@ -19,6 +19,7 @@ import com.google.android.material.transition.MaterialSharedAxis
19import org.yuzu.yuzu_emu.R 19import org.yuzu.yuzu_emu.R
20import org.yuzu.yuzu_emu.databinding.FragmentEarlyAccessBinding 20import org.yuzu.yuzu_emu.databinding.FragmentEarlyAccessBinding
21import org.yuzu.yuzu_emu.model.HomeViewModel 21import org.yuzu.yuzu_emu.model.HomeViewModel
22import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
22 23
23class EarlyAccessFragment : Fragment() { 24class EarlyAccessFragment : Fragment() {
24 private var _binding: FragmentEarlyAccessBinding? = null 25 private var _binding: FragmentEarlyAccessBinding? = null
@@ -73,10 +74,7 @@ class EarlyAccessFragment : Fragment() {
73 val leftInsets = barInsets.left + cutoutInsets.left 74 val leftInsets = barInsets.left + cutoutInsets.left
74 val rightInsets = barInsets.right + cutoutInsets.right 75 val rightInsets = barInsets.right + cutoutInsets.right
75 76
76 val mlpAppBar = binding.appbarEa.layoutParams as ViewGroup.MarginLayoutParams 77 binding.appbarEa.updateMargins(left = leftInsets, right = rightInsets)
77 mlpAppBar.leftMargin = leftInsets
78 mlpAppBar.rightMargin = rightInsets
79 binding.appbarEa.layoutParams = mlpAppBar
80 78
81 binding.scrollEa.updatePadding( 79 binding.scrollEa.updatePadding(
82 left = leftInsets, 80 left = leftInsets,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 937b8faf1..44af896da 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -13,6 +13,7 @@ import android.net.Uri
13import android.os.Bundle 13import android.os.Bundle
14import android.os.Handler 14import android.os.Handler
15import android.os.Looper 15import android.os.Looper
16import android.os.PowerManager
16import android.os.SystemClock 17import android.os.SystemClock
17import android.view.* 18import android.view.*
18import android.widget.TextView 19import android.widget.TextView
@@ -23,6 +24,7 @@ import androidx.core.content.res.ResourcesCompat
23import androidx.core.graphics.Insets 24import androidx.core.graphics.Insets
24import androidx.core.view.ViewCompat 25import androidx.core.view.ViewCompat
25import androidx.core.view.WindowInsetsCompat 26import androidx.core.view.WindowInsetsCompat
27import androidx.core.view.updatePadding
26import androidx.drawerlayout.widget.DrawerLayout 28import androidx.drawerlayout.widget.DrawerLayout
27import androidx.drawerlayout.widget.DrawerLayout.DrawerListener 29import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
28import androidx.fragment.app.Fragment 30import androidx.fragment.app.Fragment
@@ -38,7 +40,6 @@ import androidx.window.layout.WindowLayoutInfo
38import com.google.android.material.dialog.MaterialAlertDialogBuilder 40import com.google.android.material.dialog.MaterialAlertDialogBuilder
39import com.google.android.material.slider.Slider 41import com.google.android.material.slider.Slider
40import kotlinx.coroutines.Dispatchers 42import kotlinx.coroutines.Dispatchers
41import kotlinx.coroutines.flow.collect
42import kotlinx.coroutines.flow.collectLatest 43import kotlinx.coroutines.flow.collectLatest
43import kotlinx.coroutines.launch 44import kotlinx.coroutines.launch
44import org.yuzu.yuzu_emu.HomeNavigationDirections 45import org.yuzu.yuzu_emu.HomeNavigationDirections
@@ -64,6 +65,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
64 private lateinit var emulationState: EmulationState 65 private lateinit var emulationState: EmulationState
65 private var emulationActivity: EmulationActivity? = null 66 private var emulationActivity: EmulationActivity? = null
66 private var perfStatsUpdater: (() -> Unit)? = null 67 private var perfStatsUpdater: (() -> Unit)? = null
68 private var thermalStatsUpdater: (() -> Unit)? = null
67 69
68 private var _binding: FragmentEmulationBinding? = null 70 private var _binding: FragmentEmulationBinding? = null
69 private val binding get() = _binding!! 71 private val binding get() = _binding!!
@@ -77,6 +79,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
77 79
78 private var isInFoldableLayout = false 80 private var isInFoldableLayout = false
79 81
82 private lateinit var powerManager: PowerManager
83
80 override fun onAttach(context: Context) { 84 override fun onAttach(context: Context) {
81 super.onAttach(context) 85 super.onAttach(context)
82 if (context is EmulationActivity) { 86 if (context is EmulationActivity) {
@@ -102,6 +106,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
102 super.onCreate(savedInstanceState) 106 super.onCreate(savedInstanceState)
103 updateOrientation() 107 updateOrientation()
104 108
109 powerManager = requireContext().getSystemService(Context.POWER_SERVICE) as PowerManager
110
105 val intentUri: Uri? = requireActivity().intent.data 111 val intentUri: Uri? = requireActivity().intent.data
106 var intentGame: Game? = null 112 var intentGame: Game? = null
107 if (intentUri != null) { 113 if (intentUri != null) {
@@ -394,8 +400,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
394 400
395 emulationState.updateSurface() 401 emulationState.updateSurface()
396 402
397 // Setup overlay 403 // Setup overlays
398 updateShowFpsOverlay() 404 updateShowFpsOverlay()
405 updateThermalOverlay()
399 } 406 }
400 } 407 }
401 } 408 }
@@ -553,6 +560,38 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
553 } 560 }
554 } 561 }
555 562
563 private fun updateThermalOverlay() {
564 if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) {
565 thermalStatsUpdater = {
566 if (emulationViewModel.emulationStarted.value &&
567 !emulationViewModel.isEmulationStopping.value
568 ) {
569 val thermalStatus = when (powerManager.currentThermalStatus) {
570 PowerManager.THERMAL_STATUS_LIGHT -> "😥"
571 PowerManager.THERMAL_STATUS_MODERATE -> "🥵"
572 PowerManager.THERMAL_STATUS_SEVERE -> "🔥"
573 PowerManager.THERMAL_STATUS_CRITICAL,
574 PowerManager.THERMAL_STATUS_EMERGENCY,
575 PowerManager.THERMAL_STATUS_SHUTDOWN -> "☢️"
576
577 else -> "🙂"
578 }
579 if (_binding != null) {
580 binding.showThermalsText.text = thermalStatus
581 }
582 thermalStatsUpdateHandler.postDelayed(thermalStatsUpdater!!, 1000)
583 }
584 }
585 thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
586 binding.showThermalsText.visibility = View.VISIBLE
587 } else {
588 if (thermalStatsUpdater != null) {
589 thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
590 }
591 binding.showThermalsText.visibility = View.GONE
592 }
593 }
594
556 @SuppressLint("SourceLockedOrientationActivity") 595 @SuppressLint("SourceLockedOrientationActivity")
557 private fun updateOrientation() { 596 private fun updateOrientation() {
558 emulationActivity?.let { 597 emulationActivity?.let {
@@ -641,6 +680,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
641 popup.menu.apply { 680 popup.menu.apply {
642 findItem(R.id.menu_toggle_fps).isChecked = 681 findItem(R.id.menu_toggle_fps).isChecked =
643 BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() 682 BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
683 findItem(R.id.thermal_indicator).isChecked =
684 BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
644 findItem(R.id.menu_rel_stick_center).isChecked = 685 findItem(R.id.menu_rel_stick_center).isChecked =
645 BooleanSetting.JOYSTICK_REL_CENTER.getBoolean() 686 BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
646 findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean() 687 findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
@@ -660,6 +701,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
660 true 701 true
661 } 702 }
662 703
704 R.id.thermal_indicator -> {
705 it.isChecked = !it.isChecked
706 BooleanSetting.SHOW_THERMAL_OVERLAY.setBoolean(it.isChecked)
707 updateThermalOverlay()
708 true
709 }
710
663 R.id.menu_edit_overlay -> { 711 R.id.menu_edit_overlay -> {
664 binding.drawerLayout.close() 712 binding.drawerLayout.close()
665 binding.surfaceInputOverlay.requestFocus() 713 binding.surfaceInputOverlay.requestFocus()
@@ -850,7 +898,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
850 right = cutInsets.right 898 right = cutInsets.right
851 } 899 }
852 900
853 v.setPadding(left, cutInsets.top, right, 0) 901 v.updatePadding(left = left, top = cutInsets.top, right = right)
854 windowInsets 902 windowInsets
855 } 903 }
856 } 904 }
@@ -1003,5 +1051,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
1003 1051
1004 companion object { 1052 companion object {
1005 private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!) 1053 private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
1054 private val thermalStatsUpdateHandler = Handler(Looper.myLooper()!!)
1006 } 1055 }
1007} 1056}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
index 341a37fdb..5c558b1a5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
@@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding
26import org.yuzu.yuzu_emu.model.GamesViewModel 26import org.yuzu.yuzu_emu.model.GamesViewModel
27import org.yuzu.yuzu_emu.model.HomeViewModel 27import org.yuzu.yuzu_emu.model.HomeViewModel
28import org.yuzu.yuzu_emu.ui.main.MainActivity 28import org.yuzu.yuzu_emu.ui.main.MainActivity
29import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
29 30
30class GameFoldersFragment : Fragment() { 31class GameFoldersFragment : Fragment() {
31 private var _binding: FragmentFoldersBinding? = null 32 private var _binding: FragmentFoldersBinding? = null
@@ -100,23 +101,16 @@ class GameFoldersFragment : Fragment() {
100 val leftInsets = barInsets.left + cutoutInsets.left 101 val leftInsets = barInsets.left + cutoutInsets.left
101 val rightInsets = barInsets.right + cutoutInsets.right 102 val rightInsets = barInsets.right + cutoutInsets.right
102 103
103 val mlpToolbar = binding.toolbarFolders.layoutParams as ViewGroup.MarginLayoutParams 104 binding.toolbarFolders.updateMargins(left = leftInsets, right = rightInsets)
104 mlpToolbar.leftMargin = leftInsets
105 mlpToolbar.rightMargin = rightInsets
106 binding.toolbarFolders.layoutParams = mlpToolbar
107 105
108 val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) 106 val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
109 val mlpFab = 107 binding.buttonAdd.updateMargins(
110 binding.buttonAdd.layoutParams as ViewGroup.MarginLayoutParams 108 left = leftInsets + fabSpacing,
111 mlpFab.leftMargin = leftInsets + fabSpacing 109 right = rightInsets + fabSpacing,
112 mlpFab.rightMargin = rightInsets + fabSpacing 110 bottom = barInsets.bottom + fabSpacing
113 mlpFab.bottomMargin = barInsets.bottom + fabSpacing 111 )
114 binding.buttonAdd.layoutParams = mlpFab 112
115 113 binding.listFolders.updateMargins(left = leftInsets, right = rightInsets)
116 val mlpListFolders = binding.listFolders.layoutParams as ViewGroup.MarginLayoutParams
117 mlpListFolders.leftMargin = leftInsets
118 mlpListFolders.rightMargin = rightInsets
119 binding.listFolders.layoutParams = mlpListFolders
120 114
121 binding.listFolders.updatePadding( 115 binding.listFolders.updatePadding(
122 bottom = barInsets.bottom + 116 bottom = barInsets.bottom +
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
index 5aa3f453f..dbd56e84f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
@@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
27import org.yuzu.yuzu_emu.model.GameVerificationResult 27import org.yuzu.yuzu_emu.model.GameVerificationResult
28import org.yuzu.yuzu_emu.model.HomeViewModel 28import org.yuzu.yuzu_emu.model.HomeViewModel
29import org.yuzu.yuzu_emu.utils.GameMetadata 29import org.yuzu.yuzu_emu.utils.GameMetadata
30import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
30 31
31class GameInfoFragment : Fragment() { 32class GameInfoFragment : Fragment() {
32 private var _binding: FragmentGameInfoBinding? = null 33 private var _binding: FragmentGameInfoBinding? = null
@@ -122,11 +123,13 @@ class GameInfoFragment : Fragment() {
122 titleId = R.string.verify_success, 123 titleId = R.string.verify_success,
123 descriptionId = R.string.operation_completed_successfully 124 descriptionId = R.string.operation_completed_successfully
124 ) 125 )
126
125 GameVerificationResult.Failed -> 127 GameVerificationResult.Failed ->
126 MessageDialogFragment.newInstance( 128 MessageDialogFragment.newInstance(
127 titleId = R.string.verify_failure, 129 titleId = R.string.verify_failure,
128 descriptionId = R.string.verify_failure_description 130 descriptionId = R.string.verify_failure_description
129 ) 131 )
132
130 GameVerificationResult.NotImplemented -> 133 GameVerificationResult.NotImplemented ->
131 MessageDialogFragment.newInstance( 134 MessageDialogFragment.newInstance(
132 titleId = R.string.verify_no_result, 135 titleId = R.string.verify_no_result,
@@ -165,15 +168,8 @@ class GameInfoFragment : Fragment() {
165 val leftInsets = barInsets.left + cutoutInsets.left 168 val leftInsets = barInsets.left + cutoutInsets.left
166 val rightInsets = barInsets.right + cutoutInsets.right 169 val rightInsets = barInsets.right + cutoutInsets.right
167 170
168 val mlpToolbar = binding.toolbarInfo.layoutParams as ViewGroup.MarginLayoutParams 171 binding.toolbarInfo.updateMargins(left = leftInsets, right = rightInsets)
169 mlpToolbar.leftMargin = leftInsets 172 binding.scrollInfo.updateMargins(left = leftInsets, right = rightInsets)
170 mlpToolbar.rightMargin = rightInsets
171 binding.toolbarInfo.layoutParams = mlpToolbar
172
173 val mlpScrollAbout = binding.scrollInfo.layoutParams as ViewGroup.MarginLayoutParams
174 mlpScrollAbout.leftMargin = leftInsets
175 mlpScrollAbout.rightMargin = rightInsets
176 binding.scrollInfo.layoutParams = mlpScrollAbout
177 173
178 binding.contentInfo.updatePadding(bottom = barInsets.bottom) 174 binding.contentInfo.updatePadding(bottom = barInsets.bottom)
179 175
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
index 582df0133..d14b2c634 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
@@ -46,6 +46,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
46import org.yuzu.yuzu_emu.utils.GameIconUtils 46import org.yuzu.yuzu_emu.utils.GameIconUtils
47import org.yuzu.yuzu_emu.utils.GpuDriverHelper 47import org.yuzu.yuzu_emu.utils.GpuDriverHelper
48import org.yuzu.yuzu_emu.utils.MemoryUtil 48import org.yuzu.yuzu_emu.utils.MemoryUtil
49import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
49import java.io.BufferedOutputStream 50import java.io.BufferedOutputStream
50import java.io.File 51import java.io.File
51 52
@@ -320,46 +321,25 @@ class GamePropertiesFragment : Fragment() {
320 321
321 val smallLayout = resources.getBoolean(R.bool.small_layout) 322 val smallLayout = resources.getBoolean(R.bool.small_layout)
322 if (smallLayout) { 323 if (smallLayout) {
323 val mlpListAll = 324 binding.listAll.updateMargins(left = leftInsets, right = rightInsets)
324 binding.listAll.layoutParams as ViewGroup.MarginLayoutParams
325 mlpListAll.leftMargin = leftInsets
326 mlpListAll.rightMargin = rightInsets
327 binding.listAll.layoutParams = mlpListAll
328 } else { 325 } else {
329 if (ViewCompat.getLayoutDirection(binding.root) == 326 if (ViewCompat.getLayoutDirection(binding.root) ==
330 ViewCompat.LAYOUT_DIRECTION_LTR 327 ViewCompat.LAYOUT_DIRECTION_LTR
331 ) { 328 ) {
332 val mlpListAll = 329 binding.listAll.updateMargins(right = rightInsets)
333 binding.listAll.layoutParams as ViewGroup.MarginLayoutParams 330 binding.iconLayout!!.updateMargins(top = barInsets.top, left = leftInsets)
334 mlpListAll.rightMargin = rightInsets
335 binding.listAll.layoutParams = mlpListAll
336
337 val mlpIconLayout =
338 binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams
339 mlpIconLayout.topMargin = barInsets.top
340 mlpIconLayout.leftMargin = leftInsets
341 binding.iconLayout!!.layoutParams = mlpIconLayout
342 } else { 331 } else {
343 val mlpListAll = 332 binding.listAll.updateMargins(left = leftInsets)
344 binding.listAll.layoutParams as ViewGroup.MarginLayoutParams 333 binding.iconLayout!!.updateMargins(top = barInsets.top, right = rightInsets)
345 mlpListAll.leftMargin = leftInsets
346 binding.listAll.layoutParams = mlpListAll
347
348 val mlpIconLayout =
349 binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams
350 mlpIconLayout.topMargin = barInsets.top
351 mlpIconLayout.rightMargin = rightInsets
352 binding.iconLayout!!.layoutParams = mlpIconLayout
353 } 334 }
354 } 335 }
355 336
356 val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) 337 val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
357 val mlpFab = 338 binding.buttonStart.updateMargins(
358 binding.buttonStart.layoutParams as ViewGroup.MarginLayoutParams 339 left = leftInsets + fabSpacing,
359 mlpFab.leftMargin = leftInsets + fabSpacing 340 right = rightInsets + fabSpacing,
360 mlpFab.rightMargin = rightInsets + fabSpacing 341 bottom = barInsets.bottom + fabSpacing
361 mlpFab.bottomMargin = barInsets.bottom + fabSpacing 342 )
362 binding.buttonStart.layoutParams = mlpFab
363 343
364 binding.layoutAll.updatePadding( 344 binding.layoutAll.updatePadding(
365 top = barInsets.top, 345 top = barInsets.top,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 1f3578b22..87e130d3e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -12,7 +12,6 @@ import android.provider.DocumentsContract
12import android.view.LayoutInflater 12import android.view.LayoutInflater
13import android.view.View 13import android.view.View
14import android.view.ViewGroup 14import android.view.ViewGroup
15import android.view.ViewGroup.MarginLayoutParams
16import android.widget.Toast 15import android.widget.Toast
17import androidx.appcompat.app.AppCompatActivity 16import androidx.appcompat.app.AppCompatActivity
18import androidx.core.app.ActivityCompat 17import androidx.core.app.ActivityCompat
@@ -44,6 +43,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
44import org.yuzu.yuzu_emu.utils.FileUtil 43import org.yuzu.yuzu_emu.utils.FileUtil
45import org.yuzu.yuzu_emu.utils.GpuDriverHelper 44import org.yuzu.yuzu_emu.utils.GpuDriverHelper
46import org.yuzu.yuzu_emu.utils.Log 45import org.yuzu.yuzu_emu.utils.Log
46import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
47 47
48class HomeSettingsFragment : Fragment() { 48class HomeSettingsFragment : Fragment() {
49 private var _binding: FragmentHomeSettingsBinding? = null 49 private var _binding: FragmentHomeSettingsBinding? = null
@@ -408,10 +408,7 @@ class HomeSettingsFragment : Fragment() {
408 bottom = barInsets.bottom 408 bottom = barInsets.bottom
409 ) 409 )
410 410
411 val mlpScrollSettings = binding.scrollViewSettings.layoutParams as MarginLayoutParams 411 binding.scrollViewSettings.updateMargins(left = leftInsets, right = rightInsets)
412 mlpScrollSettings.leftMargin = leftInsets
413 mlpScrollSettings.rightMargin = rightInsets
414 binding.scrollViewSettings.layoutParams = mlpScrollSettings
415 412
416 binding.linearLayoutSettings.updatePadding(bottom = spacingNavigation) 413 binding.linearLayoutSettings.updatePadding(bottom = spacingNavigation)
417 414
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
index 7df8e6bf4..63112dc6f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
@@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.model.TaskState
34import org.yuzu.yuzu_emu.ui.main.MainActivity 34import org.yuzu.yuzu_emu.ui.main.MainActivity
35import org.yuzu.yuzu_emu.utils.DirectoryInitialization 35import org.yuzu.yuzu_emu.utils.DirectoryInitialization
36import org.yuzu.yuzu_emu.utils.FileUtil 36import org.yuzu.yuzu_emu.utils.FileUtil
37import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
37import java.io.BufferedOutputStream 38import java.io.BufferedOutputStream
38import java.io.File 39import java.io.File
39import java.math.BigInteger 40import java.math.BigInteger
@@ -172,16 +173,8 @@ class InstallableFragment : Fragment() {
172 val leftInsets = barInsets.left + cutoutInsets.left 173 val leftInsets = barInsets.left + cutoutInsets.left
173 val rightInsets = barInsets.right + cutoutInsets.right 174 val rightInsets = barInsets.right + cutoutInsets.right
174 175
175 val mlpAppBar = binding.toolbarInstallables.layoutParams as ViewGroup.MarginLayoutParams 176 binding.toolbarInstallables.updateMargins(left = leftInsets, right = rightInsets)
176 mlpAppBar.leftMargin = leftInsets 177 binding.listInstallables.updateMargins(left = leftInsets, right = rightInsets)
177 mlpAppBar.rightMargin = rightInsets
178 binding.toolbarInstallables.layoutParams = mlpAppBar
179
180 val mlpScrollAbout =
181 binding.listInstallables.layoutParams as ViewGroup.MarginLayoutParams
182 mlpScrollAbout.leftMargin = leftInsets
183 mlpScrollAbout.rightMargin = rightInsets
184 binding.listInstallables.layoutParams = mlpScrollAbout
185 178
186 binding.listInstallables.updatePadding(bottom = barInsets.bottom) 179 binding.listInstallables.updatePadding(bottom = barInsets.bottom)
187 180
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
index b6e9129f7..f17f621f8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
@@ -7,7 +7,6 @@ import android.os.Bundle
7import android.view.LayoutInflater 7import android.view.LayoutInflater
8import android.view.View 8import android.view.View
9import android.view.ViewGroup 9import android.view.ViewGroup
10import android.view.ViewGroup.MarginLayoutParams
11import androidx.appcompat.app.AppCompatActivity 10import androidx.appcompat.app.AppCompatActivity
12import androidx.core.view.ViewCompat 11import androidx.core.view.ViewCompat
13import androidx.core.view.WindowInsetsCompat 12import androidx.core.view.WindowInsetsCompat
@@ -22,6 +21,7 @@ import org.yuzu.yuzu_emu.adapters.LicenseAdapter
22import org.yuzu.yuzu_emu.databinding.FragmentLicensesBinding 21import org.yuzu.yuzu_emu.databinding.FragmentLicensesBinding
23import org.yuzu.yuzu_emu.model.HomeViewModel 22import org.yuzu.yuzu_emu.model.HomeViewModel
24import org.yuzu.yuzu_emu.model.License 23import org.yuzu.yuzu_emu.model.License
24import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
25 25
26class LicensesFragment : Fragment() { 26class LicensesFragment : Fragment() {
27 private var _binding: FragmentLicensesBinding? = null 27 private var _binding: FragmentLicensesBinding? = null
@@ -122,15 +122,8 @@ class LicensesFragment : Fragment() {
122 val leftInsets = barInsets.left + cutoutInsets.left 122 val leftInsets = barInsets.left + cutoutInsets.left
123 val rightInsets = barInsets.right + cutoutInsets.right 123 val rightInsets = barInsets.right + cutoutInsets.right
124 124
125 val mlpAppBar = binding.appbarLicenses.layoutParams as MarginLayoutParams 125 binding.appbarLicenses.updateMargins(left = leftInsets, right = rightInsets)
126 mlpAppBar.leftMargin = leftInsets 126 binding.listLicenses.updateMargins(left = leftInsets, right = rightInsets)
127 mlpAppBar.rightMargin = rightInsets
128 binding.appbarLicenses.layoutParams = mlpAppBar
129
130 val mlpScrollAbout = binding.listLicenses.layoutParams as MarginLayoutParams
131 mlpScrollAbout.leftMargin = leftInsets
132 mlpScrollAbout.rightMargin = rightInsets
133 binding.listLicenses.layoutParams = mlpScrollAbout
134 127
135 binding.listLicenses.updatePadding(bottom = barInsets.bottom) 128 binding.listLicenses.updatePadding(bottom = barInsets.bottom)
136 129
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
index f95d545bf..a135b80b4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
@@ -29,6 +29,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
29import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 29import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
30import org.yuzu.yuzu_emu.model.SettingsViewModel 30import org.yuzu.yuzu_emu.model.SettingsViewModel
31import org.yuzu.yuzu_emu.utils.NativeConfig 31import org.yuzu.yuzu_emu.utils.NativeConfig
32import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
32 33
33class SettingsSearchFragment : Fragment() { 34class SettingsSearchFragment : Fragment() {
34 private var _binding: FragmentSettingsSearchBinding? = null 35 private var _binding: FragmentSettingsSearchBinding? = null
@@ -174,15 +175,14 @@ class SettingsSearchFragment : Fragment() {
174 bottom = barInsets.bottom 175 bottom = barInsets.bottom
175 ) 176 )
176 177
177 val mlpSettingsList = binding.settingsList.layoutParams as ViewGroup.MarginLayoutParams 178 binding.settingsList.updateMargins(
178 mlpSettingsList.leftMargin = leftInsets + sideMargin 179 left = leftInsets + sideMargin,
179 mlpSettingsList.rightMargin = rightInsets + sideMargin 180 right = rightInsets + sideMargin
180 binding.settingsList.layoutParams = mlpSettingsList 181 )
181 182 binding.divider.updateMargins(
182 val mlpDivider = binding.divider.layoutParams as ViewGroup.MarginLayoutParams 183 left = leftInsets + sideMargin,
183 mlpDivider.leftMargin = leftInsets + sideMargin 184 right = rightInsets + sideMargin
184 mlpDivider.rightMargin = rightInsets + sideMargin 185 )
185 binding.divider.layoutParams = mlpDivider
186 186
187 windowInsets 187 windowInsets
188 } 188 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index 54380323e..23ca49b53 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -8,7 +8,6 @@ import android.os.Bundle
8import android.view.LayoutInflater 8import android.view.LayoutInflater
9import android.view.View 9import android.view.View
10import android.view.ViewGroup 10import android.view.ViewGroup
11import android.view.ViewGroup.MarginLayoutParams
12import androidx.appcompat.app.AppCompatActivity 11import androidx.appcompat.app.AppCompatActivity
13import androidx.core.view.ViewCompat 12import androidx.core.view.ViewCompat
14import androidx.core.view.WindowInsetsCompat 13import androidx.core.view.WindowInsetsCompat
@@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
27import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager 26import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
28import org.yuzu.yuzu_emu.model.GamesViewModel 27import org.yuzu.yuzu_emu.model.GamesViewModel
29import org.yuzu.yuzu_emu.model.HomeViewModel 28import org.yuzu.yuzu_emu.model.HomeViewModel
29import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
30 30
31class GamesFragment : Fragment() { 31class GamesFragment : Fragment() {
32 private var _binding: FragmentGamesBinding? = null 32 private var _binding: FragmentGamesBinding? = null
@@ -169,15 +169,16 @@ class GamesFragment : Fragment() {
169 169
170 val leftInsets = barInsets.left + cutoutInsets.left 170 val leftInsets = barInsets.left + cutoutInsets.left
171 val rightInsets = barInsets.right + cutoutInsets.right 171 val rightInsets = barInsets.right + cutoutInsets.right
172 val mlpSwipe = binding.swipeRefresh.layoutParams as MarginLayoutParams 172 val left: Int
173 val right: Int
173 if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) { 174 if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
174 mlpSwipe.leftMargin = leftInsets + spacingNavigationRail 175 left = leftInsets + spacingNavigationRail
175 mlpSwipe.rightMargin = rightInsets 176 right = rightInsets
176 } else { 177 } else {
177 mlpSwipe.leftMargin = leftInsets 178 left = leftInsets
178 mlpSwipe.rightMargin = rightInsets + spacingNavigationRail 179 right = rightInsets + spacingNavigationRail
179 } 180 }
180 binding.swipeRefresh.layoutParams = mlpSwipe 181 binding.swipeRefresh.updateMargins(left = left, right = right)
181 182
182 binding.noticeText.updatePadding(bottom = spacingNavigation) 183 binding.noticeText.updatePadding(bottom = spacingNavigation)
183 184
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index b3967d294..4df4ac4c6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -34,7 +34,6 @@ import kotlinx.coroutines.launch
34import org.yuzu.yuzu_emu.HomeNavigationDirections 34import org.yuzu.yuzu_emu.HomeNavigationDirections
35import org.yuzu.yuzu_emu.NativeLibrary 35import org.yuzu.yuzu_emu.NativeLibrary
36import org.yuzu.yuzu_emu.R 36import org.yuzu.yuzu_emu.R
37import org.yuzu.yuzu_emu.activities.EmulationActivity
38import org.yuzu.yuzu_emu.databinding.ActivityMainBinding 37import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
39import org.yuzu.yuzu_emu.features.settings.model.Settings 38import org.yuzu.yuzu_emu.features.settings.model.Settings
40import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment 39import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
@@ -177,9 +176,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
177 } 176 }
178 } 177 }
179 178
180 // Dismiss previous notifications (should not happen unless a crash occurred)
181 EmulationActivity.stopForegroundService(this)
182
183 setInsets() 179 setInsets()
184 } 180 }
185 181
@@ -298,11 +294,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
298 super.onResume() 294 super.onResume()
299 } 295 }
300 296
301 override fun onDestroy() {
302 EmulationActivity.stopForegroundService(this)
303 super.onDestroy()
304 }
305
306 private fun setInsets() = 297 private fun setInsets() =
307 ViewCompat.setOnApplyWindowInsetsListener( 298 ViewCompat.setOnApplyWindowInsetsListener(
308 binding.root 299 binding.root
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
deleted file mode 100644
index 086d17606..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
+++ /dev/null
@@ -1,70 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import android.app.PendingIntent
7import android.app.Service
8import android.content.Intent
9import android.os.IBinder
10import androidx.core.app.NotificationCompat
11import androidx.core.app.NotificationManagerCompat
12import org.yuzu.yuzu_emu.R
13import org.yuzu.yuzu_emu.activities.EmulationActivity
14
15/**
16 * A service that shows a permanent notification in the background to avoid the app getting
17 * cleared from memory by the system.
18 */
19class ForegroundService : Service() {
20 companion object {
21 const val EMULATION_RUNNING_NOTIFICATION = 0x1000
22
23 const val ACTION_STOP = "stop"
24 }
25
26 private fun showRunningNotification() {
27 // Intent is used to resume emulation if the notification is clicked
28 val contentIntent = PendingIntent.getActivity(
29 this,
30 0,
31 Intent(this, EmulationActivity::class.java),
32 PendingIntent.FLAG_IMMUTABLE
33 )
34 val builder =
35 NotificationCompat.Builder(this, getString(R.string.emulation_notification_channel_id))
36 .setSmallIcon(R.drawable.ic_stat_notification_logo)
37 .setContentTitle(getString(R.string.app_name))
38 .setContentText(getString(R.string.emulation_notification_running))
39 .setPriority(NotificationCompat.PRIORITY_LOW)
40 .setOngoing(true)
41 .setVibrate(null)
42 .setSound(null)
43 .setContentIntent(contentIntent)
44 startForeground(EMULATION_RUNNING_NOTIFICATION, builder.build())
45 }
46
47 override fun onBind(intent: Intent): IBinder? {
48 return null
49 }
50
51 override fun onCreate() {
52 showRunningNotification()
53 }
54
55 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
56 if (intent == null) {
57 return START_NOT_STICKY
58 }
59 if (intent.action == ACTION_STOP) {
60 NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
61 stopForeground(STOP_FOREGROUND_REMOVE)
62 stopSelfResult(startId)
63 }
64 return START_STICKY
65 }
66
67 override fun onDestroy() {
68 NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
69 }
70}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
index f9a3e4126..ffbfa9337 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
@@ -4,6 +4,7 @@
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import android.view.View 6import android.view.View
7import android.view.ViewGroup
7 8
8object ViewUtils { 9object ViewUtils {
9 fun showView(view: View, length: Long = 300) { 10 fun showView(view: View, length: Long = 300) {
@@ -32,4 +33,28 @@ object ViewUtils {
32 view.visibility = View.INVISIBLE 33 view.visibility = View.INVISIBLE
33 }.start() 34 }.start()
34 } 35 }
36
37 fun View.updateMargins(
38 left: Int = -1,
39 top: Int = -1,
40 right: Int = -1,
41 bottom: Int = -1
42 ) {
43 val layoutParams = this.layoutParams as ViewGroup.MarginLayoutParams
44 layoutParams.apply {
45 if (left != -1) {
46 leftMargin = left
47 }
48 if (top != -1) {
49 topMargin = top
50 }
51 if (right != -1) {
52 rightMargin = right
53 }
54 if (bottom != -1) {
55 bottomMargin = bottom
56 }
57 }
58 this.layoutParams = layoutParams
59 }
35} 60}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index abc6055ab..20b319c12 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -2,14 +2,8 @@
2# SPDX-License-Identifier: GPL-3.0-or-later 2# SPDX-License-Identifier: GPL-3.0-or-later
3 3
4add_library(yuzu-android SHARED 4add_library(yuzu-android SHARED
5 android_common/android_common.cpp
6 android_common/android_common.h
7 applets/software_keyboard.cpp
8 applets/software_keyboard.h
9 emu_window/emu_window.cpp 5 emu_window/emu_window.cpp
10 emu_window/emu_window.h 6 emu_window/emu_window.h
11 id_cache.cpp
12 id_cache.h
13 native.cpp 7 native.cpp
14 native.h 8 native.h
15 native_config.cpp 9 native_config.cpp
diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h
index cf93304da..4a3bc8e53 100644
--- a/src/android/app/src/main/jni/android_settings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -60,6 +60,8 @@ struct Values {
60 Settings::Category::Overlay}; 60 Settings::Category::Overlay};
61 Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay", 61 Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay",
62 Settings::Category::Overlay}; 62 Settings::Category::Overlay};
63 Settings::Setting<bool> show_thermal_overlay{linkage, false, "show_thermal_overlay",
64 Settings::Category::Overlay};
63 Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay", 65 Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
64 Settings::Category::Overlay}; 66 Settings::Category::Overlay};
65 Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay}; 67 Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp
index c4f631924..c927cddda 100644
--- a/src/android/app/src/main/jni/emu_window/emu_window.cpp
+++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp
@@ -3,6 +3,7 @@
3 3
4#include <android/native_window_jni.h> 4#include <android/native_window_jni.h>
5 5
6#include "common/android/id_cache.h"
6#include "common/logging/log.h" 7#include "common/logging/log.h"
7#include "input_common/drivers/touch_screen.h" 8#include "input_common/drivers/touch_screen.h"
8#include "input_common/drivers/virtual_amiibo.h" 9#include "input_common/drivers/virtual_amiibo.h"
@@ -60,7 +61,8 @@ void EmuWindow_Android::OnRemoveNfcTag() {
60 61
61void EmuWindow_Android::OnFrameDisplayed() { 62void EmuWindow_Android::OnFrameDisplayed() {
62 if (!m_first_frame) { 63 if (!m_first_frame) {
63 EmulationSession::GetInstance().OnEmulationStarted(); 64 Common::Android::RunJNIOnFiber<void>(
65 [&](JNIEnv* env) { EmulationSession::GetInstance().OnEmulationStarted(); });
64 m_first_frame = true; 66 m_first_frame = true;
65 } 67 }
66} 68}
diff --git a/src/android/app/src/main/jni/game_metadata.cpp b/src/android/app/src/main/jni/game_metadata.cpp
index 8f0da1413..c33763b47 100644
--- a/src/android/app/src/main/jni/game_metadata.cpp
+++ b/src/android/app/src/main/jni/game_metadata.cpp
@@ -1,13 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/android/android_common.h"
4#include "core/core.h" 5#include "core/core.h"
5#include "core/file_sys/fs_filesystem.h" 6#include "core/file_sys/fs_filesystem.h"
6#include "core/file_sys/patch_manager.h" 7#include "core/file_sys/patch_manager.h"
7#include "core/loader/loader.h" 8#include "core/loader/loader.h"
8#include "core/loader/nro.h" 9#include "core/loader/nro.h"
9#include "jni.h"
10#include "jni/android_common/android_common.h"
11#include "native.h" 10#include "native.h"
12 11
13struct RomMetadata { 12struct RomMetadata {
@@ -79,7 +78,7 @@ extern "C" {
79jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj, 78jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
80 jstring jpath) { 79 jstring jpath) {
81 const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( 80 const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
82 GetJString(env, jpath), FileSys::OpenMode::Read); 81 Common::Android::GetJString(env, jpath), FileSys::OpenMode::Read);
83 if (!file) { 82 if (!file) {
84 return false; 83 return false;
85 } 84 }
@@ -104,27 +103,31 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobj
104 103
105jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj, 104jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj,
106 jstring jpath) { 105 jstring jpath) {
107 return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title); 106 return Common::Android::ToJString(
107 env, GetRomMetadata(Common::Android::GetJString(env, jpath)).title);
108} 108}
109 109
110jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj, 110jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj,
111 jstring jpath) { 111 jstring jpath) {
112 return ToJString(env, std::to_string(GetRomMetadata(GetJString(env, jpath)).programId)); 112 return Common::Android::ToJString(
113 env, std::to_string(GetRomMetadata(Common::Android::GetJString(env, jpath)).programId));
113} 114}
114 115
115jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj, 116jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj,
116 jstring jpath) { 117 jstring jpath) {
117 return ToJString(env, GetRomMetadata(GetJString(env, jpath)).developer); 118 return Common::Android::ToJString(
119 env, GetRomMetadata(Common::Android::GetJString(env, jpath)).developer);
118} 120}
119 121
120jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj, 122jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj,
121 jstring jpath, jboolean jreload) { 123 jstring jpath, jboolean jreload) {
122 return ToJString(env, GetRomMetadata(GetJString(env, jpath), jreload).version); 124 return Common::Android::ToJString(
125 env, GetRomMetadata(Common::Android::GetJString(env, jpath), jreload).version);
123} 126}
124 127
125jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj, 128jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj,
126 jstring jpath) { 129 jstring jpath) {
127 auto icon_data = GetRomMetadata(GetJString(env, jpath)).icon; 130 auto icon_data = GetRomMetadata(Common::Android::GetJString(env, jpath)).icon;
128 jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); 131 jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
129 env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), 132 env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
130 reinterpret_cast<jbyte*>(icon_data.data())); 133 reinterpret_cast<jbyte*>(icon_data.data()));
@@ -133,7 +136,8 @@ jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobje
133 136
134jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj, 137jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj,
135 jstring jpath) { 138 jstring jpath) {
136 return static_cast<jboolean>(GetRomMetadata(GetJString(env, jpath)).isHomebrew); 139 return static_cast<jboolean>(
140 GetRomMetadata(Common::Android::GetJString(env, jpath)).isHomebrew);
137} 141}
138 142
139void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) { 143void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) {
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 654510129..4acc60956 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -20,6 +20,8 @@
20#include <frontend_common/content_manager.h> 20#include <frontend_common/content_manager.h>
21#include <jni.h> 21#include <jni.h>
22 22
23#include "common/android/android_common.h"
24#include "common/android/id_cache.h"
23#include "common/detached_tasks.h" 25#include "common/detached_tasks.h"
24#include "common/dynamic_library.h" 26#include "common/dynamic_library.h"
25#include "common/fs/path_util.h" 27#include "common/fs/path_util.h"
@@ -57,8 +59,6 @@
57#include "hid_core/frontend/emulated_controller.h" 59#include "hid_core/frontend/emulated_controller.h"
58#include "hid_core/hid_core.h" 60#include "hid_core/hid_core.h"
59#include "hid_core/hid_types.h" 61#include "hid_core/hid_types.h"
60#include "jni/android_common/android_common.h"
61#include "jni/id_cache.h"
62#include "jni/native.h" 62#include "jni/native.h"
63#include "video_core/renderer_base.h" 63#include "video_core/renderer_base.h"
64#include "video_core/renderer_vulkan/renderer_vulkan.h" 64#include "video_core/renderer_vulkan/renderer_vulkan.h"
@@ -228,7 +228,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
228 std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library); 228 std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library);
229 229
230 // Initialize system. 230 // Initialize system.
231 jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); 231 jauto android_keyboard = std::make_unique<Common::Android::SoftwareKeyboard::AndroidKeyboard>();
232 m_software_keyboard = android_keyboard.get(); 232 m_software_keyboard = android_keyboard.get();
233 m_system.SetShuttingDown(false); 233 m_system.SetShuttingDown(false);
234 m_system.ApplySettings(); 234 m_system.ApplySettings();
@@ -411,37 +411,39 @@ void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
411 controller->Disconnect(); 411 controller->Disconnect();
412} 412}
413 413
414SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { 414Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
415 return m_software_keyboard; 415 return m_software_keyboard;
416} 416}
417 417
418void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, 418void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress,
419 int max) { 419 int max) {
420 JNIEnv* env = IDCache::GetEnvForThread(); 420 JNIEnv* env = Common::Android::GetEnvForThread();
421 env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), 421 env->CallStaticVoidMethod(Common::Android::GetDiskCacheProgressClass(),
422 IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage), 422 Common::Android::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
423 static_cast<jint>(progress), static_cast<jint>(max)); 423 static_cast<jint>(progress), static_cast<jint>(max));
424} 424}
425 425
426void EmulationSession::OnEmulationStarted() { 426void EmulationSession::OnEmulationStarted() {
427 JNIEnv* env = IDCache::GetEnvForThread(); 427 JNIEnv* env = Common::Android::GetEnvForThread();
428 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted()); 428 env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
429 Common::Android::GetOnEmulationStarted());
429} 430}
430 431
431void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { 432void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
432 JNIEnv* env = IDCache::GetEnvForThread(); 433 JNIEnv* env = Common::Android::GetEnvForThread();
433 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(), 434 env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
434 static_cast<jint>(result)); 435 Common::Android::GetOnEmulationStopped(), static_cast<jint>(result));
435} 436}
436 437
437void EmulationSession::ChangeProgram(std::size_t program_index) { 438void EmulationSession::ChangeProgram(std::size_t program_index) {
438 JNIEnv* env = IDCache::GetEnvForThread(); 439 JNIEnv* env = Common::Android::GetEnvForThread();
439 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnProgramChanged(), 440 env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
441 Common::Android::GetOnProgramChanged(),
440 static_cast<jint>(program_index)); 442 static_cast<jint>(program_index));
441} 443}
442 444
443u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { 445u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
444 auto program_id_string = GetJString(env, jprogramId); 446 auto program_id_string = Common::Android::GetJString(env, jprogramId);
445 try { 447 try {
446 return std::stoull(program_id_string); 448 return std::stoull(program_id_string);
447 } catch (...) { 449 } catch (...) {
@@ -491,7 +493,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject
491 493
492void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance, 494void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance,
493 [[maybe_unused]] jstring j_directory) { 495 [[maybe_unused]] jstring j_directory) {
494 Common::FS::SetAppDirectory(GetJString(env, j_directory)); 496 Common::FS::SetAppDirectory(Common::Android::GetJString(env, j_directory));
495} 497}
496 498
497int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance, 499int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
@@ -501,21 +503,22 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
501 jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); 503 jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
502 const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { 504 const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
503 auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, 505 auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
504 ToJDouble(env, max), ToJDouble(env, progress)); 506 Common::Android::ToJDouble(env, max),
505 return GetJBoolean(env, jwasCancelled); 507 Common::Android::ToJDouble(env, progress));
508 return Common::Android::GetJBoolean(env, jwasCancelled);
506 }; 509 };
507 510
508 return static_cast<int>( 511 return static_cast<int>(
509 ContentManager::InstallNSP(EmulationSession::GetInstance().System(), 512 ContentManager::InstallNSP(EmulationSession::GetInstance().System(),
510 *EmulationSession::GetInstance().System().GetFilesystem(), 513 *EmulationSession::GetInstance().System().GetFilesystem(),
511 GetJString(env, j_file), callback)); 514 Common::Android::GetJString(env, j_file), callback));
512} 515}
513 516
514jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj, 517jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
515 jstring jprogramId, 518 jstring jprogramId,
516 jstring jupdatePath) { 519 jstring jupdatePath) {
517 u64 program_id = EmulationSession::GetProgramId(env, jprogramId); 520 u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
518 std::string updatePath = GetJString(env, jupdatePath); 521 std::string updatePath = Common::Android::GetJString(env, jupdatePath);
519 std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( 522 std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
520 EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( 523 EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
521 updatePath, FileSys::OpenMode::Read)); 524 updatePath, FileSys::OpenMode::Read));
@@ -538,8 +541,10 @@ void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* e
538 jstring custom_driver_name, 541 jstring custom_driver_name,
539 jstring file_redirect_dir) { 542 jstring file_redirect_dir) {
540 EmulationSession::GetInstance().InitializeGpuDriver( 543 EmulationSession::GetInstance().InitializeGpuDriver(
541 GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), 544 Common::Android::GetJString(env, hook_lib_dir),
542 GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); 545 Common::Android::GetJString(env, custom_driver_dir),
546 Common::Android::GetJString(env, custom_driver_name),
547 Common::Android::GetJString(env, file_redirect_dir));
543} 548}
544 549
545[[maybe_unused]] static bool CheckKgslPresent() { 550[[maybe_unused]] static bool CheckKgslPresent() {
@@ -566,7 +571,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
566 JNIEnv* env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) { 571 JNIEnv* env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) {
567 const char* file_redirect_dir_{}; 572 const char* file_redirect_dir_{};
568 int featureFlags{}; 573 int featureFlags{};
569 std::string hook_lib_dir = GetJString(env, j_hook_lib_dir); 574 std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir);
570 auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), 575 auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
571 nullptr, nullptr, file_redirect_dir_, nullptr); 576 nullptr, nullptr, file_redirect_dir_, nullptr);
572 auto driver_library = std::make_shared<Common::DynamicLibrary>(handle); 577 auto driver_library = std::make_shared<Common::DynamicLibrary>(handle);
@@ -587,9 +592,10 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
587 fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version), 592 fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version),
588 VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version)); 593 VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version));
589 594
590 jobjectArray j_driver_info = 595 jobjectArray j_driver_info = env->NewObjectArray(
591 env->NewObjectArray(2, IDCache::GetStringClass(), ToJString(env, version_string)); 596 2, Common::Android::GetStringClass(), Common::Android::ToJString(env, version_string));
592 env->SetObjectArrayElement(j_driver_info, 1, ToJString(env, device.GetDriverName())); 597 env->SetObjectArrayElement(j_driver_info, 1,
598 Common::Android::ToJString(env, device.GetDriverName()));
593 return j_driver_info; 599 return j_driver_info;
594} 600}
595 601
@@ -742,15 +748,15 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jcl
742 748
743jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) { 749jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) {
744 if (Settings::IsNceEnabled()) { 750 if (Settings::IsNceEnabled()) {
745 return ToJString(env, "NCE"); 751 return Common::Android::ToJString(env, "NCE");
746 } 752 }
747 753
748 return ToJString(env, "JIT"); 754 return Common::Android::ToJString(env, "JIT");
749} 755}
750 756
751jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) { 757jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) {
752 return ToJString(env, 758 return Common::Android::ToJString(
753 EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor()); 759 env, EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor());
754} 760}
755 761
756void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) { 762void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
@@ -764,13 +770,14 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj
764void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path, 770void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path,
765 jint j_program_index, 771 jint j_program_index,
766 jboolean j_frontend_initiated) { 772 jboolean j_frontend_initiated) {
767 const std::string path = GetJString(env, j_path); 773 const std::string path = Common::Android::GetJString(env, j_path);
768 774
769 const Core::SystemResultStatus result{ 775 const Core::SystemResultStatus result{
770 RunEmulation(path, j_program_index, j_frontend_initiated)}; 776 RunEmulation(path, j_program_index, j_frontend_initiated)};
771 if (result != Core::SystemResultStatus::Success) { 777 if (result != Core::SystemResultStatus::Success) {
772 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), 778 env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
773 IDCache::GetExitEmulationActivity(), static_cast<int>(result)); 779 Common::Android::GetExitEmulationActivity(),
780 static_cast<int>(result));
774 } 781 }
775} 782}
776 783
@@ -781,7 +788,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass cla
781 788
782void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardText(JNIEnv* env, jclass clazz, 789void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardText(JNIEnv* env, jclass clazz,
783 jstring j_text) { 790 jstring j_text) {
784 const std::u16string input = Common::UTF8ToUTF16(GetJString(env, j_text)); 791 const std::u16string input = Common::UTF8ToUTF16(Common::Android::GetJString(env, j_text));
785 EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardText(input); 792 EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardText(input);
786} 793}
787 794
@@ -815,16 +822,16 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, j
815 auto bis_system = 822 auto bis_system =
816 EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents(); 823 EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents();
817 if (!bis_system) { 824 if (!bis_system) {
818 return ToJString(env, ""); 825 return Common::Android::ToJString(env, "");
819 } 826 }
820 827
821 auto applet_nca = 828 auto applet_nca =
822 bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program); 829 bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program);
823 if (!applet_nca) { 830 if (!applet_nca) {
824 return ToJString(env, ""); 831 return Common::Android::ToJString(env, "");
825 } 832 }
826 833
827 return ToJString(env, applet_nca->GetFullPath()); 834 return Common::Android::ToJString(env, applet_nca->GetFullPath());
828} 835}
829 836
830void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz, 837void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz,
@@ -857,7 +864,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env,
857jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj, 864jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj,
858 jstring jpath, 865 jstring jpath,
859 jstring jprogramId) { 866 jstring jprogramId) {
860 const auto path = GetJString(env, jpath); 867 const auto path = Common::Android::GetJString(env, jpath);
861 const auto vFile = 868 const auto vFile =
862 Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path); 869 Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
863 if (vFile == nullptr) { 870 if (vFile == nullptr) {
@@ -875,14 +882,15 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
875 882
876 auto patches = pm.GetPatches(update_raw); 883 auto patches = pm.GetPatches(update_raw);
877 jobjectArray jpatchArray = 884 jobjectArray jpatchArray =
878 env->NewObjectArray(patches.size(), IDCache::GetPatchClass(), nullptr); 885 env->NewObjectArray(patches.size(), Common::Android::GetPatchClass(), nullptr);
879 int i = 0; 886 int i = 0;
880 for (const auto& patch : patches) { 887 for (const auto& patch : patches) {
881 jobject jpatch = env->NewObject( 888 jobject jpatch = env->NewObject(
882 IDCache::GetPatchClass(), IDCache::GetPatchConstructor(), patch.enabled, 889 Common::Android::GetPatchClass(), Common::Android::GetPatchConstructor(), patch.enabled,
883 ToJString(env, patch.name), ToJString(env, patch.version), 890 Common::Android::ToJString(env, patch.name),
884 static_cast<jint>(patch.type), ToJString(env, std::to_string(patch.program_id)), 891 Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type),
885 ToJString(env, std::to_string(patch.title_id))); 892 Common::Android::ToJString(env, std::to_string(patch.program_id)),
893 Common::Android::ToJString(env, std::to_string(patch.title_id)));
886 env->SetObjectArrayElement(jpatchArray, i, jpatch); 894 env->SetObjectArrayElement(jpatchArray, i, jpatch);
887 ++i; 895 ++i;
888 } 896 }
@@ -906,7 +914,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj,
906 jstring jname) { 914 jstring jname) {
907 auto program_id = EmulationSession::GetProgramId(env, jprogramId); 915 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
908 ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(), 916 ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(),
909 program_id, GetJString(env, jname)); 917 program_id, Common::Android::GetJString(env, jname));
910} 918}
911 919
912jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, 920jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env,
@@ -917,17 +925,18 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEn
917 jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); 925 jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
918 const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { 926 const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
919 auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, 927 auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
920 ToJDouble(env, max), ToJDouble(env, progress)); 928 Common::Android::ToJDouble(env, max),
921 return GetJBoolean(env, jwasCancelled); 929 Common::Android::ToJDouble(env, progress));
930 return Common::Android::GetJBoolean(env, jwasCancelled);
922 }; 931 };
923 932
924 auto& session = EmulationSession::GetInstance(); 933 auto& session = EmulationSession::GetInstance();
925 std::vector<std::string> result = ContentManager::VerifyInstalledContents( 934 std::vector<std::string> result = ContentManager::VerifyInstalledContents(
926 session.System(), *session.GetContentProvider(), callback); 935 session.System(), *session.GetContentProvider(), callback);
927 jobjectArray jresult = 936 jobjectArray jresult = env->NewObjectArray(result.size(), Common::Android::GetStringClass(),
928 env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, "")); 937 Common::Android::ToJString(env, ""));
929 for (size_t i = 0; i < result.size(); ++i) { 938 for (size_t i = 0; i < result.size(); ++i) {
930 env->SetObjectArrayElement(jresult, i, ToJString(env, result[i])); 939 env->SetObjectArrayElement(jresult, i, Common::Android::ToJString(env, result[i]));
931 } 940 }
932 return jresult; 941 return jresult;
933} 942}
@@ -939,19 +948,20 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje
939 jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); 948 jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
940 const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { 949 const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
941 auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, 950 auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
942 ToJDouble(env, max), ToJDouble(env, progress)); 951 Common::Android::ToJDouble(env, max),
943 return GetJBoolean(env, jwasCancelled); 952 Common::Android::ToJDouble(env, progress));
953 return Common::Android::GetJBoolean(env, jwasCancelled);
944 }; 954 };
945 auto& session = EmulationSession::GetInstance(); 955 auto& session = EmulationSession::GetInstance();
946 return static_cast<jint>( 956 return static_cast<jint>(ContentManager::VerifyGameContents(
947 ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback)); 957 session.System(), Common::Android::GetJString(env, jpath), callback));
948} 958}
949 959
950jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, 960jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
951 jstring jprogramId) { 961 jstring jprogramId) {
952 auto program_id = EmulationSession::GetProgramId(env, jprogramId); 962 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
953 if (program_id == 0) { 963 if (program_id == 0) {
954 return ToJString(env, ""); 964 return Common::Android::ToJString(env, "");
955 } 965 }
956 966
957 auto& system = EmulationSession::GetInstance().System(); 967 auto& system = EmulationSession::GetInstance().System();
@@ -968,7 +978,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
968 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( 978 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
969 {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 979 {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
970 program_id, user_id->AsU128(), 0); 980 program_id, user_id->AsU128(), 0);
971 return ToJString(env, user_save_data_path); 981 return Common::Android::ToJString(env, user_save_data_path);
972} 982}
973 983
974jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIEnv* env, 984jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIEnv* env,
@@ -981,12 +991,13 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIE
981 991
982 const auto user_save_data_root = 992 const auto user_save_data_root =
983 FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture); 993 FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture);
984 return ToJString(env, user_save_data_root); 994 return Common::Android::ToJString(env, user_save_data_root);
985} 995}
986 996
987void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj, 997void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj,
988 jstring jpath) { 998 jstring jpath) {
989 EmulationSession::GetInstance().ConfigureFilesystemProvider(GetJString(env, jpath)); 999 EmulationSession::GetInstance().ConfigureFilesystemProvider(
1000 Common::Android::GetJString(env, jpath));
990} 1001}
991 1002
992void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, jobject jobj) { 1003void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, jobject jobj) {
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h
index e49d4e015..47936e305 100644
--- a/src/android/app/src/main/jni/native.h
+++ b/src/android/app/src/main/jni/native.h
@@ -2,13 +2,13 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <android/native_window_jni.h> 4#include <android/native_window_jni.h>
5#include "common/android/applets/software_keyboard.h"
5#include "common/detached_tasks.h" 6#include "common/detached_tasks.h"
6#include "core/core.h" 7#include "core/core.h"
7#include "core/file_sys/registered_cache.h" 8#include "core/file_sys/registered_cache.h"
8#include "core/hle/service/acc/profile_manager.h" 9#include "core/hle/service/acc/profile_manager.h"
9#include "core/perf_stats.h" 10#include "core/perf_stats.h"
10#include "frontend_common/content_manager.h" 11#include "frontend_common/content_manager.h"
11#include "jni/applets/software_keyboard.h"
12#include "jni/emu_window/emu_window.h" 12#include "jni/emu_window/emu_window.h"
13#include "video_core/rasterizer_interface.h" 13#include "video_core/rasterizer_interface.h"
14 14
@@ -54,7 +54,7 @@ public:
54 void SetDeviceType([[maybe_unused]] int index, int type); 54 void SetDeviceType([[maybe_unused]] int index, int type);
55 void OnGamepadConnectEvent([[maybe_unused]] int index); 55 void OnGamepadConnectEvent([[maybe_unused]] int index);
56 void OnGamepadDisconnectEvent([[maybe_unused]] int index); 56 void OnGamepadDisconnectEvent([[maybe_unused]] int index);
57 SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); 57 Common::Android::SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
58 58
59 static void OnEmulationStarted(); 59 static void OnEmulationStarted();
60 60
@@ -79,7 +79,7 @@ private:
79 Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; 79 Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
80 std::atomic<bool> m_is_running = false; 80 std::atomic<bool> m_is_running = false;
81 std::atomic<bool> m_is_paused = false; 81 std::atomic<bool> m_is_paused = false;
82 SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; 82 Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
83 std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; 83 std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
84 int m_applet_id{1}; 84 int m_applet_id{1};
85 85
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index c6c3343dc..8ae10fbc7 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -8,11 +8,11 @@
8 8
9#include "android_config.h" 9#include "android_config.h"
10#include "android_settings.h" 10#include "android_settings.h"
11#include "common/android/android_common.h"
12#include "common/android/id_cache.h"
11#include "common/logging/log.h" 13#include "common/logging/log.h"
12#include "common/settings.h" 14#include "common/settings.h"
13#include "frontend_common/config.h" 15#include "frontend_common/config.h"
14#include "jni/android_common/android_common.h"
15#include "jni/id_cache.h"
16#include "native.h" 16#include "native.h"
17 17
18std::unique_ptr<AndroidConfig> global_config; 18std::unique_ptr<AndroidConfig> global_config;
@@ -20,7 +20,7 @@ std::unique_ptr<AndroidConfig> per_game_config;
20 20
21template <typename T> 21template <typename T>
22Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { 22Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
23 auto key = GetJString(env, jkey); 23 auto key = Common::Android::GetJString(env, jkey);
24 auto basic_setting = Settings::values.linkage.by_key[key]; 24 auto basic_setting = Settings::values.linkage.by_key[key];
25 if (basic_setting != 0) { 25 if (basic_setting != 0) {
26 return static_cast<Settings::Setting<T>*>(basic_setting); 26 return static_cast<Settings::Setting<T>*>(basic_setting);
@@ -55,7 +55,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv*
55 jstring jprogramId, 55 jstring jprogramId,
56 jstring jfileName) { 56 jstring jfileName) {
57 auto program_id = EmulationSession::GetProgramId(env, jprogramId); 57 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
58 auto file_name = GetJString(env, jfileName); 58 auto file_name = Common::Android::GetJString(env, jfileName);
59 const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id); 59 const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
60 per_game_config = 60 per_game_config =
61 std::make_unique<AndroidConfig>(config_file_name, Config::ConfigType::PerGameConfig); 61 std::make_unique<AndroidConfig>(config_file_name, Config::ConfigType::PerGameConfig);
@@ -186,9 +186,9 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getString(JNIEnv* env, jobjec
186 jboolean needGlobal) { 186 jboolean needGlobal) {
187 auto setting = getSetting<std::string>(env, jkey); 187 auto setting = getSetting<std::string>(env, jkey);
188 if (setting == nullptr) { 188 if (setting == nullptr) {
189 return ToJString(env, ""); 189 return Common::Android::ToJString(env, "");
190 } 190 }
191 return ToJString(env, setting->GetValue(static_cast<bool>(needGlobal))); 191 return Common::Android::ToJString(env, setting->GetValue(static_cast<bool>(needGlobal)));
192} 192}
193 193
194void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey, 194void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey,
@@ -198,7 +198,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject o
198 return; 198 return;
199 } 199 }
200 200
201 setting->SetValue(GetJString(env, value)); 201 setting->SetValue(Common::Android::GetJString(env, value));
202} 202}
203 203
204jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj, 204jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj,
@@ -214,13 +214,13 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* e
214 jstring jkey) { 214 jstring jkey) {
215 auto setting = getSetting<std::string>(env, jkey); 215 auto setting = getSetting<std::string>(env, jkey);
216 if (setting == nullptr) { 216 if (setting == nullptr) {
217 return ToJString(env, ""); 217 return Common::Android::ToJString(env, "");
218 } 218 }
219 if (setting->PairedSetting() == nullptr) { 219 if (setting->PairedSetting() == nullptr) {
220 return ToJString(env, ""); 220 return Common::Android::ToJString(env, "");
221 } 221 }
222 222
223 return ToJString(env, setting->PairedSetting()->GetLabel()); 223 return Common::Android::ToJString(env, setting->PairedSetting()->GetLabel());
224} 224}
225 225
226jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsSwitchable(JNIEnv* env, jobject obj, 226jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsSwitchable(JNIEnv* env, jobject obj,
@@ -262,21 +262,21 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDefaultToString(JNIEnv* en
262 jstring jkey) { 262 jstring jkey) {
263 auto setting = getSetting<std::string>(env, jkey); 263 auto setting = getSetting<std::string>(env, jkey);
264 if (setting != nullptr) { 264 if (setting != nullptr) {
265 return ToJString(env, setting->DefaultToString()); 265 return Common::Android::ToJString(env, setting->DefaultToString());
266 } 266 }
267 return ToJString(env, ""); 267 return Common::Android::ToJString(env, "");
268} 268}
269 269
270jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) { 270jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) {
271 jclass gameDirClass = IDCache::GetGameDirClass(); 271 jclass gameDirClass = Common::Android::GetGameDirClass();
272 jmethodID gameDirConstructor = IDCache::GetGameDirConstructor(); 272 jmethodID gameDirConstructor = Common::Android::GetGameDirConstructor();
273 jobjectArray jgameDirArray = 273 jobjectArray jgameDirArray =
274 env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr); 274 env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr);
275 for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) { 275 for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
276 jobject jgameDir = 276 jobject jgameDir = env->NewObject(
277 env->NewObject(gameDirClass, gameDirConstructor, 277 gameDirClass, gameDirConstructor,
278 ToJString(env, AndroidSettings::values.game_dirs[i].path), 278 Common::Android::ToJString(env, AndroidSettings::values.game_dirs[i].path),
279 static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan)); 279 static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan));
280 env->SetObjectArrayElement(jgameDirArray, i, jgameDir); 280 env->SetObjectArrayElement(jgameDirArray, i, jgameDir);
281 } 281 }
282 return jgameDirArray; 282 return jgameDirArray;
@@ -292,14 +292,14 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject
292 } 292 }
293 293
294 jobject dir = env->GetObjectArrayElement(gameDirs, 0); 294 jobject dir = env->GetObjectArrayElement(gameDirs, 0);
295 jclass gameDirClass = IDCache::GetGameDirClass(); 295 jclass gameDirClass = Common::Android::GetGameDirClass();
296 jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;"); 296 jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
297 jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); 297 jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
298 for (int i = 0; i < size; ++i) { 298 for (int i = 0; i < size; ++i) {
299 dir = env->GetObjectArrayElement(gameDirs, i); 299 dir = env->GetObjectArrayElement(gameDirs, i);
300 jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField)); 300 jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField));
301 jboolean jdeepScanBoolean = env->GetBooleanField(dir, deepScanBooleanField); 301 jboolean jdeepScanBoolean = env->GetBooleanField(dir, deepScanBooleanField);
302 std::string uriString = GetJString(env, juriString); 302 std::string uriString = Common::Android::GetJString(env, juriString);
303 AndroidSettings::values.game_dirs.push_back( 303 AndroidSettings::values.game_dirs.push_back(
304 AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); 304 AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)});
305 } 305 }
@@ -307,13 +307,13 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject
307 307
308void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject obj, 308void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject obj,
309 jobject gameDir) { 309 jobject gameDir) {
310 jclass gameDirClass = IDCache::GetGameDirClass(); 310 jclass gameDirClass = Common::Android::GetGameDirClass();
311 jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;"); 311 jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
312 jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); 312 jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
313 313
314 jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField)); 314 jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField));
315 jboolean jdeepScanBoolean = env->GetBooleanField(gameDir, deepScanBooleanField); 315 jboolean jdeepScanBoolean = env->GetBooleanField(gameDir, deepScanBooleanField);
316 std::string uriString = GetJString(env, juriString); 316 std::string uriString = Common::Android::GetJString(env, juriString);
317 AndroidSettings::values.game_dirs.push_back( 317 AndroidSettings::values.game_dirs.push_back(
318 AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); 318 AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)});
319} 319}
@@ -323,9 +323,11 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv
323 auto program_id = EmulationSession::GetProgramId(env, jprogramId); 323 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
324 auto& disabledAddons = Settings::values.disabled_addons[program_id]; 324 auto& disabledAddons = Settings::values.disabled_addons[program_id];
325 jobjectArray jdisabledAddonsArray = 325 jobjectArray jdisabledAddonsArray =
326 env->NewObjectArray(disabledAddons.size(), IDCache::GetStringClass(), ToJString(env, "")); 326 env->NewObjectArray(disabledAddons.size(), Common::Android::GetStringClass(),
327 Common::Android::ToJString(env, ""));
327 for (size_t i = 0; i < disabledAddons.size(); ++i) { 328 for (size_t i = 0; i < disabledAddons.size(); ++i) {
328 env->SetObjectArrayElement(jdisabledAddonsArray, i, ToJString(env, disabledAddons[i])); 329 env->SetObjectArrayElement(jdisabledAddonsArray, i,
330 Common::Android::ToJString(env, disabledAddons[i]));
329 } 331 }
330 return jdisabledAddonsArray; 332 return jdisabledAddonsArray;
331} 333}
@@ -339,7 +341,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, j
339 const int size = env->GetArrayLength(jdisabledAddons); 341 const int size = env->GetArrayLength(jdisabledAddons);
340 for (int i = 0; i < size; ++i) { 342 for (int i = 0; i < size; ++i) {
341 auto jaddon = static_cast<jstring>(env->GetObjectArrayElement(jdisabledAddons, i)); 343 auto jaddon = static_cast<jstring>(env->GetObjectArrayElement(jdisabledAddons, i));
342 disabled_addons.push_back(GetJString(env, jaddon)); 344 disabled_addons.push_back(Common::Android::GetJString(env, jaddon));
343 } 345 }
344 Settings::values.disabled_addons[program_id] = disabled_addons; 346 Settings::values.disabled_addons[program_id] = disabled_addons;
345} 347}
@@ -348,26 +350,27 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JN
348 jobject obj) { 350 jobject obj) {
349 jobjectArray joverlayControlDataArray = 351 jobjectArray joverlayControlDataArray =
350 env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(), 352 env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(),
351 IDCache::GetOverlayControlDataClass(), nullptr); 353 Common::Android::GetOverlayControlDataClass(), nullptr);
352 for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) { 354 for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
353 const auto& control_data = AndroidSettings::values.overlay_control_data[i]; 355 const auto& control_data = AndroidSettings::values.overlay_control_data[i];
354 jobject jlandscapePosition = 356 jobject jlandscapePosition =
355 env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), 357 env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
356 ToJDouble(env, control_data.landscape_position.first), 358 Common::Android::ToJDouble(env, control_data.landscape_position.first),
357 ToJDouble(env, control_data.landscape_position.second)); 359 Common::Android::ToJDouble(env, control_data.landscape_position.second));
358 jobject jportraitPosition = 360 jobject jportraitPosition =
359 env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), 361 env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
360 ToJDouble(env, control_data.portrait_position.first), 362 Common::Android::ToJDouble(env, control_data.portrait_position.first),
361 ToJDouble(env, control_data.portrait_position.second)); 363 Common::Android::ToJDouble(env, control_data.portrait_position.second));
362 jobject jfoldablePosition = 364 jobject jfoldablePosition =
363 env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), 365 env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
364 ToJDouble(env, control_data.foldable_position.first), 366 Common::Android::ToJDouble(env, control_data.foldable_position.first),
365 ToJDouble(env, control_data.foldable_position.second)); 367 Common::Android::ToJDouble(env, control_data.foldable_position.second));
366 368
367 jobject jcontrolData = env->NewObject( 369 jobject jcontrolData =
368 IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(), 370 env->NewObject(Common::Android::GetOverlayControlDataClass(),
369 ToJString(env, control_data.id), control_data.enabled, jlandscapePosition, 371 Common::Android::GetOverlayControlDataConstructor(),
370 jportraitPosition, jfoldablePosition); 372 Common::Android::ToJString(env, control_data.id), control_data.enabled,
373 jlandscapePosition, jportraitPosition, jfoldablePosition);
371 env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData); 374 env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
372 } 375 }
373 return joverlayControlDataArray; 376 return joverlayControlDataArray;
@@ -384,33 +387,41 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
384 387
385 for (int i = 0; i < size; ++i) { 388 for (int i = 0; i < size; ++i) {
386 jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i); 389 jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i);
387 jstring jidString = static_cast<jstring>( 390 jstring jidString = static_cast<jstring>(env->GetObjectField(
388 env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField())); 391 joverlayControlData, Common::Android::GetOverlayControlDataIdField()));
389 bool enabled = static_cast<bool>(env->GetBooleanField( 392 bool enabled = static_cast<bool>(env->GetBooleanField(
390 joverlayControlData, IDCache::GetOverlayControlDataEnabledField())); 393 joverlayControlData, Common::Android::GetOverlayControlDataEnabledField()));
391 394
392 jobject jlandscapePosition = env->GetObjectField( 395 jobject jlandscapePosition = env->GetObjectField(
393 joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField()); 396 joverlayControlData, Common::Android::GetOverlayControlDataLandscapePositionField());
394 std::pair<double, double> landscape_position = std::make_pair( 397 std::pair<double, double> landscape_position = std::make_pair(
395 GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())), 398 Common::Android::GetJDouble(
396 GetJDouble(env, 399 env, env->GetObjectField(jlandscapePosition, Common::Android::GetPairFirstField())),
397 env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField()))); 400 Common::Android::GetJDouble(
401 env,
402 env->GetObjectField(jlandscapePosition, Common::Android::GetPairSecondField())));
398 403
399 jobject jportraitPosition = env->GetObjectField( 404 jobject jportraitPosition = env->GetObjectField(
400 joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField()); 405 joverlayControlData, Common::Android::GetOverlayControlDataPortraitPositionField());
401 std::pair<double, double> portrait_position = std::make_pair( 406 std::pair<double, double> portrait_position = std::make_pair(
402 GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())), 407 Common::Android::GetJDouble(
403 GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField()))); 408 env, env->GetObjectField(jportraitPosition, Common::Android::GetPairFirstField())),
409 Common::Android::GetJDouble(
410 env,
411 env->GetObjectField(jportraitPosition, Common::Android::GetPairSecondField())));
404 412
405 jobject jfoldablePosition = env->GetObjectField( 413 jobject jfoldablePosition = env->GetObjectField(
406 joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField()); 414 joverlayControlData, Common::Android::GetOverlayControlDataFoldablePositionField());
407 std::pair<double, double> foldable_position = std::make_pair( 415 std::pair<double, double> foldable_position = std::make_pair(
408 GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())), 416 Common::Android::GetJDouble(
409 GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField()))); 417 env, env->GetObjectField(jfoldablePosition, Common::Android::GetPairFirstField())),
418 Common::Android::GetJDouble(
419 env,
420 env->GetObjectField(jfoldablePosition, Common::Android::GetPairSecondField())));
410 421
411 AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{ 422 AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{
412 GetJString(env, jidString), enabled, landscape_position, portrait_position, 423 Common::Android::GetJString(env, jidString), enabled, landscape_position,
413 foldable_position}); 424 portrait_position, foldable_position});
414 } 425 }
415} 426}
416 427
diff --git a/src/android/app/src/main/jni/native_log.cpp b/src/android/app/src/main/jni/native_log.cpp
index 33d691dc8..95dd1f057 100644
--- a/src/android/app/src/main/jni/native_log.cpp
+++ b/src/android/app/src/main/jni/native_log.cpp
@@ -1,31 +1,30 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <common/android/android_common.h>
4#include <common/logging/log.h> 5#include <common/logging/log.h>
5#include <jni.h> 6#include <jni.h>
6 7
7#include "android_common/android_common.h"
8
9extern "C" { 8extern "C" {
10 9
11void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) { 10void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) {
12 LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage)); 11 LOG_DEBUG(Frontend, "{}", Common::Android::GetJString(env, jmessage));
13} 12}
14 13
15void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) { 14void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) {
16 LOG_WARNING(Frontend, "{}", GetJString(env, jmessage)); 15 LOG_WARNING(Frontend, "{}", Common::Android::GetJString(env, jmessage));
17} 16}
18 17
19void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) { 18void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) {
20 LOG_INFO(Frontend, "{}", GetJString(env, jmessage)); 19 LOG_INFO(Frontend, "{}", Common::Android::GetJString(env, jmessage));
21} 20}
22 21
23void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) { 22void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) {
24 LOG_ERROR(Frontend, "{}", GetJString(env, jmessage)); 23 LOG_ERROR(Frontend, "{}", Common::Android::GetJString(env, jmessage));
25} 24}
26 25
27void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) { 26void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) {
28 LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage)); 27 LOG_CRITICAL(Frontend, "{}", Common::Android::GetJString(env, jmessage));
29} 28}
30 29
31} // extern "C" 30} // extern "C"
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml
index 0d2bfe8d6..e99a15783 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -140,6 +140,7 @@
140 android:id="@+id/overlay_container" 140 android:id="@+id/overlay_container"
141 android:layout_width="match_parent" 141 android:layout_width="match_parent"
142 android:layout_height="match_parent" 142 android:layout_height="match_parent"
143 android:layout_marginHorizontal="20dp"
143 android:fitsSystemWindows="true"> 144 android:fitsSystemWindows="true">
144 145
145 <com.google.android.material.textview.MaterialTextView 146 <com.google.android.material.textview.MaterialTextView
@@ -150,7 +151,19 @@
150 android:layout_gravity="left" 151 android:layout_gravity="left"
151 android:clickable="false" 152 android:clickable="false"
152 android:focusable="false" 153 android:focusable="false"
153 android:paddingHorizontal="20dp" 154 android:textColor="@android:color/white"
155 android:shadowColor="@android:color/black"
156 android:shadowRadius="3"
157 tools:ignore="RtlHardcoded" />
158
159 <com.google.android.material.textview.MaterialTextView
160 android:id="@+id/show_thermals_text"
161 style="@style/TextAppearance.Material3.BodySmall"
162 android:layout_width="wrap_content"
163 android:layout_height="wrap_content"
164 android:layout_gravity="right"
165 android:clickable="false"
166 android:focusable="false"
154 android:textColor="@android:color/white" 167 android:textColor="@android:color/white"
155 android:shadowColor="@android:color/black" 168 android:shadowColor="@android:color/black"
156 android:shadowRadius="3" 169 android:shadowRadius="3"
diff --git a/src/android/app/src/main/res/menu/menu_overlay_options.xml b/src/android/app/src/main/res/menu/menu_overlay_options.xml
index 363781652..a9e807427 100644
--- a/src/android/app/src/main/res/menu/menu_overlay_options.xml
+++ b/src/android/app/src/main/res/menu/menu_overlay_options.xml
@@ -7,6 +7,11 @@
7 android:checkable="true" /> 7 android:checkable="true" />
8 8
9 <item 9 <item
10 android:id="@+id/thermal_indicator"
11 android:title="@string/emulation_thermal_indicator"
12 android:checkable="true" />
13
14 <item
10 android:id="@+id/menu_edit_overlay" 15 android:id="@+id/menu_edit_overlay"
11 android:title="@string/emulation_touch_overlay_edit" /> 16 android:title="@string/emulation_touch_overlay_edit" />
12 17
diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml
index 53678f465..41d741847 100644
--- a/src/android/app/src/main/res/values-ar/strings.xml
+++ b/src/android/app/src/main/res/values-ar/strings.xml
@@ -1,9 +1,6 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="emulation_notification_channel_name">المحاكي نشط</string>
5 <string name="emulation_notification_channel_description">اظهار اشعار دائم عندما يكون المحاكي نشطاً</string>
6 <string name="emulation_notification_running">يوزو قيد التشغيل</string>
7 <string name="notice_notification_channel_name">الإشعارات والأخطاء</string> 4 <string name="notice_notification_channel_name">الإشعارات والأخطاء</string>
8 <string name="notice_notification_channel_description">اظهار اشعار عند حصول اي مشكلة.</string> 5 <string name="notice_notification_channel_description">اظهار اشعار عند حصول اي مشكلة.</string>
9 <string name="notification_permission_not_granted">لم يتم منح إذن الإشعار</string> 6 <string name="notification_permission_not_granted">لم يتم منح إذن الإشعار</string>
diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml
index 7e1eb2b8d..827339505 100644
--- a/src/android/app/src/main/res/values-ckb/strings.xml
+++ b/src/android/app/src/main/res/values-ckb/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">ئەم نەرمەکاڵایە یارییەکانی کۆنسۆلی نینتێندۆ سویچ کارپێدەکات. هیچ ناونیشانێکی یاری و کلیلی تێدا نییە..&lt;br /&gt;&lt;br /&gt;پێش ئەوەی دەست پێ بکەیت، تکایە شوێنی فایلی <![CDATA[<b> prod.keys </b>]]> دیاریبکە لە نێو کۆگای ئامێرەکەت.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">زیاتر فێربە</a>]]></string> 4 <string name="app_disclaimer">ئەم نەرمەکاڵایە یارییەکانی کۆنسۆلی نینتێندۆ سویچ کارپێدەکات. هیچ ناونیشانێکی یاری و کلیلی تێدا نییە..&lt;br /&gt;&lt;br /&gt;پێش ئەوەی دەست پێ بکەیت، تکایە شوێنی فایلی <![CDATA[<b> prod.keys </b>]]> دیاریبکە لە نێو کۆگای ئامێرەکەت.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">زیاتر فێربە</a>]]></string>
5 <string name="emulation_notification_channel_name">ئیمولەیشن کارایە</string>
6 <string name="emulation_notification_channel_description">ئاگادارکردنەوەیەکی بەردەوام نیشان دەدات کاتێک ئیمولەیشن کاردەکات.</string>
7 <string name="emulation_notification_running">یوزو کاردەکات</string>
8 <string name="notice_notification_channel_name">ئاگاداری و هەڵەکان</string> 5 <string name="notice_notification_channel_name">ئاگاداری و هەڵەکان</string>
9 <string name="notice_notification_channel_description">ئاگادارکردنەوەکان پیشان دەدات کاتێک شتێک بە هەڵەدا دەچێت.</string> 6 <string name="notice_notification_channel_description">ئاگادارکردنەوەکان پیشان دەدات کاتێک شتێک بە هەڵەدا دەچێت.</string>
10 <string name="notification_permission_not_granted">مۆڵەتی ئاگادارکردنەوە نەدراوە!</string> 7 <string name="notification_permission_not_granted">مۆڵەتی ئاگادارکردنەوە نەدراوە!</string>
diff --git a/src/android/app/src/main/res/values-cs/strings.xml b/src/android/app/src/main/res/values-cs/strings.xml
index b9a4a11e4..8f8e2848d 100644
--- a/src/android/app/src/main/res/values-cs/strings.xml
+++ b/src/android/app/src/main/res/values-cs/strings.xml
@@ -1,7 +1,6 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="emulation_notification_channel_name">Emulace je aktivní</string>
5 <string name="notice_notification_channel_name">Upozornění a chyby</string> 4 <string name="notice_notification_channel_name">Upozornění a chyby</string>
6 <string name="notice_notification_channel_description">Ukáže oznámení v případě chyby.</string> 5 <string name="notice_notification_channel_description">Ukáže oznámení v případě chyby.</string>
7 <string name="notification_permission_not_granted">Oznámení nejsou oprávněna!</string> 6 <string name="notification_permission_not_granted">Oznámení nejsou oprávněna!</string>
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml
index 483ea8c88..fb25b3c93 100644
--- a/src/android/app/src/main/res/values-de/strings.xml
+++ b/src/android/app/src/main/res/values-de/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.&lt;br /&gt;&lt;br /&gt;Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string> 4 <string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.&lt;br /&gt;&lt;br /&gt;Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string>
5 <string name="emulation_notification_channel_name">Emulation ist aktiv</string>
6 <string name="emulation_notification_channel_description">Zeigt eine dauerhafte Benachrichtigung an, wenn die Emulation läuft.</string>
7 <string name="emulation_notification_running">yuzu läuft</string>
8 <string name="notice_notification_channel_name">Hinweise und Fehler</string> 5 <string name="notice_notification_channel_name">Hinweise und Fehler</string>
9 <string name="notice_notification_channel_description">Zeigt Benachrichtigungen an, wenn etwas schief läuft.</string> 6 <string name="notice_notification_channel_description">Zeigt Benachrichtigungen an, wenn etwas schief läuft.</string>
10 <string name="notification_permission_not_granted">Berechtigung für Benachrichtigungen nicht erlaubt!</string> 7 <string name="notification_permission_not_granted">Berechtigung für Benachrichtigungen nicht erlaubt!</string>
diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml
index c3825710b..7ecbeaba4 100644
--- a/src/android/app/src/main/res/values-es/strings.xml
+++ b/src/android/app/src/main/res/values-es/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o claves no vienen incluidos.&lt;br /&gt;&lt;br /&gt;Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string> 4 <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o claves no vienen incluidos.&lt;br /&gt;&lt;br /&gt;Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string>
5 <string name="emulation_notification_channel_name">Emulación activa</string>
6 <string name="emulation_notification_channel_description">Muestra una notificación persistente cuando la emulación está activa.</string>
7 <string name="emulation_notification_running">yuzu está ejecutándose</string>
8 <string name="notice_notification_channel_name">Avisos y errores</string> 5 <string name="notice_notification_channel_name">Avisos y errores</string>
9 <string name="notice_notification_channel_description">Mostrar notificaciones cuándo algo vaya mal.</string> 6 <string name="notice_notification_channel_description">Mostrar notificaciones cuándo algo vaya mal.</string>
10 <string name="notification_permission_not_granted">¡Permisos de notificación no concedidos!</string> 7 <string name="notification_permission_not_granted">¡Permisos de notificación no concedidos!</string>
diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml
index 667fe33cb..a848b9163 100644
--- a/src/android/app/src/main/res/values-fr/strings.xml
+++ b/src/android/app/src/main/res/values-fr/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.&lt;br /&gt;&lt;br /&gt;Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string> 4 <string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.&lt;br /&gt;&lt;br /&gt;Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string>
5 <string name="emulation_notification_channel_name">L\'émulation est active</string>
6 <string name="emulation_notification_channel_description">Affiche une notification persistante lorsque l\'émulation est en cours d\'exécution.</string>
7 <string name="emulation_notification_running">yuzu est en cours d\'exécution</string>
8 <string name="notice_notification_channel_name">Avis et erreurs</string> 5 <string name="notice_notification_channel_name">Avis et erreurs</string>
9 <string name="notice_notification_channel_description">Affiche des notifications en cas de problème.</string> 6 <string name="notice_notification_channel_description">Affiche des notifications en cas de problème.</string>
10 <string name="notification_permission_not_granted">Permission de notification non accordée !</string> 7 <string name="notification_permission_not_granted">Permission de notification non accordée !</string>
diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml
index 41e4450c6..6096605a9 100644
--- a/src/android/app/src/main/res/values-he/strings.xml
+++ b/src/android/app/src/main/res/values-he/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">התוכנה תריץ משחקים לקונסולת ה Nintendo Switch. אף משחק או קבצים בעלי זכויות יוצרים נכללים.&lt;br /&gt;&lt;br /&gt; לפני שאת/ה מתחיל בבקשה מצא את קובץ <![CDATA[<b>prod.keys</b>]]> על המכשיר.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">קרא עוד</a>]]></string> 4 <string name="app_disclaimer">התוכנה תריץ משחקים לקונסולת ה Nintendo Switch. אף משחק או קבצים בעלי זכויות יוצרים נכללים.&lt;br /&gt;&lt;br /&gt; לפני שאת/ה מתחיל בבקשה מצא את קובץ <![CDATA[<b>prod.keys</b>]]> על המכשיר.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">קרא עוד</a>]]></string>
5 <string name="emulation_notification_channel_name">אמולציה פעילה</string>
6 <string name="emulation_notification_channel_description">מציג התראה מתמשכת כאשר האמולציה פועלת.</string>
7 <string name="emulation_notification_running">yuzu רץ</string>
8 <string name="notice_notification_channel_name">התראות ותקלות</string> 5 <string name="notice_notification_channel_name">התראות ותקלות</string>
9 <string name="notice_notification_channel_description">מציג התראות כאשר משהו הולך לא כשורה.</string> 6 <string name="notice_notification_channel_description">מציג התראות כאשר משהו הולך לא כשורה.</string>
10 <string name="notification_permission_not_granted">הרשאות התראות לא ניתנה!</string> 7 <string name="notification_permission_not_granted">הרשאות התראות לא ניתנה!</string>
diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml
index 554da0816..f3a29e0c3 100644
--- a/src/android/app/src/main/res/values-hu/strings.xml
+++ b/src/android/app/src/main/res/values-hu/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Ez a szoftver Nintendo Switch játékkonzolhoz készült játékokat futtat. Nem tartalmaz játékokat vagy kulcsokat. .&lt;br /&gt;&lt;br /&gt;Mielőtt hozzákezdenél, kérjük, válaszd ki a <![CDATA[<b>prod.keys</b>]]> fájl helyét a készülék tárhelyén&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tudj meg többet</a>]]></string> 4 <string name="app_disclaimer">Ez a szoftver Nintendo Switch játékkonzolhoz készült játékokat futtat. Nem tartalmaz játékokat vagy kulcsokat. .&lt;br /&gt;&lt;br /&gt;Mielőtt hozzákezdenél, kérjük, válaszd ki a <![CDATA[<b>prod.keys</b>]]> fájl helyét a készülék tárhelyén&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tudj meg többet</a>]]></string>
5 <string name="emulation_notification_channel_name">Emuláció aktív</string>
6 <string name="emulation_notification_channel_description">Állandó értesítést jelenít meg, amíg az emuláció fut.</string>
7 <string name="emulation_notification_running">A yuzu fut</string>
8 <string name="notice_notification_channel_name">Megjegyzések és hibák</string> 5 <string name="notice_notification_channel_name">Megjegyzések és hibák</string>
9 <string name="notice_notification_channel_description">Értesítések megjelenítése, ha valami rosszul sül el.</string> 6 <string name="notice_notification_channel_description">Értesítések megjelenítése, ha valami rosszul sül el.</string>
10 <string name="notification_permission_not_granted">Nincs engedély az értesítés megjelenítéséhez!</string> 7 <string name="notification_permission_not_granted">Nincs engedély az értesítés megjelenítéséhez!</string>
diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml
index 61b39f57f..433d84f5c 100644
--- a/src/android/app/src/main/res/values-it/strings.xml
+++ b/src/android/app/src/main/res/values-it/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.&lt;br /&gt;&lt;br /&gt;Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string> 4 <string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.&lt;br /&gt;&lt;br /&gt;Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string>
5 <string name="emulation_notification_channel_name">L\'emulatore è attivo</string>
6 <string name="emulation_notification_channel_description">Mostra una notifica persistente quando l\'emulatore è in esecuzione.</string>
7 <string name="emulation_notification_running">yuzu è in esecuzione</string>
8 <string name="notice_notification_channel_name">Avvisi ed errori</string> 5 <string name="notice_notification_channel_name">Avvisi ed errori</string>
9 <string name="notice_notification_channel_description">Mostra le notifiche quando qualcosa va storto.</string> 6 <string name="notice_notification_channel_description">Mostra le notifiche quando qualcosa va storto.</string>
10 <string name="notification_permission_not_granted">Autorizzazione di notifica non concessa!</string> 7 <string name="notification_permission_not_granted">Autorizzazione di notifica non concessa!</string>
diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml
index 0cff40bb6..da73ad651 100644
--- a/src/android/app/src/main/res/values-ja/strings.xml
+++ b/src/android/app/src/main/res/values-ja/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">このソフトウェアでは、Nintendo Switchのゲームを実行できます。 ゲームソフトやキーは含まれません。&lt;br /&gt;&lt;br /&gt;事前に、 <![CDATA[<b> prod.keys </b>]]> ファイルをストレージに配置しておいてください。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string> 4 <string name="app_disclaimer">このソフトウェアでは、Nintendo Switchのゲームを実行できます。 ゲームソフトやキーは含まれません。&lt;br /&gt;&lt;br /&gt;事前に、 <![CDATA[<b> prod.keys </b>]]> ファイルをストレージに配置しておいてください。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string>
5 <string name="emulation_notification_channel_name">エミュレーションが有効です</string>
6 <string name="emulation_notification_channel_description">エミュレーションの実行中に常設通知を表示します。</string>
7 <string name="emulation_notification_running">yuzu は実行中です</string>
8 <string name="notice_notification_channel_name">通知とエラー</string> 5 <string name="notice_notification_channel_name">通知とエラー</string>
9 <string name="notice_notification_channel_description">問題の発生時に通知を表示します。</string> 6 <string name="notice_notification_channel_description">問題の発生時に通知を表示します。</string>
10 <string name="notification_permission_not_granted">通知が許可されていません!</string> 7 <string name="notification_permission_not_granted">通知が許可されていません!</string>
diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml
index eaa6c23ce..904353d34 100644
--- a/src/android/app/src/main/res/values-ko/strings.xml
+++ b/src/android/app/src/main/res/values-ko/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">이 소프트웨어는 Nintendo Switch 게임을 실행합니다. 게임 타이틀이나 키는 포함되어 있지 않습니다.&lt;br /&gt;&lt;br /&gt;시작하기 전에 장치 저장소에서 <![CDATA[<b> prod.keys </b>]]> 파일을 찾아주세요.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">자세히 알아보기</a>]]></string> 4 <string name="app_disclaimer">이 소프트웨어는 Nintendo Switch 게임을 실행합니다. 게임 타이틀이나 키는 포함되어 있지 않습니다.&lt;br /&gt;&lt;br /&gt;시작하기 전에 장치 저장소에서 <![CDATA[<b> prod.keys </b>]]> 파일을 찾아주세요.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">자세히 알아보기</a>]]></string>
5 <string name="emulation_notification_channel_name">에뮬레이션이 활성화됨</string>
6 <string name="emulation_notification_channel_description">에뮬레이션이 실행 중일 때 지속적으로 알림을 표시합니다.</string>
7 <string name="emulation_notification_running">yuzu가 실행 중입니다.</string>
8 <string name="notice_notification_channel_name">알림 및 오류</string> 5 <string name="notice_notification_channel_name">알림 및 오류</string>
9 <string name="notice_notification_channel_description">문제가 발생하면 알림을 표시합니다.</string> 6 <string name="notice_notification_channel_description">문제가 발생하면 알림을 표시합니다.</string>
10 <string name="notification_permission_not_granted">알림 권한이 부여되지 않았습니다!</string> 7 <string name="notification_permission_not_granted">알림 권한이 부여되지 않았습니다!</string>
diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml
index e92dc62d9..fe3af5920 100644
--- a/src/android/app/src/main/res/values-nb/strings.xml
+++ b/src/android/app/src/main/res/values-nb/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.&lt;br /&gt;&lt;br /&gt;Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string> 4 <string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.&lt;br /&gt;&lt;br /&gt;Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string>
5 <string name="emulation_notification_channel_name">Emulering er aktiv</string>
6 <string name="emulation_notification_channel_description">Viser et vedvarende varsel når emuleringen kjører.</string>
7 <string name="emulation_notification_running">Yuzu kjører</string>
8 <string name="notice_notification_channel_name">Merknader og feil</string> 5 <string name="notice_notification_channel_name">Merknader og feil</string>
9 <string name="notice_notification_channel_description">Viser varsler når noe går galt.</string> 6 <string name="notice_notification_channel_description">Viser varsler når noe går galt.</string>
10 <string name="notification_permission_not_granted">Varslingstillatelse ikke gitt!</string> 7 <string name="notification_permission_not_granted">Varslingstillatelse ikke gitt!</string>
diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml
index fbd0ad7e9..2af7fd7b4 100644
--- a/src/android/app/src/main/res/values-pl/strings.xml
+++ b/src/android/app/src/main/res/values-pl/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.&lt;br /&gt;&lt;br /&gt;Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string> 4 <string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.&lt;br /&gt;&lt;br /&gt;Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string>
5 <string name="emulation_notification_channel_name">Emulacja jest uruchomiona</string>
6 <string name="emulation_notification_channel_description">Pokaż trwałe powiadomienie gdy emulacja jest uruchomiona.</string>
7 <string name="emulation_notification_running">yuzu jest uruchomiony</string>
8 <string name="notice_notification_channel_name">Powiadomienia błędy</string> 5 <string name="notice_notification_channel_name">Powiadomienia błędy</string>
9 <string name="notice_notification_channel_description">Pokaż powiadomienie gdy coś pójdzie źle</string> 6 <string name="notice_notification_channel_description">Pokaż powiadomienie gdy coś pójdzie źle</string>
10 <string name="notification_permission_not_granted">Nie zezwolono na powiadomienia!</string> 7 <string name="notification_permission_not_granted">Nie zezwolono na powiadomienia!</string>
diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml
index a87eb11e4..130252590 100644
--- a/src/android/app/src/main/res/values-pt-rBR/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Este software executa jogos do console Nintendo Switch. Não estão inclusos nem jogos ou chaves.&lt;br /&gt;&lt;br /&gt;Antes de começar, por favor localize o arquivo <![CDATA[<b> prod.keys </b>]]> no armazenamento de seu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saiba mais</a>]]></string> 4 <string name="app_disclaimer">Este software executa jogos do console Nintendo Switch. Não estão inclusos nem jogos ou chaves.&lt;br /&gt;&lt;br /&gt;Antes de começar, por favor localize o arquivo <![CDATA[<b> prod.keys </b>]]> no armazenamento de seu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saiba mais</a>]]></string>
5 <string name="emulation_notification_channel_name">A emulação está Ativa</string>
6 <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação estiver em andamento.</string>
7 <string name="emulation_notification_running">O Yuzu está em execução </string>
8 <string name="notice_notification_channel_name">Notificações e erros</string> 5 <string name="notice_notification_channel_name">Notificações e erros</string>
9 <string name="notice_notification_channel_description">Mostra notificações quando algo dá errado.</string> 6 <string name="notice_notification_channel_description">Mostra notificações quando algo dá errado.</string>
10 <string name="notification_permission_not_granted">Acesso às notificações não concedido!</string> 7 <string name="notification_permission_not_granted">Acesso às notificações não concedido!</string>
diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml
index 684a71616..0fdbae4f8 100644
--- a/src/android/app/src/main/res/values-pt-rPT/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. &lt;br /&gt;&lt;br /&gt;Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[2Learn more2]]></string> 4 <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. &lt;br /&gt;&lt;br /&gt;Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[2Learn more2]]></string>
5 <string name="emulation_notification_channel_name">Emulação está Ativa</string>
6 <string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação está a correr.</string>
7 <string name="emulation_notification_running">Yuzu está em execução </string>
8 <string name="notice_notification_channel_name">Notificações e erros</string> 5 <string name="notice_notification_channel_name">Notificações e erros</string>
9 <string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string> 6 <string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string>
10 <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string> 7 <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string>
diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml
index 099b2c9eb..2dfd4a824 100644
--- a/src/android/app/src/main/res/values-ru/strings.xml
+++ b/src/android/app/src/main/res/values-ru/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Это программное обеспечение позволяет запускать игры для игровой консоли Nintendo Switch. Мы не предоставляем сами игры или ключи.&lt;br /&gt;&lt;br /&gt;Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище устройства..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string> 4 <string name="app_disclaimer">Это программное обеспечение позволяет запускать игры для игровой консоли Nintendo Switch. Мы не предоставляем сами игры или ключи.&lt;br /&gt;&lt;br /&gt;Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище устройства..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string>
5 <string name="emulation_notification_channel_name">Эмуляция активна</string>
6 <string name="emulation_notification_channel_description">Показывает постоянное уведомление, когда запущена эмуляция.</string>
7 <string name="emulation_notification_running">yuzu запущен</string>
8 <string name="notice_notification_channel_name">Уведомления и ошибки</string> 5 <string name="notice_notification_channel_name">Уведомления и ошибки</string>
9 <string name="notice_notification_channel_description">Показывать уведомления, когда что-то пошло не так</string> 6 <string name="notice_notification_channel_description">Показывать уведомления, когда что-то пошло не так</string>
10 <string name="notification_permission_not_granted">Вы не предоставили разрешение на уведомления!</string> 7 <string name="notification_permission_not_granted">Вы не предоставили разрешение на уведомления!</string>
diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml
index 361f0b726..9a2804a93 100644
--- a/src/android/app/src/main/res/values-uk/strings.xml
+++ b/src/android/app/src/main/res/values-uk/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Це програмне забезпечення дозволяє запускати ігри для ігрової консолі Nintendo Switch. Ми не надаємо самі ігри або ключі.&lt;br /&gt;&lt;br /&gt;Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у сховищі пристрою.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Дізнатися більше</a>]]></string> 4 <string name="app_disclaimer">Це програмне забезпечення дозволяє запускати ігри для ігрової консолі Nintendo Switch. Ми не надаємо самі ігри або ключі.&lt;br /&gt;&lt;br /&gt;Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у сховищі пристрою.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Дізнатися більше</a>]]></string>
5 <string name="emulation_notification_channel_name">Емуляція активна</string>
6 <string name="emulation_notification_channel_description">Показує постійне сповіщення, коли запущено емуляцію.</string>
7 <string name="emulation_notification_running">yuzu запущено</string>
8 <string name="notice_notification_channel_name">Сповіщення та помилки</string> 5 <string name="notice_notification_channel_name">Сповіщення та помилки</string>
9 <string name="notice_notification_channel_description">Показувати сповіщення, коли щось пішло не так</string> 6 <string name="notice_notification_channel_description">Показувати сповіщення, коли щось пішло не так</string>
10 <string name="notification_permission_not_granted">Ви не надали дозвіл сповіщень!</string> 7 <string name="notification_permission_not_granted">Ви не надали дозвіл сповіщень!</string>
diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml
index 0a722f329..dc06610c7 100644
--- a/src/android/app/src/main/res/values-vi/strings.xml
+++ b/src/android/app/src/main/res/values-vi/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">Phần mềm này sẽ chạy các game cho máy chơi game Nintendo Switch. Không có title games hoặc keys được bao gồm.&lt;br /&gt;&lt;br /&gt;Trước khi bạn bắt đầu, hãy tìm tập tin <![CDATA[<b> prod.keys </b>]]> trên bộ nhớ thiết bị của bạn.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tìm hiểu thêm</a>]]></string> 4 <string name="app_disclaimer">Phần mềm này sẽ chạy các game cho máy chơi game Nintendo Switch. Không có title games hoặc keys được bao gồm.&lt;br /&gt;&lt;br /&gt;Trước khi bạn bắt đầu, hãy tìm tập tin <![CDATA[<b> prod.keys </b>]]> trên bộ nhớ thiết bị của bạn.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tìm hiểu thêm</a>]]></string>
5 <string name="emulation_notification_channel_name">Giả lập đang chạy</string>
6 <string name="emulation_notification_channel_description">Hiển thị thông báo liên tục khi giả lập đang chạy.</string>
7 <string name="emulation_notification_running">yuzu đang chạy</string>
8 <string name="notice_notification_channel_name">Thông báo và lỗi</string> 5 <string name="notice_notification_channel_name">Thông báo và lỗi</string>
9 <string name="notice_notification_channel_description">Hiển thị thông báo khi có sự cố xảy ra.</string> 6 <string name="notice_notification_channel_description">Hiển thị thông báo khi có sự cố xảy ra.</string>
10 <string name="notification_permission_not_granted">Ứng dụng không được cấp quyền thông báo!</string> 7 <string name="notification_permission_not_granted">Ứng dụng không được cấp quyền thông báo!</string>
diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml
index b840591a4..6acf6f391 100644
--- a/src/android/app/src/main/res/values-zh-rCN/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">此软件可以运行 Nintendo Switch 游戏,但不包含任何游戏和密钥文件。&lt;br /&gt;&lt;br /&gt;在开始前,请找到放置于设备存储中的 <![CDATA[<b> prod.keys </b>]]> 文件。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string> 4 <string name="app_disclaimer">此软件可以运行 Nintendo Switch 游戏,但不包含任何游戏和密钥文件。&lt;br /&gt;&lt;br /&gt;在开始前,请找到放置于设备存储中的 <![CDATA[<b> prod.keys </b>]]> 文件。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string>
5 <string name="emulation_notification_channel_name">正在进行模拟</string>
6 <string name="emulation_notification_channel_description">在模拟运行时显示持久通知。</string>
7 <string name="emulation_notification_running">yuzu 正在运行</string>
8 <string name="notice_notification_channel_name">通知及错误提醒</string> 5 <string name="notice_notification_channel_name">通知及错误提醒</string>
9 <string name="notice_notification_channel_description">当发生错误时显示通知。</string> 6 <string name="notice_notification_channel_description">当发生错误时显示通知。</string>
10 <string name="notification_permission_not_granted">未授予通知权限!</string> 7 <string name="notification_permission_not_granted">未授予通知权限!</string>
diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml
index d39255714..411fc5947 100644
--- a/src/android/app/src/main/res/values-zh-rTW/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -2,9 +2,6 @@
2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> 2<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
3 3
4 <string name="app_disclaimer">此軟體可以執行 Nintendo Switch 主機遊戲,但不包含任何遊戲和金鑰。&lt;br /&gt;&lt;br /&gt;在您開始前,請找到放置於您的裝置儲存空間的 <![CDATA[<b> prod.keys </b>]]> 檔案。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入瞭解</a>]]></string> 4 <string name="app_disclaimer">此軟體可以執行 Nintendo Switch 主機遊戲,但不包含任何遊戲和金鑰。&lt;br /&gt;&lt;br /&gt;在您開始前,請找到放置於您的裝置儲存空間的 <![CDATA[<b> prod.keys </b>]]> 檔案。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入瞭解</a>]]></string>
5 <string name="emulation_notification_channel_name">模擬進行中</string>
6 <string name="emulation_notification_channel_description">在模擬執行時顯示持續通知。</string>
7 <string name="emulation_notification_running">yuzu 正在執行</string>
8 <string name="notice_notification_channel_name">通知和錯誤</string> 5 <string name="notice_notification_channel_name">通知和錯誤</string>
9 <string name="notice_notification_channel_description">發生錯誤時顯示通知。</string> 6 <string name="notice_notification_channel_description">發生錯誤時顯示通知。</string>
10 <string name="notification_permission_not_granted">未授予通知權限!</string> 7 <string name="notification_permission_not_granted">未授予通知權限!</string>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 3cd1586fd..489e00107 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -4,10 +4,6 @@
4 <!-- General application strings --> 4 <!-- General application strings -->
5 <string name="app_name" translatable="false">yuzu</string> 5 <string name="app_name" translatable="false">yuzu</string>
6 <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href="https://yuzu-emu.org/help/quickstart">Learn more</a>]]></string> 6 <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href="https://yuzu-emu.org/help/quickstart">Learn more</a>]]></string>
7 <string name="emulation_notification_channel_name">Emulation is Active</string>
8 <string name="emulation_notification_channel_id" translatable="false">emulationIsActive</string>
9 <string name="emulation_notification_channel_description">Shows a persistent notification when emulation is running.</string>
10 <string name="emulation_notification_running">yuzu is running</string>
11 <string name="notice_notification_channel_name">Notices and errors</string> 7 <string name="notice_notification_channel_name">Notices and errors</string>
12 <string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string> 8 <string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
13 <string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string> 9 <string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
@@ -380,6 +376,7 @@
380 <string name="emulation_exit">Exit emulation</string> 376 <string name="emulation_exit">Exit emulation</string>
381 <string name="emulation_done">Done</string> 377 <string name="emulation_done">Done</string>
382 <string name="emulation_fps_counter">FPS counter</string> 378 <string name="emulation_fps_counter">FPS counter</string>
379 <string name="emulation_thermal_indicator">Thermal indicator</string>
383 <string name="emulation_toggle_controls">Toggle controls</string> 380 <string name="emulation_toggle_controls">Toggle controls</string>
384 <string name="emulation_rel_stick_center">Relative stick center</string> 381 <string name="emulation_rel_stick_center">Relative stick center</string>
385 <string name="emulation_dpad_slide">D-pad slide</string> 382 <string name="emulation_dpad_slide">D-pad slide</string>
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index c19af2ab8..779be211e 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -182,9 +182,15 @@ endif()
182 182
183if(ANDROID) 183if(ANDROID)
184 target_sources(common 184 target_sources(common
185 PRIVATE 185 PUBLIC
186 fs/fs_android.cpp 186 fs/fs_android.cpp
187 fs/fs_android.h 187 fs/fs_android.h
188 android/android_common.cpp
189 android/android_common.h
190 android/id_cache.cpp
191 android/id_cache.h
192 android/applets/software_keyboard.cpp
193 android/applets/software_keyboard.h
188 ) 194 )
189endif() 195endif()
190 196
diff --git a/src/android/app/src/main/jni/android_common/android_common.cpp b/src/common/android/android_common.cpp
index 7018a52af..e79005658 100644
--- a/src/android/app/src/main/jni/android_common/android_common.cpp
+++ b/src/common/android/android_common.cpp
@@ -1,15 +1,17 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "jni/android_common/android_common.h" 4#include "android_common.h"
5 5
6#include <string> 6#include <string>
7#include <string_view> 7#include <string_view>
8 8
9#include <jni.h> 9#include <jni.h>
10 10
11#include "common/android/id_cache.h"
11#include "common/string_util.h" 12#include "common/string_util.h"
12#include "jni/id_cache.h" 13
14namespace Common::Android {
13 15
14std::string GetJString(JNIEnv* env, jstring jstr) { 16std::string GetJString(JNIEnv* env, jstring jstr) {
15 if (!jstr) { 17 if (!jstr) {
@@ -18,7 +20,8 @@ std::string GetJString(JNIEnv* env, jstring jstr) {
18 20
19 const jchar* jchars = env->GetStringChars(jstr, nullptr); 21 const jchar* jchars = env->GetStringChars(jstr, nullptr);
20 const jsize length = env->GetStringLength(jstr); 22 const jsize length = env->GetStringLength(jstr);
21 const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); 23 const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars),
24 static_cast<u32>(length));
22 const std::string converted_string = Common::UTF16ToUTF8(string_view); 25 const std::string converted_string = Common::UTF16ToUTF8(string_view);
23 env->ReleaseStringChars(jstr, jchars); 26 env->ReleaseStringChars(jstr, jchars);
24 27
@@ -36,25 +39,27 @@ jstring ToJString(JNIEnv* env, std::u16string_view str) {
36} 39}
37 40
38double GetJDouble(JNIEnv* env, jobject jdouble) { 41double GetJDouble(JNIEnv* env, jobject jdouble) {
39 return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField()); 42 return env->GetDoubleField(jdouble, GetDoubleValueField());
40} 43}
41 44
42jobject ToJDouble(JNIEnv* env, double value) { 45jobject ToJDouble(JNIEnv* env, double value) {
43 return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value); 46 return env->NewObject(GetDoubleClass(), GetDoubleConstructor(), value);
44} 47}
45 48
46s32 GetJInteger(JNIEnv* env, jobject jinteger) { 49s32 GetJInteger(JNIEnv* env, jobject jinteger) {
47 return env->GetIntField(jinteger, IDCache::GetIntegerValueField()); 50 return env->GetIntField(jinteger, GetIntegerValueField());
48} 51}
49 52
50jobject ToJInteger(JNIEnv* env, s32 value) { 53jobject ToJInteger(JNIEnv* env, s32 value) {
51 return env->NewObject(IDCache::GetIntegerClass(), IDCache::GetIntegerConstructor(), value); 54 return env->NewObject(GetIntegerClass(), GetIntegerConstructor(), value);
52} 55}
53 56
54bool GetJBoolean(JNIEnv* env, jobject jboolean) { 57bool GetJBoolean(JNIEnv* env, jobject jboolean) {
55 return env->GetBooleanField(jboolean, IDCache::GetBooleanValueField()); 58 return env->GetBooleanField(jboolean, GetBooleanValueField());
56} 59}
57 60
58jobject ToJBoolean(JNIEnv* env, bool value) { 61jobject ToJBoolean(JNIEnv* env, bool value) {
59 return env->NewObject(IDCache::GetBooleanClass(), IDCache::GetBooleanConstructor(), value); 62 return env->NewObject(GetBooleanClass(), GetBooleanConstructor(), value);
60} 63}
64
65} // namespace Common::Android
diff --git a/src/android/app/src/main/jni/android_common/android_common.h b/src/common/android/android_common.h
index 29a338c0a..d0ccb4ec2 100644
--- a/src/android/app/src/main/jni/android_common/android_common.h
+++ b/src/common/android/android_common.h
@@ -8,6 +8,8 @@
8#include <jni.h> 8#include <jni.h>
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace Common::Android {
12
11std::string GetJString(JNIEnv* env, jstring jstr); 13std::string GetJString(JNIEnv* env, jstring jstr);
12jstring ToJString(JNIEnv* env, std::string_view str); 14jstring ToJString(JNIEnv* env, std::string_view str);
13jstring ToJString(JNIEnv* env, std::u16string_view str); 15jstring ToJString(JNIEnv* env, std::u16string_view str);
@@ -20,3 +22,5 @@ jobject ToJInteger(JNIEnv* env, s32 value);
20 22
21bool GetJBoolean(JNIEnv* env, jobject jboolean); 23bool GetJBoolean(JNIEnv* env, jobject jboolean);
22jobject ToJBoolean(JNIEnv* env, bool value); 24jobject ToJBoolean(JNIEnv* env, bool value);
25
26} // namespace Common::Android
diff --git a/src/android/app/src/main/jni/applets/software_keyboard.cpp b/src/common/android/applets/software_keyboard.cpp
index 9943483e8..477e62b16 100644
--- a/src/android/app/src/main/jni/applets/software_keyboard.cpp
+++ b/src/common/android/applets/software_keyboard.cpp
@@ -6,12 +6,12 @@
6 6
7#include <jni.h> 7#include <jni.h>
8 8
9#include "common/android/android_common.h"
10#include "common/android/applets/software_keyboard.h"
11#include "common/android/id_cache.h"
9#include "common/logging/log.h" 12#include "common/logging/log.h"
10#include "common/string_util.h" 13#include "common/string_util.h"
11#include "core/core.h" 14#include "core/core.h"
12#include "jni/android_common/android_common.h"
13#include "jni/applets/software_keyboard.h"
14#include "jni/id_cache.h"
15 15
16static jclass s_software_keyboard_class; 16static jclass s_software_keyboard_class;
17static jclass s_keyboard_config_class; 17static jclass s_keyboard_config_class;
@@ -19,10 +19,10 @@ static jclass s_keyboard_data_class;
19static jmethodID s_swkbd_execute_normal; 19static jmethodID s_swkbd_execute_normal;
20static jmethodID s_swkbd_execute_inline; 20static jmethodID s_swkbd_execute_inline;
21 21
22namespace SoftwareKeyboard { 22namespace Common::Android::SoftwareKeyboard {
23 23
24static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) { 24static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) {
25 JNIEnv* env = IDCache::GetEnvForThread(); 25 JNIEnv* env = GetEnvForThread();
26 jobject object = env->AllocObject(s_keyboard_config_class); 26 jobject object = env->AllocObject(s_keyboard_config_class);
27 27
28 env->SetObjectField(object, 28 env->SetObjectField(object,
@@ -78,7 +78,7 @@ static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParamet
78} 78}
79 79
80AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) { 80AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) {
81 JNIEnv* env = IDCache::GetEnvForThread(); 81 JNIEnv* env = GetEnvForThread();
82 const jstring string = reinterpret_cast<jstring>(env->GetObjectField( 82 const jstring string = reinterpret_cast<jstring>(env->GetObjectField(
83 object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;"))); 83 object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;")));
84 return ResultData{GetJString(env, string), 84 return ResultData{GetJString(env, string),
@@ -141,7 +141,7 @@ void AndroidKeyboard::ShowNormalKeyboard() const {
141 141
142 // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber. 142 // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
143 std::thread([&] { 143 std::thread([&] {
144 data = ResultData::CreateFromFrontend(IDCache::GetEnvForThread()->CallStaticObjectMethod( 144 data = ResultData::CreateFromFrontend(GetEnvForThread()->CallStaticObjectMethod(
145 s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters))); 145 s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters)));
146 }).join(); 146 }).join();
147 147
@@ -183,8 +183,8 @@ void AndroidKeyboard::ShowInlineKeyboard(
183 // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber. 183 // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
184 m_is_inline_active = true; 184 m_is_inline_active = true;
185 std::thread([&] { 185 std::thread([&] {
186 IDCache::GetEnvForThread()->CallStaticVoidMethod( 186 GetEnvForThread()->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_execute_inline,
187 s_software_keyboard_class, s_swkbd_execute_inline, ToJKeyboardParams(parameters)); 187 ToJKeyboardParams(parameters));
188 }).join(); 188 }).join();
189} 189}
190 190
@@ -220,7 +220,7 @@ void AndroidKeyboard::SubmitInlineKeyboardText(std::u16string submitted_text) {
220 m_current_text += submitted_text; 220 m_current_text += submitted_text;
221 221
222 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, 222 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
223 m_current_text.size()); 223 static_cast<int>(m_current_text.size()));
224} 224}
225 225
226void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) { 226void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
@@ -242,7 +242,7 @@ void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
242 case KEYCODE_DEL: 242 case KEYCODE_DEL:
243 m_current_text.pop_back(); 243 m_current_text.pop_back();
244 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, 244 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
245 m_current_text.size()); 245 static_cast<int>(m_current_text.size()));
246 break; 246 break;
247 } 247 }
248} 248}
@@ -274,4 +274,4 @@ void CleanupJNI(JNIEnv* env) {
274 env->DeleteGlobalRef(s_keyboard_data_class); 274 env->DeleteGlobalRef(s_keyboard_data_class);
275} 275}
276 276
277} // namespace SoftwareKeyboard 277} // namespace Common::Android::SoftwareKeyboard
diff --git a/src/android/app/src/main/jni/applets/software_keyboard.h b/src/common/android/applets/software_keyboard.h
index 2affc01f6..9fd09d27c 100644
--- a/src/android/app/src/main/jni/applets/software_keyboard.h
+++ b/src/common/android/applets/software_keyboard.h
@@ -7,7 +7,7 @@
7 7
8#include "core/frontend/applets/software_keyboard.h" 8#include "core/frontend/applets/software_keyboard.h"
9 9
10namespace SoftwareKeyboard { 10namespace Common::Android::SoftwareKeyboard {
11 11
12class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet { 12class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet {
13public: 13public:
@@ -66,7 +66,7 @@ void InitJNI(JNIEnv* env);
66// Should be called in JNI_Unload 66// Should be called in JNI_Unload
67void CleanupJNI(JNIEnv* env); 67void CleanupJNI(JNIEnv* env);
68 68
69} // namespace SoftwareKeyboard 69} // namespace Common::Android::SoftwareKeyboard
70 70
71// Native function calls 71// Native function calls
72extern "C" { 72extern "C" {
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/common/android/id_cache.cpp
index f30100bd8..f39262db9 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/common/android/id_cache.cpp
@@ -3,10 +3,10 @@
3 3
4#include <jni.h> 4#include <jni.h>
5 5
6#include "applets/software_keyboard.h"
7#include "common/android/id_cache.h"
6#include "common/assert.h" 8#include "common/assert.h"
7#include "common/fs/fs_android.h" 9#include "common/fs/fs_android.h"
8#include "jni/applets/software_keyboard.h"
9#include "jni/id_cache.h"
10#include "video_core/rasterizer_interface.h" 10#include "video_core/rasterizer_interface.h"
11 11
12static JavaVM* s_java_vm; 12static JavaVM* s_java_vm;
@@ -67,7 +67,7 @@ static jfieldID s_boolean_value_field;
67 67
68static constexpr jint JNI_VERSION = JNI_VERSION_1_6; 68static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
69 69
70namespace IDCache { 70namespace Common::Android {
71 71
72JNIEnv* GetEnvForThread() { 72JNIEnv* GetEnvForThread() {
73 thread_local static struct OwnedEnv { 73 thread_local static struct OwnedEnv {
@@ -276,8 +276,6 @@ jfieldID GetBooleanValueField() {
276 return s_boolean_value_field; 276 return s_boolean_value_field;
277} 277}
278 278
279} // namespace IDCache
280
281#ifdef __cplusplus 279#ifdef __cplusplus
282extern "C" { 280extern "C" {
283#endif 281#endif
@@ -393,7 +391,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
393 Common::FS::Android::RegisterCallbacks(env, s_native_library_class); 391 Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
394 392
395 // Initialize applets 393 // Initialize applets
396 SoftwareKeyboard::InitJNI(env); 394 Common::Android::SoftwareKeyboard::InitJNI(env);
397 395
398 return JNI_VERSION; 396 return JNI_VERSION;
399} 397}
@@ -426,3 +424,5 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
426#ifdef __cplusplus 424#ifdef __cplusplus
427} 425}
428#endif 426#endif
427
428} // namespace Common::Android
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/common/android/id_cache.h
index 00e48afc0..47802f96c 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/common/android/id_cache.h
@@ -3,20 +3,40 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <future>
6#include <jni.h> 7#include <jni.h>
7 8
8#include "video_core/rasterizer_interface.h" 9#include "video_core/rasterizer_interface.h"
9 10
10namespace IDCache { 11namespace Common::Android {
11 12
12JNIEnv* GetEnvForThread(); 13JNIEnv* GetEnvForThread();
14
15/**
16 * Starts a new thread to run JNI. Intended to be used when you must run JNI from a fiber.
17 * @tparam T Typename of the return value for the work param
18 * @param work Lambda that runs JNI code. This function will take care of attaching this thread to
19 * the JVM
20 * @return The result from the work lambda param
21 */
22template <typename T = void>
23T RunJNIOnFiber(const std::function<T(JNIEnv*)>& work) {
24 std::future<T> j_result = std::async(std::launch::async, [&] {
25 auto env = GetEnvForThread();
26 return work(env);
27 });
28 return j_result.get();
29}
30
13jclass GetNativeLibraryClass(); 31jclass GetNativeLibraryClass();
32
14jclass GetDiskCacheProgressClass(); 33jclass GetDiskCacheProgressClass();
15jclass GetDiskCacheLoadCallbackStageClass(); 34jclass GetDiskCacheLoadCallbackStageClass();
16jclass GetGameDirClass(); 35jclass GetGameDirClass();
17jmethodID GetGameDirConstructor(); 36jmethodID GetGameDirConstructor();
18jmethodID GetExitEmulationActivity();
19jmethodID GetDiskCacheLoadProgress(); 37jmethodID GetDiskCacheLoadProgress();
38
39jmethodID GetExitEmulationActivity();
20jmethodID GetOnEmulationStarted(); 40jmethodID GetOnEmulationStarted();
21jmethodID GetOnEmulationStopped(); 41jmethodID GetOnEmulationStopped();
22jmethodID GetOnProgramChanged(); 42jmethodID GetOnProgramChanged();
@@ -65,4 +85,4 @@ jclass GetBooleanClass();
65jmethodID GetBooleanConstructor(); 85jmethodID GetBooleanConstructor();
66jfieldID GetBooleanValueField(); 86jfieldID GetBooleanValueField();
67 87
68} // namespace IDCache 88} // namespace Common::Android
diff --git a/src/common/fs/fs_android.cpp b/src/common/fs/fs_android.cpp
index 1dd826a4a..9a8053222 100644
--- a/src/common/fs/fs_android.cpp
+++ b/src/common/fs/fs_android.cpp
@@ -1,63 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/android/android_common.h"
5#include "common/android/id_cache.h"
6#include "common/assert.h"
4#include "common/fs/fs_android.h" 7#include "common/fs/fs_android.h"
5#include "common/string_util.h" 8#include "common/string_util.h"
6 9
7namespace Common::FS::Android { 10namespace Common::FS::Android {
8 11
9JNIEnv* GetEnvForThread() {
10 thread_local static struct OwnedEnv {
11 OwnedEnv() {
12 status = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
13 if (status == JNI_EDETACHED)
14 g_jvm->AttachCurrentThread(&env, nullptr);
15 }
16
17 ~OwnedEnv() {
18 if (status == JNI_EDETACHED)
19 g_jvm->DetachCurrentThread();
20 }
21
22 int status;
23 JNIEnv* env = nullptr;
24 } owned;
25 return owned.env;
26}
27
28void RegisterCallbacks(JNIEnv* env, jclass clazz) { 12void RegisterCallbacks(JNIEnv* env, jclass clazz) {
29 env->GetJavaVM(&g_jvm); 13 env->GetJavaVM(&g_jvm);
30 native_library = clazz; 14 native_library = clazz;
31 15
32#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ 16 s_get_parent_directory = env->GetStaticMethodID(native_library, "getParentDirectory",
33 F(JMethodID, JMethodName, Signature) 17 "(Ljava/lang/String;)Ljava/lang/String;");
34#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ 18 s_get_filename = env->GetStaticMethodID(native_library, "getFilename",
35 F(JMethodID, JMethodName, Signature) 19 "(Ljava/lang/String;)Ljava/lang/String;");
36#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ 20 s_get_size = env->GetStaticMethodID(native_library, "getSize", "(Ljava/lang/String;)J");
37 F(JMethodID, JMethodName, Signature) 21 s_is_directory = env->GetStaticMethodID(native_library, "isDirectory", "(Ljava/lang/String;)Z");
38#define F(JMethodID, JMethodName, Signature) \ 22 s_file_exists = env->GetStaticMethodID(native_library, "exists", "(Ljava/lang/String;)Z");
39 JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature); 23 s_open_content_uri = env->GetStaticMethodID(native_library, "openContentUri",
40 ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) 24 "(Ljava/lang/String;Ljava/lang/String;)I");
41 ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
42 ANDROID_STORAGE_FUNCTIONS(FS)
43#undef F
44#undef FS
45#undef FR
46#undef FH
47} 25}
48 26
49void UnRegisterCallbacks() { 27void UnRegisterCallbacks() {
50#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) 28 s_get_parent_directory = nullptr;
51#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) 29 s_get_filename = nullptr;
52#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) 30
53#define F(JMethodID) JMethodID = nullptr; 31 s_get_size = nullptr;
54 ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) 32 s_is_directory = nullptr;
55 ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) 33 s_file_exists = nullptr;
56 ANDROID_STORAGE_FUNCTIONS(FS) 34
57#undef F 35 s_open_content_uri = nullptr;
58#undef FS
59#undef FR
60#undef FH
61} 36}
62 37
63bool IsContentUri(const std::string& path) { 38bool IsContentUri(const std::string& path) {
@@ -69,8 +44,8 @@ bool IsContentUri(const std::string& path) {
69 return path.find(prefix) == 0; 44 return path.find(prefix) == 0;
70} 45}
71 46
72int OpenContentUri(const std::string& filepath, OpenMode openmode) { 47s32 OpenContentUri(const std::string& filepath, OpenMode openmode) {
73 if (open_content_uri == nullptr) 48 if (s_open_content_uri == nullptr)
74 return -1; 49 return -1;
75 50
76 const char* mode = ""; 51 const char* mode = "";
@@ -82,50 +57,66 @@ int OpenContentUri(const std::string& filepath, OpenMode openmode) {
82 UNIMPLEMENTED(); 57 UNIMPLEMENTED();
83 return -1; 58 return -1;
84 } 59 }
85 auto env = GetEnvForThread(); 60 auto env = Common::Android::GetEnvForThread();
86 jstring j_filepath = env->NewStringUTF(filepath.c_str()); 61 jstring j_filepath = Common::Android::ToJString(env, filepath);
87 jstring j_mode = env->NewStringUTF(mode); 62 jstring j_mode = Common::Android::ToJString(env, mode);
88 return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode); 63 return env->CallStaticIntMethod(native_library, s_open_content_uri, j_filepath, j_mode);
64}
65
66u64 GetSize(const std::string& filepath) {
67 if (s_get_size == nullptr) {
68 return 0;
69 }
70 auto env = Common::Android::GetEnvForThread();
71 return static_cast<u64>(env->CallStaticLongMethod(
72 native_library, s_get_size,
73 Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath)));
74}
75
76bool IsDirectory(const std::string& filepath) {
77 if (s_is_directory == nullptr) {
78 return 0;
79 }
80 auto env = Common::Android::GetEnvForThread();
81 return env->CallStaticBooleanMethod(
82 native_library, s_is_directory,
83 Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath));
89} 84}
90 85
91#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ 86bool Exists(const std::string& filepath) {
92 F(FunctionName, ReturnValue, JMethodID, Caller) 87 if (s_file_exists == nullptr) {
93#define F(FunctionName, ReturnValue, JMethodID, Caller) \ 88 return 0;
94 ReturnValue FunctionName(const std::string& filepath) { \
95 if (JMethodID == nullptr) { \
96 return 0; \
97 } \
98 auto env = GetEnvForThread(); \
99 jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
100 return env->Caller(native_library, JMethodID, j_filepath); \
101 } 89 }
102ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) 90 auto env = Common::Android::GetEnvForThread();
103#undef F 91 return env->CallStaticBooleanMethod(
104#undef FR 92 native_library, s_file_exists,
105 93 Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath));
106#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ 94}
107 F(FunctionName, JMethodID, Caller) 95
108#define F(FunctionName, JMethodID, Caller) \ 96std::string GetParentDirectory(const std::string& filepath) {
109 std::string FunctionName(const std::string& filepath) { \ 97 if (s_get_parent_directory == nullptr) {
110 if (JMethodID == nullptr) { \ 98 return 0;
111 return 0; \
112 } \
113 auto env = GetEnvForThread(); \
114 jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
115 jstring j_return = \
116 static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \
117 if (!j_return) { \
118 return {}; \
119 } \
120 const jchar* jchars = env->GetStringChars(j_return, nullptr); \
121 const jsize length = env->GetStringLength(j_return); \
122 const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \
123 const std::string converted_string = Common::UTF16ToUTF8(string_view); \
124 env->ReleaseStringChars(j_return, jchars); \
125 return converted_string; \
126 } 99 }
127ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) 100 auto env = Common::Android::GetEnvForThread();
128#undef F 101 jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod(
129#undef FH 102 native_library, s_get_parent_directory, Common::Android::ToJString(env, filepath)));
103 if (!j_return) {
104 return {};
105 }
106 return Common::Android::GetJString(env, j_return);
107}
108
109std::string GetFilename(const std::string& filepath) {
110 if (s_get_filename == nullptr) {
111 return 0;
112 }
113 auto env = Common::Android::GetEnvForThread();
114 jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod(
115 native_library, s_get_filename, Common::Android::ToJString(env, filepath)));
116 if (!j_return) {
117 return {};
118 }
119 return Common::Android::GetJString(env, j_return);
120}
130 121
131} // namespace Common::FS::Android 122} // namespace Common::FS::Android
diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h
index 2c9234313..b33f4beb8 100644
--- a/src/common/fs/fs_android.h
+++ b/src/common/fs/fs_android.h
@@ -7,38 +7,17 @@
7#include <vector> 7#include <vector>
8#include <jni.h> 8#include <jni.h>
9 9
10#define ANDROID_STORAGE_FUNCTIONS(V) \
11 V(OpenContentUri, int, (const std::string& filepath, OpenMode openmode), open_content_uri, \
12 "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I")
13
14#define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \
15 V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") \
16 V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \
17 "(Ljava/lang/String;)Z") \
18 V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
19
20#define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \
21 V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \
22 "(Ljava/lang/String;)Ljava/lang/String;") \
23 V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \
24 "(Ljava/lang/String;)Ljava/lang/String;")
25
26namespace Common::FS::Android { 10namespace Common::FS::Android {
27 11
28static JavaVM* g_jvm = nullptr; 12static JavaVM* g_jvm = nullptr;
29static jclass native_library = nullptr; 13static jclass native_library = nullptr;
30 14
31#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) 15static jmethodID s_get_parent_directory;
32#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) 16static jmethodID s_get_filename;
33#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) 17static jmethodID s_get_size;
34#define F(JMethodID) static jmethodID JMethodID = nullptr; 18static jmethodID s_is_directory;
35ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) 19static jmethodID s_file_exists;
36ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) 20static jmethodID s_open_content_uri;
37ANDROID_STORAGE_FUNCTIONS(FS)
38#undef F
39#undef FS
40#undef FR
41#undef FH
42 21
43enum class OpenMode { 22enum class OpenMode {
44 Read, 23 Read,
@@ -57,24 +36,11 @@ void UnRegisterCallbacks();
57 36
58bool IsContentUri(const std::string& path); 37bool IsContentUri(const std::string& path);
59 38
60#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ 39int OpenContentUri(const std::string& filepath, OpenMode openmode);
61 F(FunctionName, Parameters, ReturnValue) 40std::uint64_t GetSize(const std::string& filepath);
62#define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters; 41bool IsDirectory(const std::string& filepath);
63ANDROID_STORAGE_FUNCTIONS(FS) 42bool Exists(const std::string& filepath);
64#undef F 43std::string GetParentDirectory(const std::string& filepath);
65#undef FS 44std::string GetFilename(const std::string& filepath);
66
67#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
68 F(FunctionName, ReturnValue)
69#define F(FunctionName, ReturnValue) ReturnValue FunctionName(const std::string& filepath);
70ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
71#undef F
72#undef FR
73
74#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName)
75#define F(FunctionName) std::string FunctionName(const std::string& filepath);
76ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
77#undef F
78#undef FH
79 45
80} // namespace Common::FS::Android 46} // namespace Common::FS::Android
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index eb8f643a2..2d5490968 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -512,10 +512,35 @@ add_library(core STATIC
512 hle/service/audio/hwopus.h 512 hle/service/audio/hwopus.h
513 hle/service/bcat/backend/backend.cpp 513 hle/service/bcat/backend/backend.cpp
514 hle/service/bcat/backend/backend.h 514 hle/service/bcat/backend/backend.h
515 hle/service/bcat/news/newly_arrived_event_holder.cpp
516 hle/service/bcat/news/newly_arrived_event_holder.h
517 hle/service/bcat/news/news_data_service.cpp
518 hle/service/bcat/news/news_data_service.h
519 hle/service/bcat/news/news_database_service.cpp
520 hle/service/bcat/news/news_database_service.h
521 hle/service/bcat/news/news_service.cpp
522 hle/service/bcat/news/news_service.h
523 hle/service/bcat/news/overwrite_event_holder.cpp
524 hle/service/bcat/news/overwrite_event_holder.h
525 hle/service/bcat/news/service_creator.cpp
526 hle/service/bcat/news/service_creator.h
515 hle/service/bcat/bcat.cpp 527 hle/service/bcat/bcat.cpp
516 hle/service/bcat/bcat.h 528 hle/service/bcat/bcat.h
517 hle/service/bcat/bcat_module.cpp 529 hle/service/bcat/bcat_result.h
518 hle/service/bcat/bcat_module.h 530 hle/service/bcat/bcat_service.cpp
531 hle/service/bcat/bcat_service.h
532 hle/service/bcat/bcat_types.h
533 hle/service/bcat/bcat_util.h
534 hle/service/bcat/delivery_cache_directory_service.cpp
535 hle/service/bcat/delivery_cache_directory_service.h
536 hle/service/bcat/delivery_cache_file_service.cpp
537 hle/service/bcat/delivery_cache_file_service.h
538 hle/service/bcat/delivery_cache_progress_service.cpp
539 hle/service/bcat/delivery_cache_progress_service.h
540 hle/service/bcat/delivery_cache_storage_service.cpp
541 hle/service/bcat/delivery_cache_storage_service.h
542 hle/service/bcat/service_creator.cpp
543 hle/service/bcat/service_creator.h
519 hle/service/bpc/bpc.cpp 544 hle/service/bpc/bpc.cpp
520 hle/service/bpc/bpc.h 545 hle/service/bpc/bpc.h
521 hle/service/btdrv/btdrv.cpp 546 hle/service/btdrv/btdrv.cpp
@@ -548,8 +573,6 @@ add_library(core STATIC
548 hle/service/es/es.h 573 hle/service/es/es.h
549 hle/service/eupld/eupld.cpp 574 hle/service/eupld/eupld.cpp
550 hle/service/eupld/eupld.h 575 hle/service/eupld/eupld.h
551 hle/service/event.cpp
552 hle/service/event.h
553 hle/service/fatal/fatal.cpp 576 hle/service/fatal/fatal.cpp
554 hle/service/fatal/fatal.h 577 hle/service/fatal/fatal.h
555 hle/service/fatal/fatal_p.cpp 578 hle/service/fatal/fatal_p.cpp
@@ -676,8 +699,6 @@ add_library(core STATIC
676 hle/service/mm/mm_u.h 699 hle/service/mm/mm_u.h
677 hle/service/mnpp/mnpp_app.cpp 700 hle/service/mnpp/mnpp_app.cpp
678 hle/service/mnpp/mnpp_app.h 701 hle/service/mnpp/mnpp_app.h
679 hle/service/mutex.cpp
680 hle/service/mutex.h
681 hle/service/ncm/ncm.cpp 702 hle/service/ncm/ncm.cpp
682 hle/service/ncm/ncm.h 703 hle/service/ncm/ncm.h
683 hle/service/nfc/common/amiibo_crypto.cpp 704 hle/service/nfc/common/amiibo_crypto.cpp
@@ -790,6 +811,15 @@ add_library(core STATIC
790 hle/service/nvnflinger/window.h 811 hle/service/nvnflinger/window.h
791 hle/service/olsc/olsc.cpp 812 hle/service/olsc/olsc.cpp
792 hle/service/olsc/olsc.h 813 hle/service/olsc/olsc.h
814 hle/service/os/event.cpp
815 hle/service/os/event.h
816 hle/service/os/multi_wait_holder.cpp
817 hle/service/os/multi_wait_holder.h
818 hle/service/os/multi_wait_utils.h
819 hle/service/os/multi_wait.cpp
820 hle/service/os/multi_wait.h
821 hle/service/os/mutex.cpp
822 hle/service/os/mutex.h
793 hle/service/pcie/pcie.cpp 823 hle/service/pcie/pcie.cpp
794 hle/service/pcie/pcie.h 824 hle/service/pcie/pcie.h
795 hle/service/pctl/pctl.cpp 825 hle/service/pctl/pctl.cpp
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index d4e0eb6f4..b22767bf5 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -91,6 +91,7 @@ constexpr Result ResultWriteNotPermitted{ErrorModule::FS, 6203};
91constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325}; 91constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325};
92constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387}; 92constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387};
93constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388}; 93constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388};
94constexpr Result ResultPermissionDenied{ErrorModule::FS, 6400};
94constexpr Result ResultBufferAllocationFailed{ErrorModule::FS, 6705}; 95constexpr Result ResultBufferAllocationFailed{ErrorModule::FS, 6705};
95 96
96} // namespace FileSys 97} // namespace FileSys
diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h
index bce6f9050..b29ecdfed 100644
--- a/src/core/hle/service/am/applet.h
+++ b/src/core/hle/service/am/applet.h
@@ -9,8 +9,8 @@
9#include "common/math_util.h" 9#include "common/math_util.h"
10#include "core/hle/service/apm/apm_controller.h" 10#include "core/hle/service/apm/apm_controller.h"
11#include "core/hle/service/caps/caps_types.h" 11#include "core/hle/service/caps/caps_types.h"
12#include "core/hle/service/event.h"
13#include "core/hle/service/kernel_helpers.h" 12#include "core/hle/service/kernel_helpers.h"
13#include "core/hle/service/os/event.h"
14#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
15 15
16#include "core/hle/service/am/am_types.h" 16#include "core/hle/service/am/am_types.h"
diff --git a/src/core/hle/service/am/applet_data_broker.h b/src/core/hle/service/am/applet_data_broker.h
index 12326fd04..5a1d43c11 100644
--- a/src/core/hle/service/am/applet_data_broker.h
+++ b/src/core/hle/service/am/applet_data_broker.h
@@ -7,8 +7,8 @@
7#include <memory> 7#include <memory>
8#include <mutex> 8#include <mutex>
9 9
10#include "core/hle/service/event.h"
11#include "core/hle/service/kernel_helpers.h" 10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/os/event.h"
12 12
13union Result; 13union Result;
14 14
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
index 847f76987..1993493a9 100644
--- a/src/core/hle/service/bcat/backend/backend.cpp
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -33,18 +33,18 @@ void ProgressServiceBackend::SetTotalSize(u64 size) {
33} 33}
34 34
35void ProgressServiceBackend::StartConnecting() { 35void ProgressServiceBackend::StartConnecting() {
36 impl.status = DeliveryCacheProgressImpl::Status::Connecting; 36 impl.status = DeliveryCacheProgressStatus::Connecting;
37 SignalUpdate(); 37 SignalUpdate();
38} 38}
39 39
40void ProgressServiceBackend::StartProcessingDataList() { 40void ProgressServiceBackend::StartProcessingDataList() {
41 impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList; 41 impl.status = DeliveryCacheProgressStatus::ProcessingDataList;
42 SignalUpdate(); 42 SignalUpdate();
43} 43}
44 44
45void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name, 45void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name,
46 std::string_view file_name, u64 file_size) { 46 std::string_view file_name, u64 file_size) {
47 impl.status = DeliveryCacheProgressImpl::Status::Downloading; 47 impl.status = DeliveryCacheProgressStatus::Downloading;
48 impl.current_downloaded_bytes = 0; 48 impl.current_downloaded_bytes = 0;
49 impl.current_total_bytes = file_size; 49 impl.current_total_bytes = file_size;
50 std::memcpy(impl.current_directory.data(), dir_name.data(), 50 std::memcpy(impl.current_directory.data(), dir_name.data(),
@@ -65,7 +65,7 @@ void ProgressServiceBackend::FinishDownloadingFile() {
65} 65}
66 66
67void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { 67void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
68 impl.status = DeliveryCacheProgressImpl::Status::Committing; 68 impl.status = DeliveryCacheProgressStatus::Committing;
69 impl.current_file.fill(0); 69 impl.current_file.fill(0);
70 impl.current_downloaded_bytes = 0; 70 impl.current_downloaded_bytes = 0;
71 impl.current_total_bytes = 0; 71 impl.current_total_bytes = 0;
@@ -76,7 +76,7 @@ void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
76 76
77void ProgressServiceBackend::FinishDownload(Result result) { 77void ProgressServiceBackend::FinishDownload(Result result) {
78 impl.total_downloaded_bytes = impl.total_bytes; 78 impl.total_downloaded_bytes = impl.total_bytes;
79 impl.status = DeliveryCacheProgressImpl::Status::Done; 79 impl.status = DeliveryCacheProgressStatus::Done;
80 impl.result = result; 80 impl.result = result;
81 SignalUpdate(); 81 SignalUpdate();
82} 82}
@@ -85,15 +85,15 @@ void ProgressServiceBackend::SignalUpdate() {
85 update_event->Signal(); 85 update_event->Signal();
86} 86}
87 87
88Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} 88BcatBackend::BcatBackend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
89 89
90Backend::~Backend() = default; 90BcatBackend::~BcatBackend() = default;
91 91
92NullBackend::NullBackend(DirectoryGetter getter) : Backend(std::move(getter)) {} 92NullBcatBackend::NullBcatBackend(DirectoryGetter getter) : BcatBackend(std::move(getter)) {}
93 93
94NullBackend::~NullBackend() = default; 94NullBcatBackend::~NullBcatBackend() = default;
95 95
96bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { 96bool NullBcatBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
97 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, 97 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
98 title.build_id); 98 title.build_id);
99 99
@@ -101,8 +101,8 @@ bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& prog
101 return true; 101 return true;
102} 102}
103 103
104bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, 104bool NullBcatBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
105 ProgressServiceBackend& progress) { 105 ProgressServiceBackend& progress) {
106 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id, 106 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id,
107 title.build_id, name); 107 title.build_id, name);
108 108
@@ -110,18 +110,18 @@ bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
110 return true; 110 return true;
111} 111}
112 112
113bool NullBackend::Clear(u64 title_id) { 113bool NullBcatBackend::Clear(u64 title_id) {
114 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); 114 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
115 115
116 return true; 116 return true;
117} 117}
118 118
119void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) { 119void NullBcatBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
120 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, 120 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
121 Common::HexToString(passphrase)); 121 Common::HexToString(passphrase));
122} 122}
123 123
124std::optional<std::vector<u8>> NullBackend::GetLaunchParameter(TitleIDVersion title) { 124std::optional<std::vector<u8>> NullBcatBackend::GetLaunchParameter(TitleIDVersion title) {
125 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, 125 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
126 title.build_id); 126 title.build_id);
127 return std::nullopt; 127 return std::nullopt;
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h
index aa36d29d5..3680f6c9c 100644
--- a/src/core/hle/service/bcat/backend/backend.h
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -10,6 +10,7 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/file_sys/vfs/vfs_types.h" 11#include "core/file_sys/vfs/vfs_types.h"
12#include "core/hle/result.h" 12#include "core/hle/result.h"
13#include "core/hle/service/bcat/bcat_types.h"
13#include "core/hle/service/kernel_helpers.h" 14#include "core/hle/service/kernel_helpers.h"
14 15
15namespace Core { 16namespace Core {
@@ -24,44 +25,6 @@ class KReadableEvent;
24 25
25namespace Service::BCAT { 26namespace Service::BCAT {
26 27
27struct DeliveryCacheProgressImpl;
28
29using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
30using Passphrase = std::array<u8, 0x20>;
31
32struct TitleIDVersion {
33 u64 title_id;
34 u64 build_id;
35};
36
37using DirectoryName = std::array<char, 0x20>;
38using FileName = std::array<char, 0x20>;
39
40struct DeliveryCacheProgressImpl {
41 enum class Status : s32 {
42 None = 0x0,
43 Queued = 0x1,
44 Connecting = 0x2,
45 ProcessingDataList = 0x3,
46 Downloading = 0x4,
47 Committing = 0x5,
48 Done = 0x9,
49 };
50
51 Status status;
52 Result result = ResultSuccess;
53 DirectoryName current_directory;
54 FileName current_file;
55 s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
56 s64 current_total_bytes; ///< Bytes total on current file.
57 s64 total_downloaded_bytes; ///< Bytes downloaded on overall download.
58 s64 total_bytes; ///< Bytes total on overall download.
59 INSERT_PADDING_BYTES(
60 0x198); ///< Appears to be unused in official code, possibly reserved for future use.
61};
62static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200,
63 "DeliveryCacheProgressImpl has incorrect size.");
64
65// A class to manage the signalling to the game about BCAT download progress. 28// A class to manage the signalling to the game about BCAT download progress.
66// Some of this class is implemented in module.cpp to avoid exposing the implementation structure. 29// Some of this class is implemented in module.cpp to avoid exposing the implementation structure.
67class ProgressServiceBackend { 30class ProgressServiceBackend {
@@ -107,10 +70,10 @@ private:
107}; 70};
108 71
109// A class representing an abstract backend for BCAT functionality. 72// A class representing an abstract backend for BCAT functionality.
110class Backend { 73class BcatBackend {
111public: 74public:
112 explicit Backend(DirectoryGetter getter); 75 explicit BcatBackend(DirectoryGetter getter);
113 virtual ~Backend(); 76 virtual ~BcatBackend();
114 77
115 // Called when the backend is needed to synchronize the data for the game with title ID and 78 // Called when the backend is needed to synchronize the data for the game with title ID and
116 // version in title. A ProgressServiceBackend object is provided to alert the application of 79 // version in title. A ProgressServiceBackend object is provided to alert the application of
@@ -135,10 +98,10 @@ protected:
135}; 98};
136 99
137// A backend of BCAT that provides no operation. 100// A backend of BCAT that provides no operation.
138class NullBackend : public Backend { 101class NullBcatBackend : public BcatBackend {
139public: 102public:
140 explicit NullBackend(DirectoryGetter getter); 103 explicit NullBcatBackend(DirectoryGetter getter);
141 ~NullBackend() override; 104 ~NullBcatBackend() override;
142 105
143 bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; 106 bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
144 bool SynchronizeDirectory(TitleIDVersion title, std::string name, 107 bool SynchronizeDirectory(TitleIDVersion title, std::string name,
@@ -151,6 +114,7 @@ public:
151 std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override; 114 std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
152}; 115};
153 116
154std::unique_ptr<Backend> CreateBackendFromSettings(Core::System& system, DirectoryGetter getter); 117std::unique_ptr<BcatBackend> CreateBackendFromSettings(Core::System& system,
118 DirectoryGetter getter);
155 119
156} // namespace Service::BCAT 120} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp
index d0ac17324..ea8b15998 100644
--- a/src/core/hle/service/bcat/bcat.cpp
+++ b/src/core/hle/service/bcat/bcat.cpp
@@ -1,24 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/bcat/backend/backend.h"
4#include "core/hle/service/bcat/bcat.h" 5#include "core/hle/service/bcat/bcat.h"
6#include "core/hle/service/bcat/news/service_creator.h"
7#include "core/hle/service/bcat/service_creator.h"
8#include "core/hle/service/server_manager.h"
5 9
6namespace Service::BCAT { 10namespace Service::BCAT {
7 11
8BCAT::BCAT(Core::System& system_, std::shared_ptr<Module> module_, 12void LoopProcess(Core::System& system) {
9 FileSystem::FileSystemController& fsc_, const char* name_) 13 auto server_manager = std::make_unique<ServerManager>(system);
10 : Interface(system_, std::move(module_), fsc_, name_) { 14
11 // clang-format off 15 server_manager->RegisterNamedService("bcat:a",
12 static const FunctionInfo functions[] = { 16 std::make_shared<IServiceCreator>(system, "bcat:a"));
13 {0, &BCAT::CreateBcatService, "CreateBcatService"}, 17 server_manager->RegisterNamedService("bcat:m",
14 {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"}, 18 std::make_shared<IServiceCreator>(system, "bcat:m"));
15 {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"}, 19 server_manager->RegisterNamedService("bcat:u",
16 {3, nullptr, "CreateDeliveryCacheProgressService"}, 20 std::make_shared<IServiceCreator>(system, "bcat:u"));
17 {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"}, 21 server_manager->RegisterNamedService("bcat:s",
18 }; 22 std::make_shared<IServiceCreator>(system, "bcat:s"));
19 // clang-format on 23
20 RegisterHandlers(functions); 24 server_manager->RegisterNamedService(
25 "news:a", std::make_shared<News::IServiceCreator>(system, 0xffffffff, "news:a"));
26 server_manager->RegisterNamedService(
27 "news:p", std::make_shared<News::IServiceCreator>(system, 0x1, "news:p"));
28 server_manager->RegisterNamedService(
29 "news:c", std::make_shared<News::IServiceCreator>(system, 0x2, "news:c"));
30 server_manager->RegisterNamedService(
31 "news:v", std::make_shared<News::IServiceCreator>(system, 0x4, "news:v"));
32 server_manager->RegisterNamedService(
33 "news:m", std::make_shared<News::IServiceCreator>(system, 0xd, "news:m"));
34
35 ServerManager::RunServer(std::move(server_manager));
21} 36}
22 37
23BCAT::~BCAT() = default;
24} // namespace Service::BCAT 38} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h
index db9d3c8c5..2aaffc693 100644
--- a/src/core/hle/service/bcat/bcat.h
+++ b/src/core/hle/service/bcat/bcat.h
@@ -3,7 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/bcat/bcat_module.h" 6#include "core/hle/service/service.h"
7 7
8namespace Core { 8namespace Core {
9class System; 9class System;
@@ -11,11 +11,6 @@ class System;
11 11
12namespace Service::BCAT { 12namespace Service::BCAT {
13 13
14class BCAT final : public Module::Interface { 14void LoopProcess(Core::System& system);
15public:
16 explicit BCAT(Core::System& system_, std::shared_ptr<Module> module_,
17 FileSystem::FileSystemController& fsc_, const char* name_);
18 ~BCAT() override;
19};
20 15
21} // namespace Service::BCAT 16} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp
deleted file mode 100644
index 76d7bb139..000000000
--- a/src/core/hle/service/bcat/bcat_module.cpp
+++ /dev/null
@@ -1,606 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cctype>
5#include <mbedtls/md5.h>
6#include "common/hex_util.h"
7#include "common/logging/log.h"
8#include "common/settings.h"
9#include "common/string_util.h"
10#include "core/core.h"
11#include "core/file_sys/vfs/vfs.h"
12#include "core/hle/kernel/k_readable_event.h"
13#include "core/hle/service/bcat/backend/backend.h"
14#include "core/hle/service/bcat/bcat.h"
15#include "core/hle/service/bcat/bcat_module.h"
16#include "core/hle/service/filesystem/filesystem.h"
17#include "core/hle/service/ipc_helpers.h"
18#include "core/hle/service/server_manager.h"
19
20namespace Service::BCAT {
21
22constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
23constexpr Result ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
24constexpr Result ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
25constexpr Result ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7};
26
27// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files
28// and if any of them have a non-zero result it just forwards that result. This is the FS error code
29// for permission denied, which is the closest approximation of this scenario.
30constexpr Result ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400};
31
32using BCATDigest = std::array<u8, 0x10>;
33
34namespace {
35
36u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
37 u64 out{};
38 std::memcpy(&out, id.data(), sizeof(u64));
39 return out;
40}
41
42// The digest is only used to determine if a file is unique compared to others of the same name.
43// Since the algorithm isn't ever checked in game, MD5 is safe.
44BCATDigest DigestFile(const FileSys::VirtualFile& file) {
45 BCATDigest out{};
46 const auto bytes = file->ReadAllBytes();
47 mbedtls_md5_ret(bytes.data(), bytes.size(), out.data());
48 return out;
49}
50
51// For a name to be valid it must be non-empty, must have a null terminating character as the final
52// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if
53// file.
54bool VerifyNameValidInternal(HLERequestContext& ctx, std::array<char, 0x20> name, char match_char) {
55 const auto null_chars = std::count(name.begin(), name.end(), 0);
56 const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) {
57 return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0';
58 });
59 if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') {
60 LOG_ERROR(Service_BCAT, "Name passed was invalid!");
61 IPC::ResponseBuilder rb{ctx, 2};
62 rb.Push(ERROR_INVALID_ARGUMENT);
63 return false;
64 }
65
66 return true;
67}
68
69bool VerifyNameValidDir(HLERequestContext& ctx, DirectoryName name) {
70 return VerifyNameValidInternal(ctx, name, '-');
71}
72
73bool VerifyNameValidFile(HLERequestContext& ctx, FileName name) {
74 return VerifyNameValidInternal(ctx, name, '.');
75}
76
77} // Anonymous namespace
78
79struct DeliveryCacheDirectoryEntry {
80 FileName name;
81 u64 size;
82 BCATDigest digest;
83};
84
85class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
86public:
87 explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_,
88 const DeliveryCacheProgressImpl& impl_)
89 : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} {
90 // clang-format off
91 static const FunctionInfo functions[] = {
92 {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"},
93 {1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"},
94 };
95 // clang-format on
96
97 RegisterHandlers(functions);
98 }
99
100private:
101 void GetEvent(HLERequestContext& ctx) {
102 LOG_DEBUG(Service_BCAT, "called");
103
104 IPC::ResponseBuilder rb{ctx, 2, 1};
105 rb.Push(ResultSuccess);
106 rb.PushCopyObjects(event);
107 }
108
109 void GetImpl(HLERequestContext& ctx) {
110 LOG_DEBUG(Service_BCAT, "called");
111
112 ctx.WriteBuffer(impl);
113
114 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(ResultSuccess);
116 }
117
118 Kernel::KReadableEvent& event;
119 const DeliveryCacheProgressImpl& impl;
120};
121
122class IBcatService final : public ServiceFramework<IBcatService> {
123public:
124 explicit IBcatService(Core::System& system_, Backend& backend_)
125 : ServiceFramework{system_, "IBcatService"}, backend{backend_},
126 progress{{
127 ProgressServiceBackend{system_, "Normal"},
128 ProgressServiceBackend{system_, "Directory"},
129 }} {
130 // clang-format off
131 static const FunctionInfo functions[] = {
132 {10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"},
133 {10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"},
134 {10200, nullptr, "CancelSyncDeliveryCacheRequest"},
135 {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
136 {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
137 {20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"},
138 {20301, nullptr, "RequestSuspendDeliveryTask"},
139 {20400, nullptr, "RegisterSystemApplicationDeliveryTask"},
140 {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
141 {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
142 {30100, &IBcatService::SetPassphrase, "SetPassphrase"},
143 {30101, nullptr, "Unknown30101"},
144 {30102, nullptr, "Unknown30102"},
145 {30200, nullptr, "RegisterBackgroundDeliveryTask"},
146 {30201, nullptr, "UnregisterBackgroundDeliveryTask"},
147 {30202, nullptr, "BlockDeliveryTask"},
148 {30203, nullptr, "UnblockDeliveryTask"},
149 {30210, nullptr, "SetDeliveryTaskTimer"},
150 {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"},
151 {90100, nullptr, "EnumerateBackgroundDeliveryTask"},
152 {90101, nullptr, "Unknown90101"},
153 {90200, nullptr, "GetDeliveryList"},
154 {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"},
155 {90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"},
156 {90300, nullptr, "GetPushNotificationLog"},
157 {90301, nullptr, "Unknown90301"},
158 };
159 // clang-format on
160 RegisterHandlers(functions);
161 }
162
163private:
164 enum class SyncType {
165 Normal,
166 Directory,
167 Count,
168 };
169
170 std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) {
171 auto& progress_backend{GetProgressBackend(type)};
172 return std::make_shared<IDeliveryCacheProgressService>(system, progress_backend.GetEvent(),
173 progress_backend.GetImpl());
174 }
175
176 void RequestSyncDeliveryCache(HLERequestContext& ctx) {
177 LOG_DEBUG(Service_BCAT, "called");
178
179 backend.Synchronize({system.GetApplicationProcessProgramID(),
180 GetCurrentBuildID(system.GetApplicationProcessBuildID())},
181 GetProgressBackend(SyncType::Normal));
182
183 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
184 rb.Push(ResultSuccess);
185 rb.PushIpcInterface(CreateProgressService(SyncType::Normal));
186 }
187
188 void RequestSyncDeliveryCacheWithDirectoryName(HLERequestContext& ctx) {
189 IPC::RequestParser rp{ctx};
190 const auto name_raw = rp.PopRaw<DirectoryName>();
191 const auto name =
192 Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
193
194 LOG_DEBUG(Service_BCAT, "called, name={}", name);
195
196 backend.SynchronizeDirectory({system.GetApplicationProcessProgramID(),
197 GetCurrentBuildID(system.GetApplicationProcessBuildID())},
198 name, GetProgressBackend(SyncType::Directory));
199
200 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
201 rb.Push(ResultSuccess);
202 rb.PushIpcInterface(CreateProgressService(SyncType::Directory));
203 }
204
205 void SetPassphrase(HLERequestContext& ctx) {
206 IPC::RequestParser rp{ctx};
207 const auto title_id = rp.PopRaw<u64>();
208
209 const auto passphrase_raw = ctx.ReadBuffer();
210
211 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
212 Common::HexToString(passphrase_raw));
213
214 if (title_id == 0) {
215 LOG_ERROR(Service_BCAT, "Invalid title ID!");
216 IPC::ResponseBuilder rb{ctx, 2};
217 rb.Push(ERROR_INVALID_ARGUMENT);
218 }
219
220 if (passphrase_raw.size() > 0x40) {
221 LOG_ERROR(Service_BCAT, "Passphrase too large!");
222 IPC::ResponseBuilder rb{ctx, 2};
223 rb.Push(ERROR_INVALID_ARGUMENT);
224 return;
225 }
226
227 Passphrase passphrase{};
228 std::memcpy(passphrase.data(), passphrase_raw.data(),
229 std::min(passphrase.size(), passphrase_raw.size()));
230
231 backend.SetPassphrase(title_id, passphrase);
232
233 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ResultSuccess);
235 }
236
237 void ClearDeliveryCacheStorage(HLERequestContext& ctx) {
238 IPC::RequestParser rp{ctx};
239 const auto title_id = rp.PopRaw<u64>();
240
241 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
242
243 if (title_id == 0) {
244 LOG_ERROR(Service_BCAT, "Invalid title ID!");
245 IPC::ResponseBuilder rb{ctx, 2};
246 rb.Push(ERROR_INVALID_ARGUMENT);
247 return;
248 }
249
250 if (!backend.Clear(title_id)) {
251 LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!");
252 IPC::ResponseBuilder rb{ctx, 2};
253 rb.Push(ERROR_FAILED_CLEAR_CACHE);
254 return;
255 }
256
257 IPC::ResponseBuilder rb{ctx, 2};
258 rb.Push(ResultSuccess);
259 }
260
261 ProgressServiceBackend& GetProgressBackend(SyncType type) {
262 return progress.at(static_cast<size_t>(type));
263 }
264
265 const ProgressServiceBackend& GetProgressBackend(SyncType type) const {
266 return progress.at(static_cast<size_t>(type));
267 }
268
269 Backend& backend;
270 std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
271};
272
273void Module::Interface::CreateBcatService(HLERequestContext& ctx) {
274 LOG_DEBUG(Service_BCAT, "called");
275
276 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
277 rb.Push(ResultSuccess);
278 rb.PushIpcInterface<IBcatService>(system, *backend);
279}
280
281class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
282public:
283 explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_)
284 : ServiceFramework{system_, "IDeliveryCacheFileService"}, root(std::move(root_)) {
285 // clang-format off
286 static const FunctionInfo functions[] = {
287 {0, &IDeliveryCacheFileService::Open, "Open"},
288 {1, &IDeliveryCacheFileService::Read, "Read"},
289 {2, &IDeliveryCacheFileService::GetSize, "GetSize"},
290 {3, &IDeliveryCacheFileService::GetDigest, "GetDigest"},
291 };
292 // clang-format on
293
294 RegisterHandlers(functions);
295 }
296
297private:
298 void Open(HLERequestContext& ctx) {
299 IPC::RequestParser rp{ctx};
300 const auto dir_name_raw = rp.PopRaw<DirectoryName>();
301 const auto file_name_raw = rp.PopRaw<FileName>();
302
303 const auto dir_name =
304 Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
305 const auto file_name =
306 Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size());
307
308 LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name);
309
310 if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw))
311 return;
312
313 if (current_file != nullptr) {
314 LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
315 IPC::ResponseBuilder rb{ctx, 2};
316 rb.Push(ERROR_ENTITY_ALREADY_OPEN);
317 return;
318 }
319
320 const auto dir = root->GetSubdirectory(dir_name);
321
322 if (dir == nullptr) {
323 LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name);
324 IPC::ResponseBuilder rb{ctx, 2};
325 rb.Push(ERROR_FAILED_OPEN_ENTITY);
326 return;
327 }
328
329 current_file = dir->GetFile(file_name);
330
331 if (current_file == nullptr) {
332 LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name);
333 IPC::ResponseBuilder rb{ctx, 2};
334 rb.Push(ERROR_FAILED_OPEN_ENTITY);
335 return;
336 }
337
338 IPC::ResponseBuilder rb{ctx, 2};
339 rb.Push(ResultSuccess);
340 }
341
342 void Read(HLERequestContext& ctx) {
343 IPC::RequestParser rp{ctx};
344 const auto offset{rp.PopRaw<u64>()};
345
346 auto size = ctx.GetWriteBufferSize();
347
348 LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size);
349
350 if (current_file == nullptr) {
351 LOG_ERROR(Service_BCAT, "There is no file currently open!");
352 IPC::ResponseBuilder rb{ctx, 2};
353 rb.Push(ERROR_NO_OPEN_ENTITY);
354 }
355
356 size = std::min<u64>(current_file->GetSize() - offset, size);
357 const auto buffer = current_file->ReadBytes(size, offset);
358 ctx.WriteBuffer(buffer);
359
360 IPC::ResponseBuilder rb{ctx, 4};
361 rb.Push(ResultSuccess);
362 rb.Push<u64>(buffer.size());
363 }
364
365 void GetSize(HLERequestContext& ctx) {
366 LOG_DEBUG(Service_BCAT, "called");
367
368 if (current_file == nullptr) {
369 LOG_ERROR(Service_BCAT, "There is no file currently open!");
370 IPC::ResponseBuilder rb{ctx, 2};
371 rb.Push(ERROR_NO_OPEN_ENTITY);
372 }
373
374 IPC::ResponseBuilder rb{ctx, 4};
375 rb.Push(ResultSuccess);
376 rb.Push<u64>(current_file->GetSize());
377 }
378
379 void GetDigest(HLERequestContext& ctx) {
380 LOG_DEBUG(Service_BCAT, "called");
381
382 if (current_file == nullptr) {
383 LOG_ERROR(Service_BCAT, "There is no file currently open!");
384 IPC::ResponseBuilder rb{ctx, 2};
385 rb.Push(ERROR_NO_OPEN_ENTITY);
386 }
387
388 IPC::ResponseBuilder rb{ctx, 6};
389 rb.Push(ResultSuccess);
390 rb.PushRaw(DigestFile(current_file));
391 }
392
393 FileSys::VirtualDir root;
394 FileSys::VirtualFile current_file;
395};
396
397class IDeliveryCacheDirectoryService final
398 : public ServiceFramework<IDeliveryCacheDirectoryService> {
399public:
400 explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_)
401 : ServiceFramework{system_, "IDeliveryCacheDirectoryService"}, root(std::move(root_)) {
402 // clang-format off
403 static const FunctionInfo functions[] = {
404 {0, &IDeliveryCacheDirectoryService::Open, "Open"},
405 {1, &IDeliveryCacheDirectoryService::Read, "Read"},
406 {2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"},
407 };
408 // clang-format on
409
410 RegisterHandlers(functions);
411 }
412
413private:
414 void Open(HLERequestContext& ctx) {
415 IPC::RequestParser rp{ctx};
416 const auto name_raw = rp.PopRaw<DirectoryName>();
417 const auto name =
418 Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
419
420 LOG_DEBUG(Service_BCAT, "called, name={}", name);
421
422 if (!VerifyNameValidDir(ctx, name_raw))
423 return;
424
425 if (current_dir != nullptr) {
426 LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
427 IPC::ResponseBuilder rb{ctx, 2};
428 rb.Push(ERROR_ENTITY_ALREADY_OPEN);
429 return;
430 }
431
432 current_dir = root->GetSubdirectory(name);
433
434 if (current_dir == nullptr) {
435 LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name);
436 IPC::ResponseBuilder rb{ctx, 2};
437 rb.Push(ERROR_FAILED_OPEN_ENTITY);
438 return;
439 }
440
441 IPC::ResponseBuilder rb{ctx, 2};
442 rb.Push(ResultSuccess);
443 }
444
445 void Read(HLERequestContext& ctx) {
446 auto write_size = ctx.GetWriteBufferNumElements<DeliveryCacheDirectoryEntry>();
447
448 LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size);
449
450 if (current_dir == nullptr) {
451 LOG_ERROR(Service_BCAT, "There is no open directory!");
452 IPC::ResponseBuilder rb{ctx, 2};
453 rb.Push(ERROR_NO_OPEN_ENTITY);
454 return;
455 }
456
457 const auto files = current_dir->GetFiles();
458 write_size = std::min<u64>(write_size, files.size());
459 std::vector<DeliveryCacheDirectoryEntry> entries(write_size);
460 std::transform(
461 files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) {
462 FileName name{};
463 std::memcpy(name.data(), file->GetName().data(),
464 std::min(file->GetName().size(), name.size()));
465 return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)};
466 });
467
468 ctx.WriteBuffer(entries);
469
470 IPC::ResponseBuilder rb{ctx, 3};
471 rb.Push(ResultSuccess);
472 rb.Push(static_cast<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry)));
473 }
474
475 void GetCount(HLERequestContext& ctx) {
476 LOG_DEBUG(Service_BCAT, "called");
477
478 if (current_dir == nullptr) {
479 LOG_ERROR(Service_BCAT, "There is no open directory!");
480 IPC::ResponseBuilder rb{ctx, 2};
481 rb.Push(ERROR_NO_OPEN_ENTITY);
482 return;
483 }
484
485 const auto files = current_dir->GetFiles();
486
487 IPC::ResponseBuilder rb{ctx, 3};
488 rb.Push(ResultSuccess);
489 rb.Push(static_cast<u32>(files.size()));
490 }
491
492 FileSys::VirtualDir root;
493 FileSys::VirtualDir current_dir;
494};
495
496class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
497public:
498 explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_)
499 : ServiceFramework{system_, "IDeliveryCacheStorageService"}, root(std::move(root_)) {
500 // clang-format off
501 static const FunctionInfo functions[] = {
502 {0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"},
503 {1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"},
504 {10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"},
505 };
506 // clang-format on
507
508 RegisterHandlers(functions);
509
510 for (const auto& subdir : root->GetSubdirectories()) {
511 DirectoryName name{};
512 std::memcpy(name.data(), subdir->GetName().data(),
513 std::min(sizeof(DirectoryName) - 1, subdir->GetName().size()));
514 entries.push_back(name);
515 }
516 }
517
518private:
519 void CreateFileService(HLERequestContext& ctx) {
520 LOG_DEBUG(Service_BCAT, "called");
521
522 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
523 rb.Push(ResultSuccess);
524 rb.PushIpcInterface<IDeliveryCacheFileService>(system, root);
525 }
526
527 void CreateDirectoryService(HLERequestContext& ctx) {
528 LOG_DEBUG(Service_BCAT, "called");
529
530 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
531 rb.Push(ResultSuccess);
532 rb.PushIpcInterface<IDeliveryCacheDirectoryService>(system, root);
533 }
534
535 void EnumerateDeliveryCacheDirectory(HLERequestContext& ctx) {
536 auto size = ctx.GetWriteBufferNumElements<DirectoryName>();
537
538 LOG_DEBUG(Service_BCAT, "called, size={:016X}", size);
539
540 size = std::min<u64>(size, entries.size() - next_read_index);
541 ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName));
542 next_read_index += size;
543
544 IPC::ResponseBuilder rb{ctx, 3};
545 rb.Push(ResultSuccess);
546 rb.Push(static_cast<u32>(size));
547 }
548
549 FileSys::VirtualDir root;
550 std::vector<DirectoryName> entries;
551 u64 next_read_index = 0;
552};
553
554void Module::Interface::CreateDeliveryCacheStorageService(HLERequestContext& ctx) {
555 LOG_DEBUG(Service_BCAT, "called");
556
557 const auto title_id = system.GetApplicationProcessProgramID();
558 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
559 rb.Push(ResultSuccess);
560 rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
561}
562
563void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(HLERequestContext& ctx) {
564 IPC::RequestParser rp{ctx};
565 const auto title_id = rp.PopRaw<u64>();
566
567 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
568
569 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
570 rb.Push(ResultSuccess);
571 rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
572}
573
574std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
575 DirectoryGetter getter) {
576 return std::make_unique<NullBackend>(std::move(getter));
577}
578
579Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
580 FileSystem::FileSystemController& fsc_, const char* name)
581 : ServiceFramework{system_, name}, fsc{fsc_}, module{std::move(module_)},
582 backend{CreateBackendFromSettings(system_,
583 [&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })} {}
584
585Module::Interface::~Interface() = default;
586
587void LoopProcess(Core::System& system) {
588 auto server_manager = std::make_unique<ServerManager>(system);
589 auto module = std::make_shared<Module>();
590
591 server_manager->RegisterNamedService(
592 "bcat:a",
593 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a"));
594 server_manager->RegisterNamedService(
595 "bcat:m",
596 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m"));
597 server_manager->RegisterNamedService(
598 "bcat:u",
599 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u"));
600 server_manager->RegisterNamedService(
601 "bcat:s",
602 std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:s"));
603 ServerManager::RunServer(std::move(server_manager));
604}
605
606} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_module.h b/src/core/hle/service/bcat/bcat_module.h
deleted file mode 100644
index 87576288b..000000000
--- a/src/core/hle/service/bcat/bcat_module.h
+++ /dev/null
@@ -1,46 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service {
13
14namespace FileSystem {
15class FileSystemController;
16} // namespace FileSystem
17
18namespace BCAT {
19
20class Backend;
21
22class Module final {
23public:
24 class Interface : public ServiceFramework<Interface> {
25 public:
26 explicit Interface(Core::System& system_, std::shared_ptr<Module> module_,
27 FileSystem::FileSystemController& fsc_, const char* name);
28 ~Interface() override;
29
30 void CreateBcatService(HLERequestContext& ctx);
31 void CreateDeliveryCacheStorageService(HLERequestContext& ctx);
32 void CreateDeliveryCacheStorageServiceWithApplicationId(HLERequestContext& ctx);
33
34 protected:
35 FileSystem::FileSystemController& fsc;
36
37 std::shared_ptr<Module> module;
38 std::unique_ptr<Backend> backend;
39 };
40};
41
42void LoopProcess(Core::System& system);
43
44} // namespace BCAT
45
46} // namespace Service
diff --git a/src/core/hle/service/bcat/bcat_result.h b/src/core/hle/service/bcat/bcat_result.h
new file mode 100644
index 000000000..edf8a6564
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_result.h
@@ -0,0 +1,15 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::BCAT {
9
10constexpr Result ResultInvalidArgument{ErrorModule::BCAT, 1};
11constexpr Result ResultFailedOpenEntity{ErrorModule::BCAT, 2};
12constexpr Result ResultEntityAlreadyOpen{ErrorModule::BCAT, 6};
13constexpr Result ResultNoOpenEntry{ErrorModule::BCAT, 7};
14
15} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_service.cpp b/src/core/hle/service/bcat/bcat_service.cpp
new file mode 100644
index 000000000..63b1072d2
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_service.cpp
@@ -0,0 +1,132 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/hex_util.h"
5#include "common/string_util.h"
6#include "core/core.h"
7#include "core/file_sys/errors.h"
8#include "core/hle/service/bcat/backend/backend.h"
9#include "core/hle/service/bcat/bcat_result.h"
10#include "core/hle/service/bcat/bcat_service.h"
11#include "core/hle/service/bcat/bcat_util.h"
12#include "core/hle/service/bcat/delivery_cache_progress_service.h"
13#include "core/hle/service/bcat/delivery_cache_storage_service.h"
14#include "core/hle/service/cmif_serialization.h"
15
16namespace Service::BCAT {
17
18static u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
19 u64 out{};
20 std::memcpy(&out, id.data(), sizeof(u64));
21 return out;
22}
23
24IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_)
25 : ServiceFramework{system_, "IBcatService"}, backend{backend_},
26 progress{{
27 ProgressServiceBackend{system_, "Normal"},
28 ProgressServiceBackend{system_, "Directory"},
29 }} {
30 // clang-format off
31 static const FunctionInfo functions[] = {
32 {10100, D<&IBcatService::RequestSyncDeliveryCache>, "RequestSyncDeliveryCache"},
33 {10101, D<&IBcatService::RequestSyncDeliveryCacheWithDirectoryName>, "RequestSyncDeliveryCacheWithDirectoryName"},
34 {10200, nullptr, "CancelSyncDeliveryCacheRequest"},
35 {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
36 {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
37 {20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"},
38 {20301, nullptr, "RequestSuspendDeliveryTask"},
39 {20400, nullptr, "RegisterSystemApplicationDeliveryTask"},
40 {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
41 {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
42 {30100, D<&IBcatService::SetPassphrase>, "SetPassphrase"},
43 {30101, nullptr, "Unknown30101"},
44 {30102, nullptr, "Unknown30102"},
45 {30200, nullptr, "RegisterBackgroundDeliveryTask"},
46 {30201, nullptr, "UnregisterBackgroundDeliveryTask"},
47 {30202, nullptr, "BlockDeliveryTask"},
48 {30203, nullptr, "UnblockDeliveryTask"},
49 {30210, nullptr, "SetDeliveryTaskTimer"},
50 {30300, D<&IBcatService::RegisterSystemApplicationDeliveryTasks>, "RegisterSystemApplicationDeliveryTasks"},
51 {90100, nullptr, "EnumerateBackgroundDeliveryTask"},
52 {90101, nullptr, "Unknown90101"},
53 {90200, nullptr, "GetDeliveryList"},
54 {90201, D<&IBcatService::ClearDeliveryCacheStorage>, "ClearDeliveryCacheStorage"},
55 {90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"},
56 {90300, nullptr, "GetPushNotificationLog"},
57 {90301, nullptr, "Unknown90301"},
58 };
59 // clang-format on
60 RegisterHandlers(functions);
61}
62
63IBcatService::~IBcatService() = default;
64
65Result IBcatService::RequestSyncDeliveryCache(
66 OutInterface<IDeliveryCacheProgressService> out_interface) {
67 LOG_DEBUG(Service_BCAT, "called");
68
69 auto& progress_backend{GetProgressBackend(SyncType::Normal)};
70 backend.Synchronize({system.GetApplicationProcessProgramID(),
71 GetCurrentBuildID(system.GetApplicationProcessBuildID())},
72 GetProgressBackend(SyncType::Normal));
73
74 *out_interface = std::make_shared<IDeliveryCacheProgressService>(
75 system, progress_backend.GetEvent(), progress_backend.GetImpl());
76 R_SUCCEED();
77}
78
79Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName(
80 const DirectoryName& name_raw, OutInterface<IDeliveryCacheProgressService> out_interface) {
81 const auto name = Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
82
83 LOG_DEBUG(Service_BCAT, "called, name={}", name);
84
85 auto& progress_backend{GetProgressBackend(SyncType::Directory)};
86 backend.SynchronizeDirectory({system.GetApplicationProcessProgramID(),
87 GetCurrentBuildID(system.GetApplicationProcessBuildID())},
88 name, progress_backend);
89
90 *out_interface = std::make_shared<IDeliveryCacheProgressService>(
91 system, progress_backend.GetEvent(), progress_backend.GetImpl());
92 R_SUCCEED();
93}
94
95Result IBcatService::SetPassphrase(u64 application_id,
96 InBuffer<BufferAttr_HipcPointer> passphrase_buffer) {
97 LOG_DEBUG(Service_BCAT, "called, application_id={:016X}, passphrase={}", application_id,
98 Common::HexToString(passphrase_buffer));
99
100 R_UNLESS(application_id != 0, ResultInvalidArgument);
101 R_UNLESS(passphrase_buffer.size() <= 0x40, ResultInvalidArgument);
102
103 Passphrase passphrase{};
104 std::memcpy(passphrase.data(), passphrase_buffer.data(),
105 std::min(passphrase.size(), passphrase_buffer.size()));
106
107 backend.SetPassphrase(application_id, passphrase);
108 R_SUCCEED();
109}
110
111Result IBcatService::RegisterSystemApplicationDeliveryTasks() {
112 LOG_WARNING(Service_BCAT, "(STUBBED) called");
113 R_SUCCEED();
114}
115
116Result IBcatService::ClearDeliveryCacheStorage(u64 application_id) {
117 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", application_id);
118
119 R_UNLESS(application_id != 0, ResultInvalidArgument);
120 R_UNLESS(backend.Clear(application_id), FileSys::ResultPermissionDenied);
121 R_SUCCEED();
122}
123
124ProgressServiceBackend& IBcatService::GetProgressBackend(SyncType type) {
125 return progress.at(static_cast<size_t>(type));
126}
127
128const ProgressServiceBackend& IBcatService::GetProgressBackend(SyncType type) const {
129 return progress.at(static_cast<size_t>(type));
130}
131
132} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_service.h b/src/core/hle/service/bcat/bcat_service.h
new file mode 100644
index 000000000..dda5a2d5f
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_service.h
@@ -0,0 +1,45 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/bcat/backend/backend.h"
7#include "core/hle/service/bcat/bcat_types.h"
8#include "core/hle/service/cmif_types.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::BCAT {
16class BcatBackend;
17class IDeliveryCacheStorageService;
18class IDeliveryCacheProgressService;
19
20class IBcatService final : public ServiceFramework<IBcatService> {
21public:
22 explicit IBcatService(Core::System& system_, BcatBackend& backend_);
23 ~IBcatService() override;
24
25private:
26 Result RequestSyncDeliveryCache(OutInterface<IDeliveryCacheProgressService> out_interface);
27
28 Result RequestSyncDeliveryCacheWithDirectoryName(
29 const DirectoryName& name, OutInterface<IDeliveryCacheProgressService> out_interface);
30
31 Result SetPassphrase(u64 application_id, InBuffer<BufferAttr_HipcPointer> passphrase_buffer);
32
33 Result RegisterSystemApplicationDeliveryTasks();
34
35 Result ClearDeliveryCacheStorage(u64 application_id);
36
37private:
38 ProgressServiceBackend& GetProgressBackend(SyncType type);
39 const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
40
41 BcatBackend& backend;
42 std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
43};
44
45} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_types.h b/src/core/hle/service/bcat/bcat_types.h
new file mode 100644
index 000000000..b35dab7c5
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_types.h
@@ -0,0 +1,66 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <functional>
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "core/file_sys/vfs/vfs_types.h"
12#include "core/hle/result.h"
13
14namespace Service::BCAT {
15
16using DirectoryName = std::array<char, 0x20>;
17using FileName = std::array<char, 0x20>;
18using BcatDigest = std::array<u8, 0x10>;
19using Passphrase = std::array<u8, 0x20>;
20using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
21
22enum class SyncType {
23 Normal,
24 Directory,
25 Count,
26};
27
28enum class DeliveryCacheProgressStatus : s32 {
29 None = 0x0,
30 Queued = 0x1,
31 Connecting = 0x2,
32 ProcessingDataList = 0x3,
33 Downloading = 0x4,
34 Committing = 0x5,
35 Done = 0x9,
36};
37
38struct DeliveryCacheDirectoryEntry {
39 FileName name;
40 u64 size;
41 BcatDigest digest;
42};
43
44struct TitleIDVersion {
45 u64 title_id;
46 u64 build_id;
47};
48
49struct DeliveryCacheProgressImpl {
50 DeliveryCacheProgressStatus status;
51 Result result;
52 DirectoryName current_directory;
53 FileName current_file;
54 s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
55 s64 current_total_bytes; ///< Bytes total on current file.
56 s64 total_downloaded_bytes; ///< Bytes downloaded on overall download.
57 s64 total_bytes; ///< Bytes total on overall download.
58 INSERT_PADDING_BYTES_NOINIT(
59 0x198); ///< Appears to be unused in official code, possibly reserved for future use.
60};
61static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200,
62 "DeliveryCacheProgressImpl has incorrect size.");
63static_assert(std::is_trivial_v<DeliveryCacheProgressImpl>,
64 "DeliveryCacheProgressImpl type must be trivially copyable.");
65
66} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat_util.h b/src/core/hle/service/bcat/bcat_util.h
new file mode 100644
index 000000000..6bf2657ee
--- /dev/null
+++ b/src/core/hle/service/bcat/bcat_util.h
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <cctype>
8#include <mbedtls/md5.h>
9
10#include "core/hle/service/bcat/bcat_result.h"
11#include "core/hle/service/bcat/bcat_types.h"
12
13namespace Service::BCAT {
14
15// For a name to be valid it must be non-empty, must have a null terminating character as the final
16// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if
17// file.
18constexpr Result VerifyNameValidInternal(std::array<char, 0x20> name, char match_char) {
19 const auto null_chars = std::count(name.begin(), name.end(), 0);
20 const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) {
21 return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0';
22 });
23 if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') {
24 LOG_ERROR(Service_BCAT, "Name passed was invalid!");
25 return ResultInvalidArgument;
26 }
27
28 return ResultSuccess;
29}
30
31constexpr Result VerifyNameValidDir(DirectoryName name) {
32 return VerifyNameValidInternal(name, '-');
33}
34
35constexpr Result VerifyNameValidFile(FileName name) {
36 return VerifyNameValidInternal(name, '.');
37}
38
39} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_directory_service.cpp b/src/core/hle/service/bcat/delivery_cache_directory_service.cpp
new file mode 100644
index 000000000..01f08a2fc
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_directory_service.cpp
@@ -0,0 +1,80 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/string_util.h"
5#include "core/file_sys/vfs/vfs_types.h"
6#include "core/hle/service/bcat/bcat_result.h"
7#include "core/hle/service/bcat/bcat_util.h"
8#include "core/hle/service/bcat/delivery_cache_directory_service.h"
9#include "core/hle/service/cmif_serialization.h"
10
11namespace Service::BCAT {
12
13// The digest is only used to determine if a file is unique compared to others of the same name.
14// Since the algorithm isn't ever checked in game, MD5 is safe.
15static BcatDigest DigestFile(const FileSys::VirtualFile& file) {
16 BcatDigest out{};
17 const auto bytes = file->ReadAllBytes();
18 mbedtls_md5_ret(bytes.data(), bytes.size(), out.data());
19 return out;
20}
21
22IDeliveryCacheDirectoryService::IDeliveryCacheDirectoryService(Core::System& system_,
23 FileSys::VirtualDir root_)
24 : ServiceFramework{system_, "IDeliveryCacheDirectoryService"}, root(std::move(root_)) {
25 // clang-format off
26 static const FunctionInfo functions[] = {
27 {0, D<&IDeliveryCacheDirectoryService::Open>, "Open"},
28 {1, D<&IDeliveryCacheDirectoryService::Read>, "Read"},
29 {2, D<&IDeliveryCacheDirectoryService::GetCount>, "GetCount"},
30 };
31 // clang-format on
32
33 RegisterHandlers(functions);
34}
35
36IDeliveryCacheDirectoryService::~IDeliveryCacheDirectoryService() = default;
37
38Result IDeliveryCacheDirectoryService::Open(const DirectoryName& dir_name_raw) {
39 const auto dir_name =
40 Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
41
42 LOG_DEBUG(Service_BCAT, "called, dir_name={}", dir_name);
43
44 R_TRY(VerifyNameValidDir(dir_name_raw));
45 R_UNLESS(current_dir == nullptr, ResultEntityAlreadyOpen);
46
47 const auto dir = root->GetSubdirectory(dir_name);
48 R_UNLESS(dir != nullptr, ResultFailedOpenEntity);
49
50 R_SUCCEED();
51}
52
53Result IDeliveryCacheDirectoryService::Read(
54 Out<s32> out_count, OutArray<DeliveryCacheDirectoryEntry, BufferAttr_HipcMapAlias> out_buffer) {
55 LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", out_buffer.size());
56
57 R_UNLESS(current_dir != nullptr, ResultNoOpenEntry);
58
59 const auto files = current_dir->GetFiles();
60 *out_count = static_cast<s32>(std::min(files.size(), out_buffer.size()));
61 std::transform(files.begin(), files.begin() + *out_count, out_buffer.begin(),
62 [](const auto& file) {
63 FileName name{};
64 std::memcpy(name.data(), file->GetName().data(),
65 std::min(file->GetName().size(), name.size()));
66 return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)};
67 });
68 R_SUCCEED();
69}
70
71Result IDeliveryCacheDirectoryService::GetCount(Out<s32> out_count) {
72 LOG_DEBUG(Service_BCAT, "called");
73
74 R_UNLESS(current_dir != nullptr, ResultNoOpenEntry);
75
76 *out_count = static_cast<s32>(current_dir->GetFiles().size());
77 R_SUCCEED();
78}
79
80} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_directory_service.h b/src/core/hle/service/bcat/delivery_cache_directory_service.h
new file mode 100644
index 000000000..b902c6495
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_directory_service.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/file_sys/vfs/vfs.h"
7#include "core/hle/service/bcat/bcat_types.h"
8#include "core/hle/service/cmif_types.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::BCAT {
16
17class IDeliveryCacheDirectoryService final
18 : public ServiceFramework<IDeliveryCacheDirectoryService> {
19public:
20 explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_);
21 ~IDeliveryCacheDirectoryService() override;
22
23private:
24 Result Open(const DirectoryName& dir_name_raw);
25 Result Read(Out<s32> out_count,
26 OutArray<DeliveryCacheDirectoryEntry, BufferAttr_HipcMapAlias> out_buffer);
27 Result GetCount(Out<s32> out_count);
28
29 FileSys::VirtualDir root;
30 FileSys::VirtualDir current_dir;
31};
32
33} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_file_service.cpp b/src/core/hle/service/bcat/delivery_cache_file_service.cpp
new file mode 100644
index 000000000..b75fac4bf
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_file_service.cpp
@@ -0,0 +1,82 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/string_util.h"
5#include "core/hle/service/bcat/bcat_result.h"
6#include "core/hle/service/bcat/bcat_util.h"
7#include "core/hle/service/bcat/delivery_cache_file_service.h"
8#include "core/hle/service/cmif_serialization.h"
9
10namespace Service::BCAT {
11
12IDeliveryCacheFileService::IDeliveryCacheFileService(Core::System& system_,
13 FileSys::VirtualDir root_)
14 : ServiceFramework{system_, "IDeliveryCacheFileService"}, root(std::move(root_)) {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, D<&IDeliveryCacheFileService::Open>, "Open"},
18 {1, D<&IDeliveryCacheFileService::Read>, "Read"},
19 {2, D<&IDeliveryCacheFileService::GetSize>, "GetSize"},
20 {3, D<&IDeliveryCacheFileService::GetDigest>, "GetDigest"},
21 };
22 // clang-format on
23
24 RegisterHandlers(functions);
25}
26
27IDeliveryCacheFileService::~IDeliveryCacheFileService() = default;
28
29Result IDeliveryCacheFileService::Open(const DirectoryName& dir_name_raw,
30 const FileName& file_name_raw) {
31 const auto dir_name =
32 Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
33 const auto file_name =
34 Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size());
35
36 LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name);
37
38 R_TRY(VerifyNameValidDir(dir_name_raw));
39 R_TRY(VerifyNameValidDir(file_name_raw));
40 R_UNLESS(current_file == nullptr, ResultEntityAlreadyOpen);
41
42 const auto dir = root->GetSubdirectory(dir_name);
43 R_UNLESS(dir != nullptr, ResultFailedOpenEntity);
44
45 current_file = dir->GetFile(file_name);
46 R_UNLESS(current_file != nullptr, ResultFailedOpenEntity);
47
48 R_SUCCEED();
49}
50
51Result IDeliveryCacheFileService::Read(Out<u64> out_buffer_size, u64 offset,
52 OutBuffer<BufferAttr_HipcMapAlias> out_buffer) {
53 LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, out_buffer.size());
54
55 R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
56
57 *out_buffer_size = std::min<u64>(current_file->GetSize() - offset, out_buffer.size());
58 const auto buffer = current_file->ReadBytes(*out_buffer_size, offset);
59 memcpy(out_buffer.data(), buffer.data(), buffer.size());
60
61 R_SUCCEED();
62}
63
64Result IDeliveryCacheFileService::GetSize(Out<u64> out_size) {
65 LOG_DEBUG(Service_BCAT, "called");
66
67 R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
68
69 *out_size = current_file->GetSize();
70 R_SUCCEED();
71}
72
73Result IDeliveryCacheFileService::GetDigest(Out<BcatDigest> out_digest) {
74 LOG_DEBUG(Service_BCAT, "called");
75
76 R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
77
78 //*out_digest = DigestFile(current_file);
79 R_SUCCEED();
80}
81
82} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_file_service.h b/src/core/hle/service/bcat/delivery_cache_file_service.h
new file mode 100644
index 000000000..e1012e687
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_file_service.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/file_sys/vfs/vfs.h"
7#include "core/hle/service/bcat/bcat_types.h"
8#include "core/hle/service/cmif_types.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::BCAT {
16
17class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
18public:
19 explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_);
20 ~IDeliveryCacheFileService() override;
21
22private:
23 Result Open(const DirectoryName& dir_name_raw, const FileName& file_name_raw);
24 Result Read(Out<u64> out_buffer_size, u64 offset,
25 OutBuffer<BufferAttr_HipcMapAlias> out_buffer);
26 Result GetSize(Out<u64> out_size);
27 Result GetDigest(Out<BcatDigest> out_digest);
28
29 FileSys::VirtualDir root;
30 FileSys::VirtualFile current_file;
31};
32
33} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_progress_service.cpp b/src/core/hle/service/bcat/delivery_cache_progress_service.cpp
new file mode 100644
index 000000000..79e7e0d95
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_progress_service.cpp
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/bcat/bcat_types.h"
5#include "core/hle/service/bcat/delivery_cache_progress_service.h"
6#include "core/hle/service/cmif_serialization.h"
7
8namespace Service::BCAT {
9
10IDeliveryCacheProgressService::IDeliveryCacheProgressService(Core::System& system_,
11 Kernel::KReadableEvent& event_,
12 const DeliveryCacheProgressImpl& impl_)
13 : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} {
14 // clang-format off
15 static const FunctionInfo functions[] = {
16 {0, D<&IDeliveryCacheProgressService::GetEvent>, "Get"},
17 {1, D<&IDeliveryCacheProgressService::GetImpl>, "Get"},
18 };
19 // clang-format on
20
21 RegisterHandlers(functions);
22}
23
24IDeliveryCacheProgressService::~IDeliveryCacheProgressService() = default;
25
26Result IDeliveryCacheProgressService::GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
27 LOG_DEBUG(Service_BCAT, "called");
28
29 *out_event = &event;
30 R_SUCCEED();
31}
32
33Result IDeliveryCacheProgressService::GetImpl(
34 OutLargeData<DeliveryCacheProgressImpl, BufferAttr_HipcPointer> out_impl) {
35 LOG_DEBUG(Service_BCAT, "called");
36
37 *out_impl = impl;
38 R_SUCCEED();
39}
40
41} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_progress_service.h b/src/core/hle/service/bcat/delivery_cache_progress_service.h
new file mode 100644
index 000000000..f81a13980
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_progress_service.h
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Kernel {
14class KEvent;
15class KReadableEvent;
16} // namespace Kernel
17
18namespace Service::BCAT {
19struct DeliveryCacheProgressImpl;
20
21class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
22public:
23 explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_,
24 const DeliveryCacheProgressImpl& impl_);
25 ~IDeliveryCacheProgressService() override;
26
27private:
28 Result GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
29 Result GetImpl(OutLargeData<DeliveryCacheProgressImpl, BufferAttr_HipcPointer> out_impl);
30
31 Kernel::KReadableEvent& event;
32 const DeliveryCacheProgressImpl& impl;
33};
34
35} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_storage_service.cpp b/src/core/hle/service/bcat/delivery_cache_storage_service.cpp
new file mode 100644
index 000000000..4c79d71f4
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_storage_service.cpp
@@ -0,0 +1,57 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/bcat/bcat_result.h"
5#include "core/hle/service/bcat/delivery_cache_directory_service.h"
6#include "core/hle/service/bcat/delivery_cache_file_service.h"
7#include "core/hle/service/bcat/delivery_cache_storage_service.h"
8#include "core/hle/service/cmif_serialization.h"
9
10namespace Service::BCAT {
11
12IDeliveryCacheStorageService::IDeliveryCacheStorageService(Core::System& system_,
13 FileSys::VirtualDir root_)
14 : ServiceFramework{system_, "IDeliveryCacheStorageService"}, root(std::move(root_)) {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, D<&IDeliveryCacheStorageService::CreateFileService>, "CreateFileService"},
18 {1, D<&IDeliveryCacheStorageService::CreateDirectoryService>, "CreateDirectoryService"},
19 {10, D<&IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory>, "EnumerateDeliveryCacheDirectory"},
20 };
21 // clang-format on
22
23 RegisterHandlers(functions);
24}
25
26IDeliveryCacheStorageService::~IDeliveryCacheStorageService() = default;
27
28Result IDeliveryCacheStorageService::CreateFileService(
29 OutInterface<IDeliveryCacheFileService> out_interface) {
30 LOG_DEBUG(Service_BCAT, "called");
31
32 *out_interface = std::make_shared<IDeliveryCacheFileService>(system, root);
33 R_SUCCEED();
34}
35
36Result IDeliveryCacheStorageService::CreateDirectoryService(
37 OutInterface<IDeliveryCacheDirectoryService> out_interface) {
38 LOG_DEBUG(Service_BCAT, "called");
39
40 *out_interface = std::make_shared<IDeliveryCacheDirectoryService>(system, root);
41 R_SUCCEED();
42}
43
44Result IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory(
45 Out<s32> out_directory_count,
46 OutArray<DirectoryName, BufferAttr_HipcMapAlias> out_directories) {
47 LOG_DEBUG(Service_BCAT, "called, size={:016X}", out_directories.size());
48
49 *out_directory_count =
50 static_cast<s32>(std::min(out_directories.size(), entries.size() - next_read_index));
51 memcpy(out_directories.data(), entries.data() + next_read_index,
52 *out_directory_count * sizeof(DirectoryName));
53 next_read_index += *out_directory_count;
54 R_SUCCEED();
55}
56
57} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/delivery_cache_storage_service.h b/src/core/hle/service/bcat/delivery_cache_storage_service.h
new file mode 100644
index 000000000..3b8dfb1a3
--- /dev/null
+++ b/src/core/hle/service/bcat/delivery_cache_storage_service.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/file_sys/vfs/vfs.h"
7#include "core/hle/service/bcat/bcat_types.h"
8#include "core/hle/service/cmif_types.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::BCAT {
16class IDeliveryCacheFileService;
17class IDeliveryCacheDirectoryService;
18
19class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
20public:
21 explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_);
22 ~IDeliveryCacheStorageService() override;
23
24private:
25 Result CreateFileService(OutInterface<IDeliveryCacheFileService> out_interface);
26 Result CreateDirectoryService(OutInterface<IDeliveryCacheDirectoryService> out_interface);
27 Result EnumerateDeliveryCacheDirectory(
28 Out<s32> out_directory_count,
29 OutArray<DirectoryName, BufferAttr_HipcMapAlias> out_directories);
30
31 FileSys::VirtualDir root;
32 std::vector<DirectoryName> entries;
33 std::size_t next_read_index = 0;
34};
35
36} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp
new file mode 100644
index 000000000..ed393f7a2
--- /dev/null
+++ b/src/core/hle/service/bcat/news/newly_arrived_event_holder.cpp
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/bcat/news/newly_arrived_event_holder.h"
5#include "core/hle/service/cmif_serialization.h"
6
7namespace Service::News {
8
9INewlyArrivedEventHolder::INewlyArrivedEventHolder(Core::System& system_)
10 : ServiceFramework{system_, "INewlyArrivedEventHolder"}, service_context{
11 system_,
12 "INewlyArrivedEventHolder"} {
13 // clang-format off
14 static const FunctionInfo functions[] = {
15 {0, D<&INewlyArrivedEventHolder::Get>, "Get"},
16 };
17 // clang-format on
18
19 RegisterHandlers(functions);
20 arrived_event = service_context.CreateEvent("INewlyArrivedEventHolder::ArrivedEvent");
21}
22
23INewlyArrivedEventHolder::~INewlyArrivedEventHolder() {
24 service_context.CloseEvent(arrived_event);
25}
26
27Result INewlyArrivedEventHolder::Get(OutCopyHandle<Kernel::KReadableEvent> out_event) {
28 LOG_INFO(Service_BCAT, "called");
29
30 *out_event = &arrived_event->GetReadableEvent();
31 R_SUCCEED();
32}
33
34} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/newly_arrived_event_holder.h b/src/core/hle/service/bcat/news/newly_arrived_event_holder.h
new file mode 100644
index 000000000..6cc9ae099
--- /dev/null
+++ b/src/core/hle/service/bcat/news/newly_arrived_event_holder.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/service.h"
9
10namespace Core {
11class System;
12}
13
14namespace Kernel {
15class KEvent;
16class KReadableEvent;
17} // namespace Kernel
18
19namespace Service::News {
20
21class INewlyArrivedEventHolder final : public ServiceFramework<INewlyArrivedEventHolder> {
22public:
23 explicit INewlyArrivedEventHolder(Core::System& system_);
24 ~INewlyArrivedEventHolder() override;
25
26private:
27 Result Get(OutCopyHandle<Kernel::KReadableEvent> out_event);
28
29 Kernel::KEvent* arrived_event;
30 KernelHelpers::ServiceContext service_context;
31};
32
33} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_data_service.cpp b/src/core/hle/service/bcat/news/news_data_service.cpp
new file mode 100644
index 000000000..08103c9c3
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_data_service.cpp
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/bcat/news/news_data_service.h"
5
6namespace Service::News {
7
8INewsDataService::INewsDataService(Core::System& system_)
9 : ServiceFramework{system_, "INewsDataService"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "Open"},
13 {1, nullptr, "OpenWithNewsRecordV1"},
14 {2, nullptr, "Read"},
15 {3, nullptr, "GetSize"},
16 {1001, nullptr, "OpenWithNewsRecord"},
17 };
18 // clang-format on
19
20 RegisterHandlers(functions);
21}
22
23INewsDataService::~INewsDataService() = default;
24
25} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_data_service.h b/src/core/hle/service/bcat/news/news_data_service.h
new file mode 100644
index 000000000..12082ada4
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_data_service.h
@@ -0,0 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::News {
13
14class INewsDataService final : public ServiceFramework<INewsDataService> {
15public:
16 explicit INewsDataService(Core::System& system_);
17 ~INewsDataService() override;
18};
19
20} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_database_service.cpp b/src/core/hle/service/bcat/news/news_database_service.cpp
new file mode 100644
index 000000000..b94ef0636
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_database_service.cpp
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/bcat/news/news_database_service.h"
5#include "core/hle/service/cmif_serialization.h"
6
7namespace Service::News {
8
9INewsDatabaseService::INewsDatabaseService(Core::System& system_)
10 : ServiceFramework{system_, "INewsDatabaseService"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, nullptr, "GetListV1"},
14 {1, D<&INewsDatabaseService::Count>, "Count"},
15 {2, nullptr, "CountWithKey"},
16 {3, nullptr, "UpdateIntegerValue"},
17 {4, D<&INewsDatabaseService::UpdateIntegerValueWithAddition>, "UpdateIntegerValueWithAddition"},
18 {5, nullptr, "UpdateStringValue"},
19 {1000, D<&INewsDatabaseService::GetList>, "GetList"},
20 };
21 // clang-format on
22
23 RegisterHandlers(functions);
24}
25
26INewsDatabaseService::~INewsDatabaseService() = default;
27
28Result INewsDatabaseService::Count(Out<s32> out_count,
29 InBuffer<BufferAttr_HipcPointer> buffer_data) {
30 LOG_WARNING(Service_BCAT, "(STUBBED) called, buffer_size={}", buffer_data.size());
31 *out_count = 0;
32 R_SUCCEED();
33}
34
35Result INewsDatabaseService::UpdateIntegerValueWithAddition(
36 u32 value, InBuffer<BufferAttr_HipcPointer> buffer_data_1,
37 InBuffer<BufferAttr_HipcPointer> buffer_data_2) {
38 LOG_WARNING(Service_BCAT, "(STUBBED) called, value={}, buffer_size_1={}, buffer_data_2={}",
39 value, buffer_data_1.size(), buffer_data_2.size());
40 R_SUCCEED();
41}
42
43Result INewsDatabaseService::GetList(Out<s32> out_count, u32 value,
44 OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data,
45 InBuffer<BufferAttr_HipcPointer> buffer_data_1,
46 InBuffer<BufferAttr_HipcPointer> buffer_data_2) {
47 LOG_WARNING(Service_BCAT, "(STUBBED) called, value={}, buffer_size_1={}, buffer_data_2={}",
48 value, buffer_data_1.size(), buffer_data_2.size());
49 *out_count = 0;
50 R_SUCCEED();
51}
52
53} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_database_service.h b/src/core/hle/service/bcat/news/news_database_service.h
new file mode 100644
index 000000000..860b7074c
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_database_service.h
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::News {
14
15class INewsDatabaseService final : public ServiceFramework<INewsDatabaseService> {
16public:
17 explicit INewsDatabaseService(Core::System& system_);
18 ~INewsDatabaseService() override;
19
20private:
21 Result Count(Out<s32> out_count, InBuffer<BufferAttr_HipcPointer> buffer_data);
22
23 Result UpdateIntegerValueWithAddition(u32 value, InBuffer<BufferAttr_HipcPointer> buffer_data_1,
24 InBuffer<BufferAttr_HipcPointer> buffer_data_2);
25
26 Result GetList(Out<s32> out_count, u32 value,
27 OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data,
28 InBuffer<BufferAttr_HipcPointer> buffer_data_1,
29 InBuffer<BufferAttr_HipcPointer> buffer_data_2);
30};
31
32} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_service.cpp b/src/core/hle/service/bcat/news/news_service.cpp
new file mode 100644
index 000000000..bc6c2afd2
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_service.cpp
@@ -0,0 +1,57 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/bcat/news/news_service.h"
5#include "core/hle/service/cmif_serialization.h"
6
7namespace Service::News {
8
9INewsService::INewsService(Core::System& system_) : ServiceFramework{system_, "INewsService"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {10100, nullptr, "PostLocalNews"},
13 {20100, nullptr, "SetPassphrase"},
14 {30100, D<&INewsService::GetSubscriptionStatus>, "GetSubscriptionStatus"},
15 {30101, nullptr, "GetTopicList"},
16 {30110, nullptr, "Unknown30110"},
17 {30200, D<&INewsService::IsSystemUpdateRequired>, "IsSystemUpdateRequired"},
18 {30201, nullptr, "Unknown30201"},
19 {30210, nullptr, "Unknown30210"},
20 {30300, nullptr, "RequestImmediateReception"},
21 {30400, nullptr, "DecodeArchiveFile"},
22 {30500, nullptr, "Unknown30500"},
23 {30900, nullptr, "Unknown30900"},
24 {30901, nullptr, "Unknown30901"},
25 {30902, nullptr, "Unknown30902"},
26 {40100, nullptr, "SetSubscriptionStatus"},
27 {40101, D<&INewsService::RequestAutoSubscription>, "RequestAutoSubscription"},
28 {40200, nullptr, "ClearStorage"},
29 {40201, nullptr, "ClearSubscriptionStatusAll"},
30 {90100, nullptr, "GetNewsDatabaseDump"},
31 };
32 // clang-format on
33
34 RegisterHandlers(functions);
35}
36
37INewsService::~INewsService() = default;
38
39Result INewsService::GetSubscriptionStatus(Out<u32> out_status,
40 InBuffer<BufferAttr_HipcPointer> buffer_data) {
41 LOG_WARNING(Service_BCAT, "(STUBBED) called, buffer_size={}", buffer_data.size());
42 *out_status = 0;
43 R_SUCCEED();
44}
45
46Result INewsService::IsSystemUpdateRequired(Out<bool> out_is_system_update_required) {
47 LOG_WARNING(Service_BCAT, "(STUBBED) called");
48 *out_is_system_update_required = false;
49 R_SUCCEED();
50}
51
52Result INewsService::RequestAutoSubscription(u64 value) {
53 LOG_WARNING(Service_BCAT, "(STUBBED) called");
54 R_SUCCEED();
55}
56
57} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/news_service.h b/src/core/hle/service/bcat/news/news_service.h
new file mode 100644
index 000000000..f1716a302
--- /dev/null
+++ b/src/core/hle/service/bcat/news/news_service.h
@@ -0,0 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::News {
14
15class INewsService final : public ServiceFramework<INewsService> {
16public:
17 explicit INewsService(Core::System& system_);
18 ~INewsService() override;
19
20private:
21 Result GetSubscriptionStatus(Out<u32> out_status, InBuffer<BufferAttr_HipcPointer> buffer_data);
22
23 Result IsSystemUpdateRequired(Out<bool> out_is_system_update_required);
24
25 Result RequestAutoSubscription(u64 value);
26};
27
28} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/overwrite_event_holder.cpp b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp
new file mode 100644
index 000000000..1712971e4
--- /dev/null
+++ b/src/core/hle/service/bcat/news/overwrite_event_holder.cpp
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/bcat/news/overwrite_event_holder.h"
5#include "core/hle/service/cmif_serialization.h"
6
7namespace Service::News {
8
9IOverwriteEventHolder::IOverwriteEventHolder(Core::System& system_)
10 : ServiceFramework{system_, "IOverwriteEventHolder"}, service_context{system_,
11 "IOverwriteEventHolder"} {
12 // clang-format off
13 static const FunctionInfo functions[] = {
14 {0, D<&IOverwriteEventHolder::Get>, "Get"},
15 };
16 // clang-format on
17
18 RegisterHandlers(functions);
19 overwrite_event = service_context.CreateEvent("IOverwriteEventHolder::OverwriteEvent");
20}
21
22IOverwriteEventHolder::~IOverwriteEventHolder() {
23 service_context.CloseEvent(overwrite_event);
24}
25
26Result IOverwriteEventHolder::Get(OutCopyHandle<Kernel::KReadableEvent> out_event) {
27 LOG_INFO(Service_BCAT, "called");
28
29 *out_event = &overwrite_event->GetReadableEvent();
30 R_SUCCEED();
31}
32
33} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/overwrite_event_holder.h b/src/core/hle/service/bcat/news/overwrite_event_holder.h
new file mode 100644
index 000000000..cdc87d782
--- /dev/null
+++ b/src/core/hle/service/bcat/news/overwrite_event_holder.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/service.h"
9
10namespace Core {
11class System;
12}
13
14namespace Kernel {
15class KEvent;
16class KReadableEvent;
17} // namespace Kernel
18
19namespace Service::News {
20
21class IOverwriteEventHolder final : public ServiceFramework<IOverwriteEventHolder> {
22public:
23 explicit IOverwriteEventHolder(Core::System& system_);
24 ~IOverwriteEventHolder() override;
25
26private:
27 Result Get(OutCopyHandle<Kernel::KReadableEvent> out_event);
28
29 Kernel::KEvent* overwrite_event;
30 KernelHelpers::ServiceContext service_context;
31};
32
33} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/service_creator.cpp b/src/core/hle/service/bcat/news/service_creator.cpp
new file mode 100644
index 000000000..a1b22c004
--- /dev/null
+++ b/src/core/hle/service/bcat/news/service_creator.cpp
@@ -0,0 +1,64 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/bcat/news/newly_arrived_event_holder.h"
5#include "core/hle/service/bcat/news/news_data_service.h"
6#include "core/hle/service/bcat/news/news_database_service.h"
7#include "core/hle/service/bcat/news/news_service.h"
8#include "core/hle/service/bcat/news/overwrite_event_holder.h"
9#include "core/hle/service/bcat/news/service_creator.h"
10#include "core/hle/service/cmif_serialization.h"
11
12namespace Service::News {
13
14IServiceCreator::IServiceCreator(Core::System& system_, u32 permissions_, const char* name_)
15 : ServiceFramework{system_, name_}, permissions{permissions_} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {0, D<&IServiceCreator::CreateNewsService>, "CreateNewsService"},
19 {1, D<&IServiceCreator::CreateNewlyArrivedEventHolder>, "CreateNewlyArrivedEventHolder"},
20 {2, D<&IServiceCreator::CreateNewsDataService>, "CreateNewsDataService"},
21 {3, D<&IServiceCreator::CreateNewsDatabaseService>, "CreateNewsDatabaseService"},
22 {4, D<&IServiceCreator::CreateOverwriteEventHolder>, "CreateOverwriteEventHolder"},
23 };
24 // clang-format on
25
26 RegisterHandlers(functions);
27}
28
29IServiceCreator::~IServiceCreator() = default;
30
31Result IServiceCreator::CreateNewsService(OutInterface<INewsService> out_interface) {
32 LOG_INFO(Service_BCAT, "called");
33 *out_interface = std::make_shared<INewsService>(system);
34 R_SUCCEED();
35}
36
37Result IServiceCreator::CreateNewlyArrivedEventHolder(
38 OutInterface<INewlyArrivedEventHolder> out_interface) {
39 LOG_INFO(Service_BCAT, "called");
40 *out_interface = std::make_shared<INewlyArrivedEventHolder>(system);
41 R_SUCCEED();
42}
43
44Result IServiceCreator::CreateNewsDataService(OutInterface<INewsDataService> out_interface) {
45 LOG_INFO(Service_BCAT, "called");
46 *out_interface = std::make_shared<INewsDataService>(system);
47 R_SUCCEED();
48}
49
50Result IServiceCreator::CreateNewsDatabaseService(
51 OutInterface<INewsDatabaseService> out_interface) {
52 LOG_INFO(Service_BCAT, "called");
53 *out_interface = std::make_shared<INewsDatabaseService>(system);
54 R_SUCCEED();
55}
56
57Result IServiceCreator::CreateOverwriteEventHolder(
58 OutInterface<IOverwriteEventHolder> out_interface) {
59 LOG_INFO(Service_BCAT, "called");
60 *out_interface = std::make_shared<IOverwriteEventHolder>(system);
61 R_SUCCEED();
62}
63
64} // namespace Service::News
diff --git a/src/core/hle/service/bcat/news/service_creator.h b/src/core/hle/service/bcat/news/service_creator.h
new file mode 100644
index 000000000..5a62e7c1a
--- /dev/null
+++ b/src/core/hle/service/bcat/news/service_creator.h
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::News {
14class INewsService;
15class INewlyArrivedEventHolder;
16class INewsDataService;
17class INewsDatabaseService;
18class IOverwriteEventHolder;
19
20class IServiceCreator final : public ServiceFramework<IServiceCreator> {
21public:
22 explicit IServiceCreator(Core::System& system_, u32 permissions_, const char* name_);
23 ~IServiceCreator() override;
24
25private:
26 Result CreateNewsService(OutInterface<INewsService> out_interface);
27 Result CreateNewlyArrivedEventHolder(OutInterface<INewlyArrivedEventHolder> out_interface);
28 Result CreateNewsDataService(OutInterface<INewsDataService> out_interface);
29 Result CreateNewsDatabaseService(OutInterface<INewsDatabaseService> out_interface);
30 Result CreateOverwriteEventHolder(OutInterface<IOverwriteEventHolder> out_interface);
31
32 u32 permissions;
33};
34
35} // namespace Service::News
diff --git a/src/core/hle/service/bcat/service_creator.cpp b/src/core/hle/service/bcat/service_creator.cpp
new file mode 100644
index 000000000..ca339e5a6
--- /dev/null
+++ b/src/core/hle/service/bcat/service_creator.cpp
@@ -0,0 +1,62 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/bcat/bcat_service.h"
5#include "core/hle/service/bcat/delivery_cache_storage_service.h"
6#include "core/hle/service/bcat/service_creator.h"
7#include "core/hle/service/cmif_serialization.h"
8#include "core/hle/service/filesystem/filesystem.h"
9
10namespace Service::BCAT {
11
12std::unique_ptr<BcatBackend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
13 DirectoryGetter getter) {
14 return std::make_unique<NullBcatBackend>(std::move(getter));
15}
16
17IServiceCreator::IServiceCreator(Core::System& system_, const char* name_)
18 : ServiceFramework{system_, name_}, fsc{system.GetFileSystemController()} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, D<&IServiceCreator::CreateBcatService>, "CreateBcatService"},
22 {1, D<&IServiceCreator::CreateDeliveryCacheStorageService>, "CreateDeliveryCacheStorageService"},
23 {2, D<&IServiceCreator::CreateDeliveryCacheStorageServiceWithApplicationId>, "CreateDeliveryCacheStorageServiceWithApplicationId"},
24 {3, nullptr, "CreateDeliveryCacheProgressService"},
25 {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"},
26 };
27 // clang-format on
28
29 RegisterHandlers(functions);
30
31 backend =
32 CreateBackendFromSettings(system_, [this](u64 tid) { return fsc.GetBCATDirectory(tid); });
33}
34
35IServiceCreator::~IServiceCreator() = default;
36
37Result IServiceCreator::CreateBcatService(ClientProcessId process_id,
38 OutInterface<IBcatService> out_interface) {
39 LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
40 *out_interface = std::make_shared<IBcatService>(system, *backend);
41 R_SUCCEED();
42}
43
44Result IServiceCreator::CreateDeliveryCacheStorageService(
45 ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
46 LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
47
48 const auto title_id = system.GetApplicationProcessProgramID();
49 *out_interface =
50 std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
51 R_SUCCEED();
52}
53
54Result IServiceCreator::CreateDeliveryCacheStorageServiceWithApplicationId(
55 u64 application_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
56 LOG_DEBUG(Service_BCAT, "called, application_id={:016X}", application_id);
57 *out_interface = std::make_shared<IDeliveryCacheStorageService>(
58 system, fsc.GetBCATDirectory(application_id));
59 R_SUCCEED();
60}
61
62} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/service_creator.h b/src/core/hle/service/bcat/service_creator.h
new file mode 100644
index 000000000..50e663324
--- /dev/null
+++ b/src/core/hle/service/bcat/service_creator.h
@@ -0,0 +1,40 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::FileSystem {
14class FileSystemController;
15}
16
17namespace Service::BCAT {
18class BcatBackend;
19class IBcatService;
20class IDeliveryCacheStorageService;
21
22class IServiceCreator final : public ServiceFramework<IServiceCreator> {
23public:
24 explicit IServiceCreator(Core::System& system_, const char* name_);
25 ~IServiceCreator() override;
26
27private:
28 Result CreateBcatService(ClientProcessId process_id, OutInterface<IBcatService> out_interface);
29
30 Result CreateDeliveryCacheStorageService(
31 ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface);
32
33 Result CreateDeliveryCacheStorageServiceWithApplicationId(
34 u64 application_id, OutInterface<IDeliveryCacheStorageService> out_interface);
35
36 std::unique_ptr<BcatBackend> backend;
37 Service::FileSystem::FileSystemController& fsc;
38};
39
40} // namespace Service::BCAT
diff --git a/src/core/hle/service/cmif_serialization.h b/src/core/hle/service/cmif_serialization.h
index e985fe317..f24682c34 100644
--- a/src/core/hle/service/cmif_serialization.h
+++ b/src/core/hle/service/cmif_serialization.h
@@ -280,7 +280,7 @@ void ReadInArgument(bool is_domain, CallArguments& args, const u8* raw_data, HLE
280 280
281 u32 value{}; 281 u32 value{};
282 std::memcpy(&value, raw_data + ArgOffset, ArgSize); 282 std::memcpy(&value, raw_data + ArgOffset, ArgSize);
283 std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1); 283 std::get<ArgIndex>(args) = ctx.GetDomainHandler<typename ArgType::element_type>(value - 1);
284 284
285 return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); 285 return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
286 } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) { 286 } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) {
diff --git a/src/core/hle/service/cmif_types.h b/src/core/hle/service/cmif_types.h
index 325304d5c..dad358b87 100644
--- a/src/core/hle/service/cmif_types.h
+++ b/src/core/hle/service/cmif_types.h
@@ -65,6 +65,14 @@ struct ClientProcessId {
65}; 65};
66 66
67struct ProcessId { 67struct ProcessId {
68 explicit ProcessId() : pid() {}
69 explicit ProcessId(u64 p) : pid(p) {}
70 /* implicit */ ProcessId(const ClientProcessId& c) : pid(c.pid) {}
71
72 bool operator==(const ProcessId& rhs) const {
73 return pid == rhs.pid;
74 }
75
68 explicit operator bool() const { 76 explicit operator bool() const {
69 return pid != 0; 77 return pid != 0;
70 } 78 }
@@ -291,4 +299,4 @@ private:
291}; 299};
292// clang-format on 300// clang-format on
293 301
294} // namespace Service \ No newline at end of file 302} // namespace Service
diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp
index f44f3077e..8787f2dcd 100644
--- a/src/core/hle/service/glue/time/worker.cpp
+++ b/src/core/hle/service/glue/time/worker.cpp
@@ -7,6 +7,7 @@
7#include "core/hle/service/glue/time/file_timestamp_worker.h" 7#include "core/hle/service/glue/time/file_timestamp_worker.h"
8#include "core/hle/service/glue/time/standard_steady_clock_resource.h" 8#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
9#include "core/hle/service/glue/time/worker.h" 9#include "core/hle/service/glue/time/worker.h"
10#include "core/hle/service/os/multi_wait_utils.h"
10#include "core/hle/service/psc/time/common.h" 11#include "core/hle/service/psc/time/common.h"
11#include "core/hle/service/psc/time/service_manager.h" 12#include "core/hle/service/psc/time/service_manager.h"
12#include "core/hle/service/psc/time/static.h" 13#include "core/hle/service/psc/time/static.h"
@@ -143,82 +144,46 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
143 Common::SetCurrentThreadName("TimeWorker"); 144 Common::SetCurrentThreadName("TimeWorker");
144 Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); 145 Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
145 146
146 enum class EventType {
147 Exit = 0,
148 IpmModuleService_GetEvent = 1,
149 PowerStateChange = 2,
150 SignalAlarms = 3,
151 UpdateLocalSystemClock = 4,
152 UpdateNetworkSystemClock = 5,
153 UpdateEphemeralSystemClock = 6,
154 UpdateSteadyClock = 7,
155 UpdateFileTimestamp = 8,
156 AutoCorrect = 9,
157 Max = 10,
158 };
159
160 s32 num_objs{};
161 std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{};
162 std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{};
163
164 const auto AddWaiter{
165 [&](Kernel::KSynchronizationObject* synchronization_object, EventType type) {
166 // Open a new reference to the object.
167 synchronization_object->Open();
168
169 // Insert into the list.
170 wait_indices[num_objs] = type;
171 wait_objs[num_objs++] = synchronization_object;
172 }};
173
174 while (!stop_token.stop_requested()) { 147 while (!stop_token.stop_requested()) {
175 SCOPE_EXIT({ 148 enum class EventType : s32 {
176 for (s32 i = 0; i < num_objs; i++) { 149 Exit = 0,
177 wait_objs[i]->Close(); 150 PowerStateChange = 1,
178 } 151 SignalAlarms = 2,
179 }); 152 UpdateLocalSystemClock = 3,
153 UpdateNetworkSystemClock = 4,
154 UpdateEphemeralSystemClock = 5,
155 UpdateSteadyClock = 6,
156 UpdateFileTimestamp = 7,
157 AutoCorrect = 8,
158 };
159
160 s32 index{};
180 161
181 num_objs = {};
182 wait_objs = {};
183 if (m_pm_state_change_handler.m_priority != 0) { 162 if (m_pm_state_change_handler.m_priority != 0) {
184 AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); 163 // TODO: gIPmModuleService::GetEvent() 1
185 // TODO 164 index = WaitAny(m_system.Kernel(),
186 // AddWaiter(gIPmModuleService::GetEvent(), 1); 165 &m_event->GetReadableEvent(), // 0
187 AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange); 166 &m_alarm_worker.GetEvent() // 1
167 );
188 } else { 168 } else {
189 AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); 169 // TODO: gIPmModuleService::GetEvent() 1
190 // TODO 170 index = WaitAny(m_system.Kernel(),
191 // AddWaiter(gIPmModuleService::GetEvent(), 1); 171 &m_event->GetReadableEvent(), // 0
192 AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange); 172 &m_alarm_worker.GetEvent(), // 1
193 AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms); 173 &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
194 AddWaiter(m_local_clock_event, EventType::UpdateLocalSystemClock); 174 m_local_clock_event, // 3
195 AddWaiter(m_network_clock_event, EventType::UpdateNetworkSystemClock); 175 m_network_clock_event, // 4
196 AddWaiter(m_ephemeral_clock_event, EventType::UpdateEphemeralSystemClock); 176 m_ephemeral_clock_event, // 5
197 AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock); 177 &m_timer_steady_clock->GetReadableEvent(), // 6
198 AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp); 178 &m_timer_file_system->GetReadableEvent(), // 7
199 AddWaiter(m_standard_user_auto_correct_clock_event, EventType::AutoCorrect); 179 m_standard_user_auto_correct_clock_event // 8
180 );
200 } 181 }
201 182
202 s32 out_index{-1}; 183 switch (static_cast<EventType>(index)) {
203 Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(),
204 num_objs, -1);
205 ASSERT(out_index >= 0 && out_index < num_objs);
206
207 if (stop_token.stop_requested()) {
208 return;
209 }
210
211 switch (wait_indices[out_index]) {
212 case EventType::Exit: 184 case EventType::Exit:
213 return; 185 return;
214 186
215 case EventType::IpmModuleService_GetEvent:
216 // TODO
217 // IPmModuleService::GetEvent()
218 // clear the event
219 // Handle power state change event
220 break;
221
222 case EventType::PowerStateChange: 187 case EventType::PowerStateChange:
223 m_alarm_worker.GetEvent().Clear(); 188 m_alarm_worker.GetEvent().Clear();
224 if (m_pm_state_change_handler.m_priority <= 1) { 189 if (m_pm_state_change_handler.m_priority <= 1) {
@@ -235,19 +200,19 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
235 m_local_clock_event->Clear(); 200 m_local_clock_event->Clear();
236 201
237 Service::PSC::Time::SystemClockContext context{}; 202 Service::PSC::Time::SystemClockContext context{};
238 auto res = m_local_clock->GetSystemClockContext(&context); 203 R_ASSERT(m_local_clock->GetSystemClockContext(&context));
239 ASSERT(res == ResultSuccess);
240 204
241 m_set_sys->SetUserSystemClockContext(context); 205 m_set_sys->SetUserSystemClockContext(context);
242
243 m_file_timestamp_worker.SetFilesystemPosixTime(); 206 m_file_timestamp_worker.SetFilesystemPosixTime();
244 } break; 207 break;
208 }
245 209
246 case EventType::UpdateNetworkSystemClock: { 210 case EventType::UpdateNetworkSystemClock: {
247 m_network_clock_event->Clear(); 211 m_network_clock_event->Clear();
212
248 Service::PSC::Time::SystemClockContext context{}; 213 Service::PSC::Time::SystemClockContext context{};
249 auto res = m_network_clock->GetSystemClockContext(&context); 214 R_ASSERT(m_network_clock->GetSystemClockContext(&context));
250 ASSERT(res == ResultSuccess); 215
251 m_set_sys->SetNetworkSystemClockContext(context); 216 m_set_sys->SetNetworkSystemClockContext(context);
252 217
253 s64 time{}; 218 s64 time{};
@@ -267,7 +232,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
267 } 232 }
268 233
269 m_file_timestamp_worker.SetFilesystemPosixTime(); 234 m_file_timestamp_worker.SetFilesystemPosixTime();
270 } break; 235 break;
236 }
271 237
272 case EventType::UpdateEphemeralSystemClock: { 238 case EventType::UpdateEphemeralSystemClock: {
273 m_ephemeral_clock_event->Clear(); 239 m_ephemeral_clock_event->Clear();
@@ -295,7 +261,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
295 if (!g_ig_report_ephemeral_clock_context_set) { 261 if (!g_ig_report_ephemeral_clock_context_set) {
296 g_ig_report_ephemeral_clock_context_set = true; 262 g_ig_report_ephemeral_clock_context_set = true;
297 } 263 }
298 } break; 264 break;
265 }
299 266
300 case EventType::UpdateSteadyClock: 267 case EventType::UpdateSteadyClock:
301 m_timer_steady_clock->Clear(); 268 m_timer_steady_clock->Clear();
@@ -314,21 +281,20 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
314 m_standard_user_auto_correct_clock_event->Clear(); 281 m_standard_user_auto_correct_clock_event->Clear();
315 282
316 bool automatic_correction{}; 283 bool automatic_correction{};
317 auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( 284 R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
318 &automatic_correction); 285 &automatic_correction));
319 ASSERT(res == ResultSuccess);
320 286
321 Service::PSC::Time::SteadyClockTimePoint time_point{}; 287 Service::PSC::Time::SteadyClockTimePoint time_point{};
322 res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point); 288 R_ASSERT(
323 ASSERT(res == ResultSuccess); 289 m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
324 290
325 m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); 291 m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
326 m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); 292 m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
327 } break; 293 break;
294 }
328 295
329 default: 296 default:
330 UNREACHABLE(); 297 UNREACHABLE();
331 break;
332 } 298 }
333 } 299 }
334} 300}
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index 22471e9e2..7126a1dcd 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -508,13 +508,8 @@ void IHidSystemServer::RegisterAppletResourceUserId(HLERequestContext& ctx) {
508 Result result = GetResourceManager()->RegisterAppletResourceUserId( 508 Result result = GetResourceManager()->RegisterAppletResourceUserId(
509 parameters.applet_resource_user_id, parameters.enable_input); 509 parameters.applet_resource_user_id, parameters.enable_input);
510 510
511 if (result.IsSuccess()) {
512 // result = GetResourceManager()->GetNpad()->RegisterAppletResourceUserId(
513 // parameters.applet_resource_user_id);
514 }
515
516 IPC::ResponseBuilder rb{ctx, 2}; 511 IPC::ResponseBuilder rb{ctx, 2};
517 rb.Push(ResultSuccess); 512 rb.Push(result);
518} 513}
519 514
520void IHidSystemServer::UnregisterAppletResourceUserId(HLERequestContext& ctx) { 515void IHidSystemServer::UnregisterAppletResourceUserId(HLERequestContext& ctx) {
@@ -524,8 +519,6 @@ void IHidSystemServer::UnregisterAppletResourceUserId(HLERequestContext& ctx) {
524 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 519 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
525 520
526 GetResourceManager()->UnregisterAppletResourceUserId(applet_resource_user_id); 521 GetResourceManager()->UnregisterAppletResourceUserId(applet_resource_user_id);
527 // GetResourceManager()->GetNpad()->UnregisterAppletResourceUserId(applet_resource_user_id);
528 // GetResourceManager()->GetPalma()->UnregisterAppletResourceUserId(applet_resource_user_id);
529 522
530 IPC::ResponseBuilder rb{ctx, 2}; 523 IPC::ResponseBuilder rb{ctx, 2};
531 rb.Push(ResultSuccess); 524 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp
index 50e1ed756..e0367e774 100644
--- a/src/core/hle/service/hle_ipc.cpp
+++ b/src/core/hle/service/hle_ipc.cpp
@@ -299,8 +299,12 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer() {
299 if (GetManager()->IsDomain()) { 299 if (GetManager()->IsDomain()) {
300 current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); 300 current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
301 for (auto& object : outgoing_domain_objects) { 301 for (auto& object : outgoing_domain_objects) {
302 GetManager()->AppendDomainHandler(std::move(object)); 302 if (object) {
303 cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount()); 303 GetManager()->AppendDomainHandler(std::move(object));
304 cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount());
305 } else {
306 cmd_buf[current_offset++] = 0;
307 }
304 } 308 }
305 } 309 }
306 310
diff --git a/src/core/hle/service/event.cpp b/src/core/hle/service/os/event.cpp
index 375660d72..ec52c17fd 100644
--- a/src/core/hle/service/event.cpp
+++ b/src/core/hle/service/os/event.cpp
@@ -2,8 +2,8 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/kernel/k_event.h" 4#include "core/hle/kernel/k_event.h"
5#include "core/hle/service/event.h"
6#include "core/hle/service/kernel_helpers.h" 5#include "core/hle/service/kernel_helpers.h"
6#include "core/hle/service/os/event.h"
7 7
8namespace Service { 8namespace Service {
9 9
diff --git a/src/core/hle/service/event.h b/src/core/hle/service/os/event.h
index cdbc4635a..cdbc4635a 100644
--- a/src/core/hle/service/event.h
+++ b/src/core/hle/service/os/event.h
diff --git a/src/core/hle/service/os/multi_wait.cpp b/src/core/hle/service/os/multi_wait.cpp
new file mode 100644
index 000000000..7b80d28be
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait.cpp
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_hardware_timer.h"
5#include "core/hle/kernel/k_synchronization_object.h"
6#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/svc_common.h"
8#include "core/hle/service/os/multi_wait.h"
9
10namespace Service {
11
12MultiWait::MultiWait() = default;
13MultiWait::~MultiWait() = default;
14
15MultiWaitHolder* MultiWait::WaitAny(Kernel::KernelCore& kernel) {
16 return this->TimedWaitImpl(kernel, -1);
17}
18
19MultiWaitHolder* MultiWait::TryWaitAny(Kernel::KernelCore& kernel) {
20 return this->TimedWaitImpl(kernel, 0);
21}
22
23MultiWaitHolder* MultiWait::TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns) {
24 return this->TimedWaitImpl(kernel, kernel.HardwareTimer().GetTick() + timeout_ns);
25}
26
27MultiWaitHolder* MultiWait::TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick) {
28 std::array<MultiWaitHolder*, Kernel::Svc::ArgumentHandleCountMax> holders{};
29 std::array<Kernel::KSynchronizationObject*, Kernel::Svc::ArgumentHandleCountMax> objects{};
30
31 s32 out_index = -1;
32 s32 num_objects = 0;
33
34 for (auto it = m_wait_list.begin(); it != m_wait_list.end(); it++) {
35 ASSERT(num_objects < Kernel::Svc::ArgumentHandleCountMax);
36 holders[num_objects] = std::addressof(*it);
37 objects[num_objects] = it->GetNativeHandle();
38 num_objects++;
39 }
40
41 Kernel::KSynchronizationObject::Wait(kernel, std::addressof(out_index), objects.data(),
42 num_objects, timeout_tick);
43
44 if (out_index == -1) {
45 return nullptr;
46 } else {
47 return holders[out_index];
48 }
49}
50
51void MultiWait::MoveAll(MultiWait* other) {
52 while (!other->m_wait_list.empty()) {
53 MultiWaitHolder& holder = other->m_wait_list.front();
54 holder.UnlinkFromMultiWait();
55 holder.LinkToMultiWait(this);
56 }
57}
58
59} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait.h b/src/core/hle/service/os/multi_wait.h
new file mode 100644
index 000000000..340c611b5
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/os/multi_wait_holder.h"
7
8namespace Kernel {
9class KernelCore;
10}
11
12namespace Service {
13
14class MultiWait final {
15public:
16 explicit MultiWait();
17 ~MultiWait();
18
19public:
20 MultiWaitHolder* WaitAny(Kernel::KernelCore& kernel);
21 MultiWaitHolder* TryWaitAny(Kernel::KernelCore& kernel);
22 MultiWaitHolder* TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns);
23 // TODO: SdkReplyAndReceive?
24
25 void MoveAll(MultiWait* other);
26
27private:
28 MultiWaitHolder* TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick);
29
30private:
31 friend class MultiWaitHolder;
32 using ListType = Common::IntrusiveListMemberTraits<&MultiWaitHolder::m_list_node>::ListType;
33 ListType m_wait_list{};
34};
35
36} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait_holder.cpp b/src/core/hle/service/os/multi_wait_holder.cpp
new file mode 100644
index 000000000..01efa045b
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait_holder.cpp
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/os/multi_wait.h"
5#include "core/hle/service/os/multi_wait_holder.h"
6
7namespace Service {
8
9void MultiWaitHolder::LinkToMultiWait(MultiWait* multi_wait) {
10 if (m_multi_wait != nullptr) {
11 UNREACHABLE();
12 }
13
14 m_multi_wait = multi_wait;
15 m_multi_wait->m_wait_list.push_back(*this);
16}
17
18void MultiWaitHolder::UnlinkFromMultiWait() {
19 if (m_multi_wait) {
20 m_multi_wait->m_wait_list.erase(m_multi_wait->m_wait_list.iterator_to(*this));
21 m_multi_wait = nullptr;
22 }
23}
24
25} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait_holder.h b/src/core/hle/service/os/multi_wait_holder.h
new file mode 100644
index 000000000..646395a3f
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait_holder.h
@@ -0,0 +1,44 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/intrusive_list.h"
7
8namespace Kernel {
9class KSynchronizationObject;
10} // namespace Kernel
11
12namespace Service {
13
14class MultiWait;
15
16class MultiWaitHolder {
17public:
18 explicit MultiWaitHolder(Kernel::KSynchronizationObject* native_handle)
19 : m_native_handle(native_handle) {}
20
21 void LinkToMultiWait(MultiWait* multi_wait);
22 void UnlinkFromMultiWait();
23
24 void SetUserData(uintptr_t user_data) {
25 m_user_data = user_data;
26 }
27
28 uintptr_t GetUserData() const {
29 return m_user_data;
30 }
31
32 Kernel::KSynchronizationObject* GetNativeHandle() const {
33 return m_native_handle;
34 }
35
36private:
37 friend class MultiWait;
38 Common::IntrusiveListNode m_list_node{};
39 MultiWait* m_multi_wait{};
40 Kernel::KSynchronizationObject* m_native_handle{};
41 uintptr_t m_user_data{};
42};
43
44} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait_utils.h b/src/core/hle/service/os/multi_wait_utils.h
new file mode 100644
index 000000000..96d3a10f3
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait_utils.h
@@ -0,0 +1,109 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/os/multi_wait.h"
7
8namespace Service {
9
10namespace impl {
11
12class AutoMultiWaitHolder {
13private:
14 MultiWaitHolder m_holder;
15
16public:
17 template <typename T>
18 explicit AutoMultiWaitHolder(MultiWait* multi_wait, T&& arg) : m_holder(arg) {
19 m_holder.LinkToMultiWait(multi_wait);
20 }
21
22 ~AutoMultiWaitHolder() {
23 m_holder.UnlinkFromMultiWait();
24 }
25
26 std::pair<MultiWaitHolder*, int> ConvertResult(const std::pair<MultiWaitHolder*, int> result,
27 int index) {
28 if (result.first == std::addressof(m_holder)) {
29 return std::make_pair(static_cast<MultiWaitHolder*>(nullptr), index);
30 } else {
31 return result;
32 }
33 }
34};
35
36using WaitAnyFunction = decltype(&MultiWait::WaitAny);
37
38inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
39 MultiWait* multi_wait, WaitAnyFunction func,
40 int) {
41 return std::pair<MultiWaitHolder*, int>((multi_wait->*func)(kernel), -1);
42}
43
44template <typename T, typename... Args>
45inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
46 MultiWait* multi_wait, WaitAnyFunction func,
47 int index, T&& x, Args&&... args) {
48 AutoMultiWaitHolder holder(multi_wait, std::forward<T>(x));
49 return holder.ConvertResult(
50 WaitAnyImpl(kernel, multi_wait, func, index + 1, std::forward<Args>(args)...), index);
51}
52
53template <typename... Args>
54inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
55 MultiWait* multi_wait, WaitAnyFunction func,
56 Args&&... args) {
57 return WaitAnyImpl(kernel, multi_wait, func, 0, std::forward<Args>(args)...);
58}
59
60template <typename... Args>
61inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
62 WaitAnyFunction func, Args&&... args) {
63 MultiWait temp_multi_wait;
64 return WaitAnyImpl(kernel, std::addressof(temp_multi_wait), func, 0,
65 std::forward<Args>(args)...);
66}
67
68class NotBoolButInt {
69public:
70 constexpr NotBoolButInt(int v) : m_value(v) {}
71 constexpr operator int() const {
72 return m_value;
73 }
74 explicit operator bool() const = delete;
75
76private:
77 int m_value;
78};
79
80} // namespace impl
81
82template <typename... Args>
83 requires(sizeof...(Args) > 0)
84inline std::pair<MultiWaitHolder*, int> WaitAny(Kernel::KernelCore& kernel, MultiWait* multi_wait,
85 Args&&... args) {
86 return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, multi_wait, std::forward<Args>(args)...);
87}
88
89template <typename... Args>
90 requires(sizeof...(Args) > 0)
91inline int WaitAny(Kernel::KernelCore& kernel, Args&&... args) {
92 return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, std::forward<Args>(args)...).second;
93}
94
95template <typename... Args>
96 requires(sizeof...(Args) > 0)
97inline std::pair<MultiWaitHolder*, int> TryWaitAny(Kernel::KernelCore& kernel,
98 MultiWait* multi_wait, Args&&... args) {
99 return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, multi_wait,
100 std::forward<Args>(args)...);
101}
102
103template <typename... Args>
104 requires(sizeof...(Args) > 0)
105inline impl::NotBoolButInt TryWaitAny(Kernel::KernelCore& kernel, Args&&... args) {
106 return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, std::forward<Args>(args)...).second;
107}
108
109} // namespace Service
diff --git a/src/core/hle/service/mutex.cpp b/src/core/hle/service/os/mutex.cpp
index b0ff71d1b..6009f4866 100644
--- a/src/core/hle/service/mutex.cpp
+++ b/src/core/hle/service/os/mutex.cpp
@@ -4,7 +4,7 @@
4#include "core/core.h" 4#include "core/core.h"
5#include "core/hle/kernel/k_event.h" 5#include "core/hle/kernel/k_event.h"
6#include "core/hle/kernel/k_synchronization_object.h" 6#include "core/hle/kernel/k_synchronization_object.h"
7#include "core/hle/service/mutex.h" 7#include "core/hle/service/os/mutex.h"
8 8
9namespace Service { 9namespace Service {
10 10
diff --git a/src/core/hle/service/mutex.h b/src/core/hle/service/os/mutex.h
index 95ac9b117..95ac9b117 100644
--- a/src/core/hle/service/mutex.h
+++ b/src/core/hle/service/os/mutex.h
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 8ef49387d..8c7f94c8c 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -20,50 +20,91 @@
20 20
21namespace Service { 21namespace Service {
22 22
23constexpr size_t MaximumWaitObjects = 0x40; 23enum class UserDataTag {
24
25enum HandleType {
26 Port, 24 Port,
27 Session, 25 Session,
28 DeferEvent, 26 DeferEvent,
29 Event,
30}; 27};
31 28
32ServerManager::ServerManager(Core::System& system) : m_system{system}, m_serve_mutex{system} { 29class Port : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Port> {
30public:
31 explicit Port(Kernel::KServerPort* server_port, SessionRequestHandlerFactory&& handler_factory)
32 : MultiWaitHolder(server_port), m_handler_factory(std::move(handler_factory)) {
33 this->SetUserData(static_cast<uintptr_t>(UserDataTag::Port));
34 }
35
36 ~Port() {
37 this->GetNativeHandle()->Close();
38 }
39
40 SessionRequestHandlerPtr CreateHandler() {
41 return m_handler_factory();
42 }
43
44private:
45 const SessionRequestHandlerFactory m_handler_factory;
46};
47
48class Session : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Session> {
49public:
50 explicit Session(Kernel::KServerSession* server_session,
51 std::shared_ptr<SessionRequestManager>&& manager)
52 : MultiWaitHolder(server_session), m_manager(std::move(manager)) {
53 this->SetUserData(static_cast<uintptr_t>(UserDataTag::Session));
54 }
55
56 ~Session() {
57 this->GetNativeHandle()->Close();
58 }
59
60 std::shared_ptr<SessionRequestManager>& GetManager() {
61 return m_manager;
62 }
63
64 std::shared_ptr<HLERequestContext>& GetContext() {
65 return m_context;
66 }
67
68private:
69 std::shared_ptr<SessionRequestManager> m_manager;
70 std::shared_ptr<HLERequestContext> m_context;
71};
72
73ServerManager::ServerManager(Core::System& system) : m_system{system}, m_selection_mutex{system} {
33 // Initialize event. 74 // Initialize event.
34 m_event = Kernel::KEvent::Create(system.Kernel()); 75 m_wakeup_event = Kernel::KEvent::Create(system.Kernel());
35 m_event->Initialize(nullptr); 76 m_wakeup_event->Initialize(nullptr);
36 77
37 // Register event. 78 // Register event.
38 Kernel::KEvent::Register(system.Kernel(), m_event); 79 Kernel::KEvent::Register(system.Kernel(), m_wakeup_event);
80
81 // Link to holder.
82 m_wakeup_holder.emplace(std::addressof(m_wakeup_event->GetReadableEvent()));
83 m_wakeup_holder->LinkToMultiWait(std::addressof(m_deferred_list));
39} 84}
40 85
41ServerManager::~ServerManager() { 86ServerManager::~ServerManager() {
42 // Signal stop. 87 // Signal stop.
43 m_stop_source.request_stop(); 88 m_stop_source.request_stop();
44 m_event->Signal(); 89 m_wakeup_event->Signal();
45 90
46 // Wait for processing to stop. 91 // Wait for processing to stop.
47 m_stopped.Wait(); 92 m_stopped.Wait();
48 m_threads.clear(); 93 m_threads.clear();
49 94
50 // Clean up server ports. 95 // Clean up ports.
51 for (const auto& [port, handler] : m_ports) { 96 for (auto it = m_servers.begin(); it != m_servers.end(); it = m_servers.erase(it)) {
52 port->Close(); 97 delete std::addressof(*it);
53 } 98 }
54 99
55 // Clean up sessions. 100 // Clean up sessions.
56 for (const auto& [session, manager] : m_sessions) { 101 for (auto it = m_sessions.begin(); it != m_sessions.end(); it = m_sessions.erase(it)) {
57 session->Close(); 102 delete std::addressof(*it);
58 }
59
60 for (const auto& request : m_deferrals) {
61 request.session->Close();
62 } 103 }
63 104
64 // Close event. 105 // Close wakeup event.
65 m_event->GetReadableEvent().Close(); 106 m_wakeup_event->GetReadableEvent().Close();
66 m_event->Close(); 107 m_wakeup_event->Close();
67 108
68 if (m_deferral_event) { 109 if (m_deferral_event) {
69 m_deferral_event->GetReadableEvent().Close(); 110 m_deferral_event->GetReadableEvent().Close();
@@ -75,19 +116,19 @@ void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
75 server_manager->m_system.RunServer(std::move(server_manager)); 116 server_manager->m_system.RunServer(std::move(server_manager));
76} 117}
77 118
78Result ServerManager::RegisterSession(Kernel::KServerSession* session, 119Result ServerManager::RegisterSession(Kernel::KServerSession* server_session,
79 std::shared_ptr<SessionRequestManager> manager) { 120 std::shared_ptr<SessionRequestManager> manager) {
80 ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
81
82 // We are taking ownership of the server session, so don't open it. 121 // We are taking ownership of the server session, so don't open it.
122 auto* session = new Session(server_session, std::move(manager));
123
83 // Begin tracking the server session. 124 // Begin tracking the server session.
84 { 125 {
85 std::scoped_lock ll{m_list_mutex}; 126 std::scoped_lock ll{m_deferred_list_mutex};
86 m_sessions.emplace(session, std::move(manager)); 127 m_sessions.push_back(*session);
87 } 128 }
88 129
89 // Signal the wakeup event. 130 // Register to wait on the session.
90 m_event->Signal(); 131 this->LinkToDeferredList(session);
91 132
92 R_SUCCEED(); 133 R_SUCCEED();
93} 134}
@@ -95,21 +136,22 @@ Result ServerManager::RegisterSession(Kernel::KServerSession* session,
95Result ServerManager::RegisterNamedService(const std::string& service_name, 136Result ServerManager::RegisterNamedService(const std::string& service_name,
96 SessionRequestHandlerFactory&& handler_factory, 137 SessionRequestHandlerFactory&& handler_factory,
97 u32 max_sessions) { 138 u32 max_sessions) {
98 ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
99
100 // Add the new server to sm: and get the moved server port. 139 // Add the new server to sm: and get the moved server port.
101 Kernel::KServerPort* server_port{}; 140 Kernel::KServerPort* server_port{};
102 R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name, 141 R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name,
103 max_sessions, handler_factory)); 142 max_sessions, handler_factory));
104 143
144 // We are taking ownership of the server port, so don't open it.
145 auto* server = new Port(server_port, std::move(handler_factory));
146
105 // Begin tracking the server port. 147 // Begin tracking the server port.
106 { 148 {
107 std::scoped_lock ll{m_list_mutex}; 149 std::scoped_lock ll{m_deferred_list_mutex};
108 m_ports.emplace(server_port, std::move(handler_factory)); 150 m_servers.push_back(*server);
109 } 151 }
110 152
111 // Signal the wakeup event. 153 // Register to wait on the server port.
112 m_event->Signal(); 154 this->LinkToDeferredList(server);
113 155
114 R_SUCCEED(); 156 R_SUCCEED();
115} 157}
@@ -127,8 +169,6 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
127Result ServerManager::ManageNamedPort(const std::string& service_name, 169Result ServerManager::ManageNamedPort(const std::string& service_name,
128 SessionRequestHandlerFactory&& handler_factory, 170 SessionRequestHandlerFactory&& handler_factory,
129 u32 max_sessions) { 171 u32 max_sessions) {
130 ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
131
132 // Create a new port. 172 // Create a new port.
133 auto* port = Kernel::KPort::Create(m_system.Kernel()); 173 auto* port = Kernel::KPort::Create(m_system.Kernel());
134 port->Initialize(max_sessions, false, 0); 174 port->Initialize(max_sessions, false, 0);
@@ -149,12 +189,18 @@ Result ServerManager::ManageNamedPort(const std::string& service_name,
149 // Open a new reference to the server port. 189 // Open a new reference to the server port.
150 port->GetServerPort().Open(); 190 port->GetServerPort().Open();
151 191
152 // Begin tracking the server port. 192 // Transfer ownership into a new port object.
193 auto* server = new Port(std::addressof(port->GetServerPort()), std::move(handler_factory));
194
195 // Begin tracking the port.
153 { 196 {
154 std::scoped_lock ll{m_list_mutex}; 197 std::scoped_lock ll{m_deferred_list_mutex};
155 m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory)); 198 m_servers.push_back(*server);
156 } 199 }
157 200
201 // Register to wait on the port.
202 this->LinkToDeferredList(server);
203
158 // We succeeded. 204 // We succeeded.
159 R_SUCCEED(); 205 R_SUCCEED();
160} 206}
@@ -173,6 +219,11 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
173 // Set the output. 219 // Set the output.
174 *out_event = m_deferral_event; 220 *out_event = m_deferral_event;
175 221
222 // Register to wait on the event.
223 m_deferral_holder.emplace(std::addressof(m_deferral_event->GetReadableEvent()));
224 m_deferral_holder->SetUserData(static_cast<uintptr_t>(UserDataTag::DeferEvent));
225 this->LinkToDeferredList(std::addressof(*m_deferral_holder));
226
176 // We succeeded. 227 // We succeeded.
177 R_SUCCEED(); 228 R_SUCCEED();
178} 229}
@@ -191,270 +242,185 @@ Result ServerManager::LoopProcess() {
191 R_RETURN(this->LoopProcessImpl()); 242 R_RETURN(this->LoopProcessImpl());
192} 243}
193 244
194Result ServerManager::LoopProcessImpl() { 245void ServerManager::LinkToDeferredList(MultiWaitHolder* holder) {
195 while (!m_stop_source.stop_requested()) { 246 // Link.
196 R_TRY(this->WaitAndProcessImpl()); 247 {
248 std::scoped_lock lk{m_deferred_list_mutex};
249 holder->LinkToMultiWait(std::addressof(m_deferred_list));
197 } 250 }
198 251
199 R_SUCCEED(); 252 // Signal the wakeup event.
253 m_wakeup_event->Signal();
200} 254}
201 255
202Result ServerManager::WaitAndProcessImpl() { 256void ServerManager::LinkDeferred() {
203 Kernel::KScopedAutoObject<Kernel::KSynchronizationObject> wait_obj; 257 std::scoped_lock lk{m_deferred_list_mutex};
204 HandleType wait_type{}; 258 m_multi_wait.MoveAll(std::addressof(m_deferred_list));
259}
205 260
261MultiWaitHolder* ServerManager::WaitSignaled() {
206 // Ensure we are the only thread waiting for this server. 262 // Ensure we are the only thread waiting for this server.
207 std::unique_lock sl{m_serve_mutex}; 263 std::scoped_lock lk{m_selection_mutex};
208 264
209 // If we're done, return before we start waiting. 265 while (true) {
210 R_SUCCEED_IF(m_stop_source.stop_requested()); 266 this->LinkDeferred();
211 267
212 // Wait for a tracked object to become signaled. 268 // If we're done, return before we start waiting.
213 { 269 if (m_stop_source.stop_requested()) {
214 s32 num_objs{}; 270 return nullptr;
215 std::array<HandleType, MaximumWaitObjects> wait_types{};
216 std::array<Kernel::KSynchronizationObject*, MaximumWaitObjects> wait_objs{};
217
218 const auto AddWaiter{
219 [&](Kernel::KSynchronizationObject* synchronization_object, HandleType type) {
220 // Open a new reference to the object.
221 synchronization_object->Open();
222
223 // Insert into the list.
224 wait_types[num_objs] = type;
225 wait_objs[num_objs++] = synchronization_object;
226 }};
227
228 {
229 std::scoped_lock ll{m_list_mutex};
230
231 // Add all of our ports.
232 for (const auto& [port, handler] : m_ports) {
233 AddWaiter(port, HandleType::Port);
234 }
235
236 // Add all of our sessions.
237 for (const auto& [session, manager] : m_sessions) {
238 AddWaiter(session, HandleType::Session);
239 }
240 } 271 }
241 272
242 // Add the deferral wakeup event. 273 auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
243 if (m_deferral_event != nullptr) { 274 if (selected == std::addressof(*m_wakeup_holder)) {
244 AddWaiter(std::addressof(m_deferral_event->GetReadableEvent()), HandleType::DeferEvent); 275 // Clear and restart if we were woken up.
276 m_wakeup_event->Clear();
277 } else {
278 // Unlink and handle the event.
279 selected->UnlinkFromMultiWait();
280 return selected;
245 } 281 }
282 }
283}
246 284
247 // Add the wakeup event. 285Result ServerManager::Process(MultiWaitHolder* holder) {
248 AddWaiter(std::addressof(m_event->GetReadableEvent()), HandleType::Event); 286 switch (static_cast<UserDataTag>(holder->GetUserData())) {
249 287 case UserDataTag::Session:
250 // Clean up extra references on exit. 288 R_RETURN(this->OnSessionEvent(static_cast<Session*>(holder)));
251 SCOPE_EXIT({ 289 case UserDataTag::Port:
252 for (s32 i = 0; i < num_objs; i++) { 290 R_RETURN(this->OnPortEvent(static_cast<Port*>(holder)));
253 wait_objs[i]->Close(); 291 case UserDataTag::DeferEvent:
254 } 292 R_RETURN(this->OnDeferralEvent());
255 }); 293 default:
256 294 UNREACHABLE();
257 // Wait for a signal. 295 }
258 s32 out_index{-1}; 296}
259 R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index,
260 wait_objs.data(), num_objs, -1)) {
261 R_CATCH(Kernel::ResultSessionClosed) {
262 // On session closed, index is updated and we don't want to return an error.
263 }
264 }
265 R_END_TRY_CATCH;
266 ASSERT(out_index >= 0 && out_index < num_objs);
267 297
268 // Set the output index. 298bool ServerManager::WaitAndProcessImpl() {
269 wait_obj = wait_objs[out_index]; 299 if (auto* signaled_holder = this->WaitSignaled(); signaled_holder != nullptr) {
270 wait_type = wait_types[out_index]; 300 R_ASSERT(this->Process(signaled_holder));
301 return true;
302 } else {
303 return false;
271 } 304 }
305}
272 306
273 // Process what we just received, temporarily removing the object so it is 307Result ServerManager::LoopProcessImpl() {
274 // not processed concurrently by another thread. 308 while (!m_stop_source.stop_requested()) {
275 { 309 this->WaitAndProcessImpl();
276 switch (wait_type) {
277 case HandleType::Port: {
278 // Port signaled.
279 auto* port = wait_obj->DynamicCast<Kernel::KServerPort*>();
280 SessionRequestHandlerFactory handler_factory;
281
282 // Remove from tracking.
283 {
284 std::scoped_lock ll{m_list_mutex};
285 ASSERT(m_ports.contains(port));
286 m_ports.at(port).swap(handler_factory);
287 m_ports.erase(port);
288 }
289
290 // Allow other threads to serve.
291 sl.unlock();
292
293 // Finish.
294 R_RETURN(this->OnPortEvent(port, std::move(handler_factory)));
295 }
296 case HandleType::Session: {
297 // Session signaled.
298 auto* session = wait_obj->DynamicCast<Kernel::KServerSession*>();
299 std::shared_ptr<SessionRequestManager> manager;
300
301 // Remove from tracking.
302 {
303 std::scoped_lock ll{m_list_mutex};
304 ASSERT(m_sessions.contains(session));
305 m_sessions.at(session).swap(manager);
306 m_sessions.erase(session);
307 }
308
309 // Allow other threads to serve.
310 sl.unlock();
311
312 // Finish.
313 R_RETURN(this->OnSessionEvent(session, std::move(manager)));
314 }
315 case HandleType::DeferEvent: {
316 // Clear event.
317 ASSERT(R_SUCCEEDED(m_deferral_event->Clear()));
318
319 // Drain the list of deferrals while we process.
320 std::list<RequestState> deferrals;
321 {
322 std::scoped_lock ll{m_list_mutex};
323 m_deferrals.swap(deferrals);
324 }
325
326 // Allow other threads to serve.
327 sl.unlock();
328
329 // Finish.
330 R_RETURN(this->OnDeferralEvent(std::move(deferrals)));
331 }
332 case HandleType::Event: {
333 // Clear event and finish.
334 R_RETURN(m_event->Clear());
335 }
336 default: {
337 UNREACHABLE();
338 }
339 }
340 } 310 }
311
312 R_SUCCEED();
341} 313}
342 314
343Result ServerManager::OnPortEvent(Kernel::KServerPort* port, 315Result ServerManager::OnPortEvent(Port* server) {
344 SessionRequestHandlerFactory&& handler_factory) {
345 // Accept a new server session. 316 // Accept a new server session.
346 Kernel::KServerSession* session = port->AcceptSession(); 317 auto* server_port = static_cast<Kernel::KServerPort*>(server->GetNativeHandle());
347 ASSERT(session != nullptr); 318 Kernel::KServerSession* server_session = server_port->AcceptSession();
319 ASSERT(server_session != nullptr);
348 320
349 // Create the session manager and install the handler. 321 // Create the session manager and install the handler.
350 auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this); 322 auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this);
351 manager->SetSessionHandler(handler_factory()); 323 manager->SetSessionHandler(server->CreateHandler());
352 324
353 // Track the server session. 325 // Create and register the new session.
354 { 326 this->RegisterSession(server_session, std::move(manager));
355 std::scoped_lock ll{m_list_mutex};
356 m_ports.emplace(port, std::move(handler_factory));
357 m_sessions.emplace(session, std::move(manager));
358 }
359 327
360 // Signal the wakeup event. 328 // Resume tracking the port.
361 m_event->Signal(); 329 this->LinkToDeferredList(server);
362 330
363 // We succeeded. 331 // We succeeded.
364 R_SUCCEED(); 332 R_SUCCEED();
365} 333}
366 334
367Result ServerManager::OnSessionEvent(Kernel::KServerSession* session, 335Result ServerManager::OnSessionEvent(Session* session) {
368 std::shared_ptr<SessionRequestManager>&& manager) { 336 Result res = ResultSuccess;
369 Result rc{ResultSuccess};
370 337
371 // Try to receive a message. 338 // Try to receive a message.
372 std::shared_ptr<HLERequestContext> context; 339 auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle());
373 rc = session->ReceiveRequestHLE(&context, manager); 340 res = server_session->ReceiveRequestHLE(&session->GetContext(), session->GetManager());
374 341
375 // If the session has been closed, we're done. 342 // If the session has been closed, we're done.
376 if (rc == Kernel::ResultSessionClosed) { 343 if (res == Kernel::ResultSessionClosed) {
377 // Close the session. 344 this->DestroySession(session);
378 session->Close();
379
380 // Finish.
381 R_SUCCEED(); 345 R_SUCCEED();
382 } 346 }
383 ASSERT(R_SUCCEEDED(rc));
384 347
385 RequestState request{ 348 R_ASSERT(res);
386 .session = session,
387 .context = std::move(context),
388 .manager = std::move(manager),
389 };
390 349
391 // Complete the sync request with deferral handling. 350 // Complete the sync request with deferral handling.
392 R_RETURN(this->CompleteSyncRequest(std::move(request))); 351 R_RETURN(this->CompleteSyncRequest(session));
393} 352}
394 353
395Result ServerManager::CompleteSyncRequest(RequestState&& request) { 354Result ServerManager::CompleteSyncRequest(Session* session) {
396 Result rc{ResultSuccess}; 355 Result res = ResultSuccess;
397 Result service_rc{ResultSuccess}; 356 Result service_res = ResultSuccess;
398 357
399 // Mark the request as not deferred. 358 // Mark the request as not deferred.
400 request.context->SetIsDeferred(false); 359 session->GetContext()->SetIsDeferred(false);
401 360
402 // Complete the request. We have exclusive access to this session. 361 // Complete the request. We have exclusive access to this session.
403 service_rc = request.manager->CompleteSyncRequest(request.session, *request.context); 362 auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle());
363 service_res =
364 session->GetManager()->CompleteSyncRequest(server_session, *session->GetContext());
404 365
405 // If we've been deferred, we're done. 366 // If we've been deferred, we're done.
406 if (request.context->GetIsDeferred()) { 367 if (session->GetContext()->GetIsDeferred()) {
407 // Insert into deferral list. 368 // Insert into deferred session list.
408 std::scoped_lock ll{m_list_mutex}; 369 std::scoped_lock ll{m_deferred_list_mutex};
409 m_deferrals.emplace_back(std::move(request)); 370 m_deferred_sessions.push_back(session);
410 371
411 // Finish. 372 // Finish.
412 R_SUCCEED(); 373 R_SUCCEED();
413 } 374 }
414 375
415 // Send the reply. 376 // Send the reply.
416 rc = request.session->SendReplyHLE(); 377 res = server_session->SendReplyHLE();
417 378
418 // If the session has been closed, we're done. 379 // If the session has been closed, we're done.
419 if (rc == Kernel::ResultSessionClosed || service_rc == IPC::ResultSessionClosed) { 380 if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) {
420 // Close the session. 381 this->DestroySession(session);
421 request.session->Close();
422
423 // Finish.
424 R_SUCCEED(); 382 R_SUCCEED();
425 } 383 }
426 384
427 ASSERT(R_SUCCEEDED(rc)); 385 R_ASSERT(res);
428 ASSERT(R_SUCCEEDED(service_rc)); 386 R_ASSERT(service_res);
429
430 // Reinsert the session.
431 {
432 std::scoped_lock ll{m_list_mutex};
433 m_sessions.emplace(request.session, std::move(request.manager));
434 }
435 387
436 // Signal the wakeup event. 388 // We succeeded, so we can process future messages on this session.
437 m_event->Signal(); 389 this->LinkToDeferredList(session);
438 390
439 // We succeeded.
440 R_SUCCEED(); 391 R_SUCCEED();
441} 392}
442 393
443Result ServerManager::OnDeferralEvent(std::list<RequestState>&& deferrals) { 394Result ServerManager::OnDeferralEvent() {
444 ON_RESULT_FAILURE { 395 // Clear event before grabbing the list.
445 std::scoped_lock ll{m_list_mutex}; 396 m_deferral_event->Clear();
446 m_deferrals.splice(m_deferrals.end(), deferrals);
447 };
448 397
449 while (!deferrals.empty()) { 398 // Get and clear list.
450 RequestState request = deferrals.front(); 399 const auto deferrals = [&] {
451 deferrals.pop_front(); 400 std::scoped_lock lk{m_deferred_list_mutex};
401 return std::move(m_deferred_sessions);
402 }();
452 403
453 // Try again to complete the request. 404 // Relink deferral event.
454 R_TRY(this->CompleteSyncRequest(std::move(request))); 405 this->LinkToDeferredList(std::addressof(*m_deferral_holder));
406
407 // For each session, try again to complete the request.
408 for (auto* session : deferrals) {
409 R_ASSERT(this->CompleteSyncRequest(session));
455 } 410 }
456 411
457 R_SUCCEED(); 412 R_SUCCEED();
458} 413}
459 414
415void ServerManager::DestroySession(Session* session) {
416 // Unlink.
417 {
418 std::scoped_lock lk{m_deferred_list_mutex};
419 m_sessions.erase(m_sessions.iterator_to(*session));
420 }
421
422 // Free the session.
423 delete session;
424}
425
460} // namespace Service 426} // namespace Service
diff --git a/src/core/hle/service/server_manager.h b/src/core/hle/service/server_manager.h
index c4bc07262..5173ce46e 100644
--- a/src/core/hle/service/server_manager.h
+++ b/src/core/hle/service/server_manager.h
@@ -3,18 +3,17 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <functional>
7#include <list> 6#include <list>
8#include <map>
9#include <mutex> 7#include <mutex>
10#include <string_view> 8#include <optional>
11#include <vector> 9#include <vector>
12 10
13#include "common/polyfill_thread.h" 11#include "common/polyfill_thread.h"
14#include "common/thread.h" 12#include "common/thread.h"
15#include "core/hle/result.h" 13#include "core/hle/result.h"
16#include "core/hle/service/hle_ipc.h" 14#include "core/hle/service/hle_ipc.h"
17#include "core/hle/service/mutex.h" 15#include "core/hle/service/os/multi_wait.h"
16#include "core/hle/service/os/mutex.h"
18 17
19namespace Core { 18namespace Core {
20class System; 19class System;
@@ -24,11 +23,13 @@ namespace Kernel {
24class KEvent; 23class KEvent;
25class KServerPort; 24class KServerPort;
26class KServerSession; 25class KServerSession;
27class KSynchronizationObject;
28} // namespace Kernel 26} // namespace Kernel
29 27
30namespace Service { 28namespace Service {
31 29
30class Port;
31class Session;
32
32class ServerManager { 33class ServerManager {
33public: 34public:
34 explicit ServerManager(Core::System& system); 35 explicit ServerManager(Core::System& system);
@@ -52,34 +53,40 @@ public:
52 static void RunServer(std::unique_ptr<ServerManager>&& server); 53 static void RunServer(std::unique_ptr<ServerManager>&& server);
53 54
54private: 55private:
55 struct RequestState; 56 void LinkToDeferredList(MultiWaitHolder* holder);
56 57 void LinkDeferred();
58 MultiWaitHolder* WaitSignaled();
59 Result Process(MultiWaitHolder* holder);
60 bool WaitAndProcessImpl();
57 Result LoopProcessImpl(); 61 Result LoopProcessImpl();
58 Result WaitAndProcessImpl(); 62
59 Result OnPortEvent(Kernel::KServerPort* port, SessionRequestHandlerFactory&& handler_factory); 63 Result OnPortEvent(Port* port);
60 Result OnSessionEvent(Kernel::KServerSession* session, 64 Result OnSessionEvent(Session* session);
61 std::shared_ptr<SessionRequestManager>&& manager); 65 Result OnDeferralEvent();
62 Result OnDeferralEvent(std::list<RequestState>&& deferrals); 66 Result CompleteSyncRequest(Session* session);
63 Result CompleteSyncRequest(RequestState&& state); 67
68private:
69 void DestroySession(Session* session);
64 70
65private: 71private:
66 Core::System& m_system; 72 Core::System& m_system;
67 Mutex m_serve_mutex; 73 Mutex m_selection_mutex;
68 std::mutex m_list_mutex;
69 74
70 // Guest state tracking 75 // Events
71 std::map<Kernel::KServerPort*, SessionRequestHandlerFactory> m_ports{}; 76 Kernel::KEvent* m_wakeup_event{};
72 std::map<Kernel::KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{};
73 Kernel::KEvent* m_event{};
74 Kernel::KEvent* m_deferral_event{}; 77 Kernel::KEvent* m_deferral_event{};
75 78
76 // Deferral tracking 79 // Deferred wait list
77 struct RequestState { 80 std::mutex m_deferred_list_mutex{};
78 Kernel::KServerSession* session; 81 MultiWait m_deferred_list{};
79 std::shared_ptr<HLERequestContext> context; 82
80 std::shared_ptr<SessionRequestManager> manager; 83 // Guest state tracking
81 }; 84 MultiWait m_multi_wait{};
82 std::list<RequestState> m_deferrals{}; 85 Common::IntrusiveListBaseTraits<Port>::ListType m_servers{};
86 Common::IntrusiveListBaseTraits<Session>::ListType m_sessions{};
87 std::list<Session*> m_deferred_sessions{};
88 std::optional<MultiWaitHolder> m_wakeup_holder{};
89 std::optional<MultiWaitHolder> m_deferral_holder{};
83 90
84 // Host state tracking 91 // Host state tracking
85 Common::Event m_stopped{}; 92 Common::Event m_stopped{};
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 06cbad268..f68c3c686 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -15,7 +15,7 @@
15#include "core/hle/service/aoc/aoc_u.h" 15#include "core/hle/service/aoc/aoc_u.h"
16#include "core/hle/service/apm/apm.h" 16#include "core/hle/service/apm/apm.h"
17#include "core/hle/service/audio/audio.h" 17#include "core/hle/service/audio/audio.h"
18#include "core/hle/service/bcat/bcat_module.h" 18#include "core/hle/service/bcat/bcat.h"
19#include "core/hle/service/bpc/bpc.h" 19#include "core/hle/service/bpc/bpc.h"
20#include "core/hle/service/btdrv/btdrv.h" 20#include "core/hle/service/btdrv/btdrv.h"
21#include "core/hle/service/btm/btm.h" 21#include "core/hle/service/btm/btm.h"
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 470521f44..b84b57d92 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/kernel/k_page_table.h" 11#include "core/hle/kernel/k_page_table.h"
12#include "core/hle/kernel/k_process.h" 12#include "core/hle/kernel/k_process.h"
13#include "core/hle/kernel/k_process_page_table.h" 13#include "core/hle/kernel/k_process_page_table.h"
14#include "core/hle/kernel/svc_types.h"
14#include "core/hle/service/hid/hid_server.h" 15#include "core/hle/service/hid/hid_server.h"
15#include "core/hle/service/sm/sm.h" 16#include "core/hle/service/sm/sm.h"
16#include "core/memory.h" 17#include "core/memory.h"
@@ -87,6 +88,20 @@ u64 StandardVmCallbacks::HidKeysDown() {
87 return static_cast<u64>(press_state & HID::NpadButton::All); 88 return static_cast<u64>(press_state & HID::NpadButton::All);
88} 89}
89 90
91void StandardVmCallbacks::PauseProcess() {
92 if (system.ApplicationProcess()->IsSuspended()) {
93 return;
94 }
95 system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Paused);
96}
97
98void StandardVmCallbacks::ResumeProcess() {
99 if (!system.ApplicationProcess()->IsSuspended()) {
100 return;
101 }
102 system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Runnable);
103}
104
90void StandardVmCallbacks::DebugLog(u8 id, u64 value) { 105void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
91 LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value); 106 LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
92} 107}
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index 619cabaa2..f52f2be7c 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -30,6 +30,8 @@ public:
30 void MemoryReadUnsafe(VAddr address, void* data, u64 size) override; 30 void MemoryReadUnsafe(VAddr address, void* data, u64 size) override;
31 void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override; 31 void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override;
32 u64 HidKeysDown() override; 32 u64 HidKeysDown() override;
33 void PauseProcess() override;
34 void ResumeProcess() override;
33 void DebugLog(u8 id, u64 value) override; 35 void DebugLog(u8 id, u64 value) override;
34 void CommandLog(std::string_view data) override; 36 void CommandLog(std::string_view data) override;
35 37
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp
index 8bc81e72d..f7097d01d 100644
--- a/src/core/memory/dmnt_cheat_vm.cpp
+++ b/src/core/memory/dmnt_cheat_vm.cpp
@@ -1205,9 +1205,9 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
1205 static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx]; 1205 static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
1206 } 1206 }
1207 } else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) { 1207 } else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) {
1208 // TODO: Pause cheat process 1208 callbacks->PauseProcess();
1209 } else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) { 1209 } else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
1210 // TODO: Resume cheat process 1210 callbacks->ResumeProcess();
1211 } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { 1211 } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
1212 // Read value from memory. 1212 // Read value from memory.
1213 u64 log_value = 0; 1213 u64 log_value = 0;
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h
index fed6a24ad..1c1ed1259 100644
--- a/src/core/memory/dmnt_cheat_vm.h
+++ b/src/core/memory/dmnt_cheat_vm.h
@@ -271,6 +271,9 @@ public:
271 271
272 virtual u64 HidKeysDown() = 0; 272 virtual u64 HidKeysDown() = 0;
273 273
274 virtual void PauseProcess() = 0;
275 virtual void ResumeProcess() = 0;
276
274 virtual void DebugLog(u8 id, u64 value) = 0; 277 virtual void DebugLog(u8 id, u64 value) = 0;
275 virtual void CommandLog(std::string_view data) = 0; 278 virtual void CommandLog(std::string_view data) = 0;
276 }; 279 };
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
index cbbb07ac7..2bebfeef9 100644
--- a/src/frontend_common/config.cpp
+++ b/src/frontend_common/config.cpp
@@ -885,15 +885,9 @@ void Config::Reload() {
885} 885}
886 886
887void Config::ClearControlPlayerValues() const { 887void Config::ClearControlPlayerValues() const {
888 // If key is an empty string, all keys in the current group() are removed. 888 // Removes the entire [Controls] section
889 const char* section = Settings::TranslateCategory(Settings::Category::Controls); 889 const char* section = Settings::TranslateCategory(Settings::Category::Controls);
890 CSimpleIniA::TNamesDepend keys; 890 config->Delete(section, nullptr, true);
891 config->GetAllKeys(section, keys);
892 for (const auto& key : keys) {
893 if (std::string(config->GetValue(section, key.pItem)).empty()) {
894 config->Delete(section, key.pItem);
895 }
896 }
897} 891}
898 892
899const std::string& Config::GetConfigFilePath() const { 893const std::string& Config::GetConfigFilePath() const {
diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp
index 245da582e..01261ba97 100644
--- a/src/hid_core/resource_manager.cpp
+++ b/src/hid_core/resource_manager.cpp
@@ -314,6 +314,7 @@ void ResourceManager::UnregisterAppletResourceUserId(u64 aruid) {
314 std::scoped_lock lock{shared_mutex}; 314 std::scoped_lock lock{shared_mutex};
315 applet_resource->UnregisterAppletResourceUserId(aruid); 315 applet_resource->UnregisterAppletResourceUserId(aruid);
316 npad->UnregisterAppletResourceUserId(aruid); 316 npad->UnregisterAppletResourceUserId(aruid);
317 // palma->UnregisterAppletResourceUserId(aruid);
317} 318}
318 319
319Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) { 320Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) {
@@ -324,6 +325,7 @@ Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle
324void ResourceManager::FreeAppletResourceId(u64 aruid) { 325void ResourceManager::FreeAppletResourceId(u64 aruid) {
325 std::scoped_lock lock{shared_mutex}; 326 std::scoped_lock lock{shared_mutex};
326 applet_resource->FreeAppletResourceId(aruid); 327 applet_resource->FreeAppletResourceId(aruid);
328 npad->FreeAppletResourceId(aruid);
327} 329}
328 330
329void ResourceManager::EnableInput(u64 aruid, bool is_enabled) { 331void ResourceManager::EnableInput(u64 aruid, bool is_enabled) {
diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp
index 053625b55..e10e97e1c 100644
--- a/src/hid_core/resources/npad/npad.cpp
+++ b/src/hid_core/resources/npad/npad.cpp
@@ -117,6 +117,10 @@ Result NPad::ActivateNpadResource(u64 aruid) {
117 return npad_resource.Activate(aruid); 117 return npad_resource.Activate(aruid);
118} 118}
119 119
120void NPad::FreeAppletResourceId(u64 aruid) {
121 return npad_resource.FreeAppletResourceId(aruid);
122}
123
120void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) { 124void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) {
121 if (type == Core::HID::ControllerTriggerType::All) { 125 if (type == Core::HID::ControllerTriggerType::All) {
122 ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); 126 ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h
index c63488346..99e761127 100644
--- a/src/hid_core/resources/npad/npad.h
+++ b/src/hid_core/resources/npad/npad.h
@@ -58,6 +58,8 @@ public:
58 Result ActivateNpadResource(); 58 Result ActivateNpadResource();
59 Result ActivateNpadResource(u64 aruid); 59 Result ActivateNpadResource(u64 aruid);
60 60
61 void FreeAppletResourceId(u64 aruid);
62
61 // When the controller is requesting an update for the shared memory 63 // When the controller is requesting an update for the shared memory
62 void OnUpdate(const Core::Timing::CoreTiming& core_timing); 64 void OnUpdate(const Core::Timing::CoreTiming& core_timing);
63 65
diff --git a/src/hid_core/resources/npad/npad_resource.cpp b/src/hid_core/resources/npad/npad_resource.cpp
index 8dd86b58e..79f7d74c0 100644
--- a/src/hid_core/resources/npad/npad_resource.cpp
+++ b/src/hid_core/resources/npad/npad_resource.cpp
@@ -67,7 +67,7 @@ Result NPadResource::RegisterAppletResourceUserId(u64 aruid) {
67void NPadResource::UnregisterAppletResourceUserId(u64 aruid) { 67void NPadResource::UnregisterAppletResourceUserId(u64 aruid) {
68 const u64 aruid_index = GetIndexFromAruid(aruid); 68 const u64 aruid_index = GetIndexFromAruid(aruid);
69 69
70 DestroyStyleSetUpdateEvents(aruid); 70 FreeAppletResourceId(aruid);
71 if (aruid_index < AruidIndexMax) { 71 if (aruid_index < AruidIndexMax) {
72 state[aruid_index] = {}; 72 state[aruid_index] = {};
73 registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete; 73 registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete;
@@ -80,14 +80,18 @@ void NPadResource::UnregisterAppletResourceUserId(u64 aruid) {
80 } 80 }
81} 81}
82 82
83void NPadResource::DestroyStyleSetUpdateEvents(u64 aruid) { 83void NPadResource::FreeAppletResourceId(u64 aruid) {
84 const u64 aruid_index = GetIndexFromAruid(aruid); 84 const u64 aruid_index = GetIndexFromAruid(aruid);
85 85
86 if (aruid_index >= AruidIndexMax) { 86 if (aruid_index >= AruidIndexMax) {
87 return; 87 return;
88 } 88 }
89 89
90 for (auto& controller_state : state[aruid_index].controller_state) { 90 auto& aruid_data = state[aruid_index];
91
92 aruid_data.flag.is_assigned.Assign(false);
93
94 for (auto& controller_state : aruid_data.controller_state) {
91 if (!controller_state.is_styleset_update_event_initialized) { 95 if (!controller_state.is_styleset_update_event_initialized) {
92 continue; 96 continue;
93 } 97 }
diff --git a/src/hid_core/resources/npad/npad_resource.h b/src/hid_core/resources/npad/npad_resource.h
index aed89eec6..8ee5702fd 100644
--- a/src/hid_core/resources/npad/npad_resource.h
+++ b/src/hid_core/resources/npad/npad_resource.h
@@ -55,7 +55,7 @@ public:
55 Result RegisterAppletResourceUserId(u64 aruid); 55 Result RegisterAppletResourceUserId(u64 aruid);
56 void UnregisterAppletResourceUserId(u64 aruid); 56 void UnregisterAppletResourceUserId(u64 aruid);
57 57
58 void DestroyStyleSetUpdateEvents(u64 aruid); 58 void FreeAppletResourceId(u64 aruid);
59 59
60 Result Activate(u64 aruid); 60 Result Activate(u64 aruid);
61 Result Activate(); 61 Result Activate();
diff --git a/src/video_core/host_shaders/vulkan_present.vert b/src/video_core/host_shaders/vulkan_present.vert
index 249c9675a..c0e6e8537 100644
--- a/src/video_core/host_shaders/vulkan_present.vert
+++ b/src/video_core/host_shaders/vulkan_present.vert
@@ -19,15 +19,13 @@ layout (push_constant) uniform PushConstants {
19// Any member of a push constant block that is declared as an 19// Any member of a push constant block that is declared as an
20// array must only be accessed with dynamically uniform indices. 20// array must only be accessed with dynamically uniform indices.
21ScreenRectVertex GetVertex(int index) { 21ScreenRectVertex GetVertex(int index) {
22 switch (index) { 22 if (index < 1) {
23 case 0:
24 default:
25 return vertices[0]; 23 return vertices[0];
26 case 1: 24 } else if (index < 2) {
27 return vertices[1]; 25 return vertices[1];
28 case 2: 26 } else if (index < 3) {
29 return vertices[2]; 27 return vertices[2];
30 case 3: 28 } else {
31 return vertices[3]; 29 return vertices[3];
32 } 30 }
33} 31}
diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h
index 398833e7a..77ec1fcde 100644
--- a/src/yuzu/multiplayer/lobby_p.h
+++ b/src/yuzu/multiplayer/lobby_p.h
@@ -202,12 +202,19 @@ public:
202 case Qt::ForegroundRole: { 202 case Qt::ForegroundRole: {
203 auto members = data(MemberListRole).toList(); 203 auto members = data(MemberListRole).toList();
204 auto max_players = data(MaxPlayerRole).toInt(); 204 auto max_players = data(MaxPlayerRole).toInt();
205 const QColor room_full_color(255, 48, 32);
206 const QColor room_almost_full_color(255, 140, 32);
207 const QColor room_has_players_color(32, 160, 32);
208 const QColor room_empty_color(128, 128, 128);
209
205 if (members.size() >= max_players) { 210 if (members.size() >= max_players) {
206 return QBrush(QColor(255, 48, 32)); 211 return QBrush(room_full_color);
207 } else if (members.size() == (max_players - 1)) { 212 } else if (members.size() == (max_players - 1)) {
208 return QBrush(QColor(255, 140, 32)); 213 return QBrush(room_almost_full_color);
209 } else if (members.size() == 0) { 214 } else if (members.size() == 0) {
210 return QBrush(QColor(128, 128, 128)); 215 return QBrush(room_empty_color);
216 } else if (members.size() > 0 && members.size() < (max_players - 1)) {
217 return QBrush(room_has_players_color);
211 } 218 }
212 // FIXME: How to return a value that tells Qt not to modify the 219 // FIXME: How to return a value that tells Qt not to modify the
213 // text color from the default (as if Qt::ForegroundRole wasn't overridden)? 220 // text color from the default (as if Qt::ForegroundRole wasn't overridden)?