Compare commits

..

54 Commits

Author SHA1 Message Date
df869c3bb9 Android 245 2024-02-12 00:57:02 +00:00
0a1bc07a33 Merge yuzu-emu#12975 2024-02-12 00:57:02 +00:00
78ddc16f6f Merge yuzu-emu#12873 2024-02-12 00:57:01 +00:00
7939fe7664 Merge yuzu-emu#12756 2024-02-12 00:57:01 +00:00
42a414b377 Merge yuzu-emu#12749 2024-02-12 00:57:01 +00:00
ff83b9ad82 Merge yuzu-emu#12461 2024-02-12 00:57:01 +00:00
98db796fde Merge pull request #12986 from t895/input-config-clear-fix
config: Always delete control settings in ClearControlPlayerValues
2024-02-11 12:24:18 -05:00
adebc96a9c config: Always delete control settings in ClearControlPlayerValues 2024-02-11 07:35:54 -05:00
564a65a82e Merge pull request #12981 from lat9nq/tzdb-asan-custom
tzdb_to_nx: Update to latest
2024-02-10 22:42:47 -05:00
501ff2eda5 Merge pull request #12980 from merryhime/race-me
dynarmic: Fix invalidation race
2024-02-10 22:42:42 -05:00
999ec5739d Merge pull request #12978 from liamwhite/ffs-qcom
host_shaders: add vendor workaround for adreno drivers
2024-02-10 22:42:25 -05:00
68b2db21b1 tzdb_to_nx: Update to latest
Includes memory leak fix.

Includes option to specify a custom zoneinfo dir.
2024-02-10 17:45:18 -05:00
2337397a15 Merge pull request #12969 from german77/bcat-interface
service: bcat: Migrate and refractor service to new IPC
2024-02-10 16:00:43 -05:00
7c56ecca3f Merge pull request #12949 from liamwhite/multi-wait
service: add os types and multi wait API
2024-02-10 16:00:34 -05:00
211544fbc8 externals: Update dynarmic to 6.6.3 2024-02-10 19:40:47 +00:00
4eeac731ff host_shaders: add vendor workaround for adreno drivers 2024-02-10 12:02:37 -05:00
816d03f7d9 service: bcat: Address review issues 2024-02-10 00:23:23 -06:00
909f7eb3d2 service: bcat: Implement news interfaces 2024-02-10 00:23:22 -06:00
7c2e9a6596 service: bcat: Migrate and refractor service to new IPC 2024-02-10 00:23:22 -06:00
fe6934593f Fix multiplayer player count color in dark themes | Temp fix until #12744: Add green color for counts > 0 and < max_players - 1 (#12930)
* fix intended player count color in dark themes

* Refactor

* Change to green color for white and dark themes

* Add const to the colors and extra name for green color
2024-02-09 18:45:11 -06:00
52c8adc7ed Merge pull request #12951 from liamwhite/more-ipc
ipc: additional fixes
2024-02-09 10:51:03 -06:00
7ec7ff0f30 Merge pull request #12920 from t895/jni-common
android: Move JNI setup and helpers to common
2024-02-09 11:49:25 -05:00
a133eadf06 Merge pull request #12927 from german77/cheat-pause
dmnt: cheat: Add pause and resume support
2024-02-09 11:47:34 -05:00
89dd0fa932 Merge pull request #12968 from t895/thermal-status
android: Thermal throttling indicator
2024-02-09 11:47:17 -05:00
a9dcfe2a42 Merge pull request #12964 from t895/foreground-service-test
android: Remove foreground service
2024-02-09 11:47:11 -05:00
2ad8d614b5 Merge pull request #12966 from german77/free_npad
service: hid: Free npad applet resource
2024-02-09 11:47:05 -05:00
f44183db9e android: Use utility function for applying view margins 2024-02-09 07:07:06 -05:00
5fa9bc192c android: Add thermal throttling overlay 2024-02-09 07:07:05 -05:00
f9a559d2b7 Merge pull request #12967 from german77/let_me_out
service: Fix OutLargeData attributes
2024-02-08 21:33:22 -05:00
af87365672 android: Remove foreground service 2024-02-08 21:04:14 -05:00
03a23c037a service: Fix OutLargeData attributes 2024-02-08 19:40:06 -06:00
0ac777460d service: hid: Free npad applet resource 2024-02-08 18:50:54 -06:00
71e59bdcd8 Merge pull request #12963 from t895/versioning-fix
android: Fix regex for git version
2024-02-08 17:03:32 -05:00
0a1283f94f android: Fix regex for git version 2024-02-08 14:24:15 -05:00
2600ac65c8 android: Run OnEmulationStarted frontend callback in another thread
The JVM has problems with attaching to a Fiber so we start a new thread and wait for the result here.
2024-02-08 14:13:46 -05:00
c8e8c614a0 common: fs: Expand android macros 2024-02-08 14:13:46 -05:00
e7c4c8b993 android: Move JNI setup and helpers to common 2024-02-08 13:45:26 -05:00
f049453dd6 Merge pull request #12903 from liamwhite/const-offset
shader_recompiler: use only ConstOffset for OpImageFetch
2024-02-08 17:00:45 +01:00
cac37a6f6e Merge pull request #12954 from german77/hidbus-interface
service: hid: Migrate hidbus to new interface
2024-02-08 11:00:11 -05:00
263dfa95e4 Merge pull request #12914 from FernandoS27/vc-refactor
VideoCore Refactor Part 1.
2024-02-08 10:59:59 -05:00
bc9711cb1e Merge pull request #12953 from FernandoS27/zero-fps-mah-ass
SMMU: Ensure the backing address range matches the current
2024-02-08 10:59:52 -05:00
b4d88a7bb4 service: hid: Migrate hidbus to new interface 2024-02-07 18:07:32 -06:00
ae833aa9c0 SMMU: Ensure the backing address range matches the current 2024-02-07 23:47:42 +01:00
fee263c59c ipc: additional fixes 2024-02-07 15:06:15 -05:00
5a64a77df3 glue: use multi wait API 2024-02-07 12:15:01 -05:00
6810929f6a server_manager: use multi wait API 2024-02-07 12:15:01 -05:00
9404633bfd service: add os types and multi wait API 2024-02-07 12:14:46 -05:00
fa47ac1c9f Common: Rename SplitRangeSet to OverlapRangeSet 2024-02-05 23:01:17 +01:00
c52d7cc694 dmnt: cheat: Add pause and resume support 2024-02-05 14:38:26 -06:00
f296a9ce9a shader_recompiler: use only ConstOffset for OpImageFetch 2024-02-05 12:01:09 -05:00
0d5a3abeae Buffer Cache: Refactor to use Range sets instead 2024-02-05 11:06:52 +01:00
accccc0cbf NVDRV: Refactor HeapMapper to use RangeSets 2024-02-04 20:01:50 +01:00
01ba6cf610 Common: Introduce Range Sets 2024-02-04 20:01:50 +01:00
4841dc0b74 VideoCore: Move Slot Vector to Common 2024-02-04 20:01:47 +01:00
154 changed files with 12042 additions and 1958 deletions

View File

@ -1,13 +1,10 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [12461](https://github.com/yuzu-emu/yuzu//pull/12461) | [`4c08a0e6d`](https://github.com/yuzu-emu/yuzu//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [12461](https://github.com/yuzu-emu/yuzu//pull/12461) | [`acc26667b`](https://github.com/yuzu-emu/yuzu//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [12749](https://github.com/yuzu-emu/yuzu//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12756](https://github.com/yuzu-emu/yuzu//pull/12756) | [`cd3de0848`](https://github.com/yuzu-emu/yuzu//pull/12756/files) | general: applet multiprocess | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12903](https://github.com/yuzu-emu/yuzu//pull/12903) | [`f296a9ce9`](https://github.com/yuzu-emu/yuzu//pull/12903/files) | shader_recompiler: use only ConstOffset for OpImageFetch | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12914](https://github.com/yuzu-emu/yuzu//pull/12914) | [`fa47ac1c9`](https://github.com/yuzu-emu/yuzu//pull/12914/files) | VideoCore Refactor Part 1. | [FernandoS27](https://github.com/FernandoS27/) | Yes |
| [12920](https://github.com/yuzu-emu/yuzu//pull/12920) | [`62fc6d5c3`](https://github.com/yuzu-emu/yuzu//pull/12920/files) | android: Move JNI setup and helpers to common | [t895](https://github.com/t895/) | Yes |
| [12951](https://github.com/yuzu-emu/yuzu//pull/12951) | [`fee263c59`](https://github.com/yuzu-emu/yuzu//pull/12951/files) | ipc: additional fixes | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12953](https://github.com/yuzu-emu/yuzu//pull/12953) | [`ae833aa9c`](https://github.com/yuzu-emu/yuzu//pull/12953/files) | SMMU: Ensure the backing address range matches the current | [FernandoS27](https://github.com/FernandoS27/) | Yes |
| [12756](https://github.com/yuzu-emu/yuzu//pull/12756) | [`4677fd3f6`](https://github.com/yuzu-emu/yuzu//pull/12756/files) | general: applet multiprocess | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12873](https://github.com/yuzu-emu/yuzu//pull/12873) | [`7a4ea8991`](https://github.com/yuzu-emu/yuzu//pull/12873/files) | GPU: Implement channel scheduling. | [FernandoS27](https://github.com/FernandoS27/) | Yes |
| [12975](https://github.com/yuzu-emu/yuzu//pull/12975) | [`9ce43ee67`](https://github.com/yuzu-emu/yuzu//pull/12975/files) | Texture Cache: Fix untracking on GPU remap | [FernandoS27](https://github.com/FernandoS27/) | Yes |
End of merge log. You can find the original README.md below the break.

View File

@ -314,3 +314,10 @@ endif()
if (NOT TARGET SimpleIni::SimpleIni)
add_subdirectory(simpleini)
endif()
# sse2neon
if (ARCHITECTURE_arm64 AND NOT TARGET sse2neon)
add_library(sse2neon INTERFACE)
target_include_directories(sse2neon INTERFACE sse2neon)
endif()

9282
externals/sse2neon/sse2neon.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -259,13 +259,20 @@ fun runGitCommand(command: List<String>): String {
}
fun getGitVersion(): String {
val gitVersion = runGitCommand(listOf("git", "describe", "--always", "--long"))
val gitVersion = runGitCommand(
listOf(
"git",
"describe",
"--always",
"--long"
)
).replace(Regex("(-0)?-[^-]+$"), "")
val versionName = if (System.getenv("GITHUB_ACTIONS") != null) {
System.getenv("GIT_TAG_NAME") ?: gitVersion
} else {
gitVersion
}
return versionName.replace(Regex("(-0)?-[^-]+$"), "").ifEmpty { "0.0" }
return versionName.ifEmpty { "0.0" }
}
fun getGitHash(): String =

View File

@ -12,8 +12,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
<uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
@ -80,10 +78,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:resource="@xml/nfc_tech_filter" />
</activity>
<service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/>
</service>
<provider
android:name=".features.DocumentProvider"
android:authorities="${applicationId}.user"

View File

@ -17,17 +17,6 @@ fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
class YuzuApplication : Application() {
private fun createNotificationChannels() {
val emulationChannel = NotificationChannel(
getString(R.string.emulation_notification_channel_id),
getString(R.string.emulation_notification_channel_name),
NotificationManager.IMPORTANCE_LOW
)
emulationChannel.description = getString(
R.string.emulation_notification_channel_description
)
emulationChannel.setSound(null, null)
emulationChannel.vibrationPattern = null
val noticeChannel = NotificationChannel(
getString(R.string.notice_notification_channel_id),
getString(R.string.notice_notification_channel_name),
@ -39,7 +28,6 @@ class YuzuApplication : Application() {
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(emulationChannel)
notificationManager.createNotificationChannel(noticeChannel)
}

View File

@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.activities
import android.annotation.SuppressLint
import android.app.Activity
import android.app.PendingIntent
import android.app.PictureInPictureParams
import android.app.RemoteAction
@ -45,7 +44,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.MemoryUtil
@ -74,11 +72,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private val emulationViewModel: EmulationViewModel by viewModels()
override fun onDestroy() {
stopForegroundService(this)
super.onDestroy()
}
override fun onCreate(savedInstanceState: Bundle?) {
Log.gameLaunched = true
ThemeHelper.setTheme(this)
@ -125,10 +118,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
.apply()
}
}
// Start a foreground service to prevent the app from getting killed in the background
val startIntent = Intent(this, ForegroundService::class.java)
startForegroundService(startIntent)
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
@ -481,12 +470,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
activity.startActivity(launcher)
}
fun stopForegroundService(activity: Activity) {
val startIntent = Intent(activity, ForegroundService::class.java)
startIntent.action = ForegroundService.ACTION_STOP
activity.startForegroundService(startIntent)
}
private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean {
if (view == null) {
return true

View File

@ -25,7 +25,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
HAPTIC_FEEDBACK("haptic_feedback"),
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
SHOW_INPUT_OVERLAY("show_input_overlay"),
TOUCHSCREEN("touchscreen");
TOUCHSCREEN("touchscreen"),
SHOW_THERMAL_OVERLAY("show_thermal_overlay");
override fun getBoolean(needsGlobal: Boolean): Boolean =
NativeConfig.getBoolean(key, needsGlobal)

View File

@ -8,7 +8,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.SettingsViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class SettingsFragment : Fragment() {
private lateinit var presenter: SettingsFragmentPresenter
@ -125,18 +125,10 @@ class SettingsFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
mlpSettingsList.leftMargin = leftInsets
mlpSettingsList.rightMargin = rightInsets
binding.listSettings.layoutParams = mlpSettingsList
binding.listSettings.updatePadding(
bottom = barInsets.bottom
)
binding.listSettings.updateMargins(left = leftInsets, right = rightInsets)
binding.listSettings.updatePadding(bottom = barInsets.bottom)
val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.appbarSettings.layoutParams = mlpAppBar
binding.appbarSettings.updateMargins(left = leftInsets, right = rightInsets)
windowInsets
}
}

View File

@ -13,7 +13,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.widget.Toast
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
@ -26,6 +25,7 @@ import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class AboutFragment : Fragment() {
private var _binding: FragmentAboutBinding? = null
@ -114,15 +114,8 @@ class AboutFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams
mlpToolbar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets
binding.toolbarAbout.layoutParams = mlpToolbar
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.scrollAbout.layoutParams = mlpScrollAbout
binding.toolbarAbout.updateMargins(left = leftInsets, right = rightInsets)
binding.scrollAbout.updateMargins(left = leftInsets, right = rightInsets)
binding.contentAbout.updatePadding(bottom = barInsets.bottom)

View File

@ -31,6 +31,7 @@ import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.AddonUtil
import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.File
class AddonsFragment : Fragment() {
@ -202,27 +203,19 @@ class AddonsFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarAddons.layoutParams as ViewGroup.MarginLayoutParams
mlpToolbar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets
binding.toolbarAddons.layoutParams = mlpToolbar
val mlpAddonsList = binding.listAddons.layoutParams as ViewGroup.MarginLayoutParams
mlpAddonsList.leftMargin = leftInsets
mlpAddonsList.rightMargin = rightInsets
binding.listAddons.layoutParams = mlpAddonsList
binding.toolbarAddons.updateMargins(left = leftInsets, right = rightInsets)
binding.listAddons.updateMargins(left = leftInsets, right = rightInsets)
binding.listAddons.updatePadding(
bottom = barInsets.bottom +
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
)
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonInstall.layoutParams = mlpFab
binding.buttonInstall.updateMargins(
left = leftInsets + fabSpacing,
right = rightInsets + fabSpacing,
bottom = barInsets.bottom + fabSpacing
)
windowInsets
}

View File

@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding
import org.yuzu.yuzu_emu.model.Applet
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class AppletLauncherFragment : Fragment() {
private var _binding: FragmentAppletLauncherBinding? = null
@ -95,16 +96,8 @@ class AppletLauncherFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarApplets.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.toolbarApplets.layoutParams = mlpAppBar
val mlpListApplets =
binding.listApplets.layoutParams as ViewGroup.MarginLayoutParams
mlpListApplets.leftMargin = leftInsets
mlpListApplets.rightMargin = rightInsets
binding.listApplets.layoutParams = mlpListApplets
binding.toolbarApplets.updateMargins(left = leftInsets, right = rightInsets)
binding.listApplets.updateMargins(left = leftInsets, right = rightInsets)
binding.listApplets.updatePadding(bottom = barInsets.bottom)

View File

@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.File
import java.io.IOException
@ -141,23 +142,15 @@ class DriverManagerFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.toolbarDrivers.layoutParams = mlpAppBar
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
mlplistDrivers.leftMargin = leftInsets
mlplistDrivers.rightMargin = rightInsets
binding.listDrivers.layoutParams = mlplistDrivers
binding.toolbarDrivers.updateMargins(left = leftInsets, right = rightInsets)
binding.listDrivers.updateMargins(left = leftInsets, right = rightInsets)
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonInstall.layoutParams = mlpFab
binding.buttonInstall.updateMargins(
left = leftInsets + fabSpacing,
right = rightInsets + fabSpacing,
bottom = barInsets.bottom + fabSpacing
)
binding.listDrivers.updatePadding(
bottom = barInsets.bottom +

View File

@ -19,6 +19,7 @@ import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentEarlyAccessBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class EarlyAccessFragment : Fragment() {
private var _binding: FragmentEarlyAccessBinding? = null
@ -73,10 +74,7 @@ class EarlyAccessFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.appbarEa.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.appbarEa.layoutParams = mlpAppBar
binding.appbarEa.updateMargins(left = leftInsets, right = rightInsets)
binding.scrollEa.updatePadding(
left = leftInsets,

View File

@ -13,6 +13,7 @@ import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import android.os.SystemClock
import android.view.*
import android.widget.TextView
@ -23,6 +24,7 @@ import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.drawerlayout.widget.DrawerLayout
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
import androidx.fragment.app.Fragment
@ -38,7 +40,6 @@ import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
@ -64,6 +65,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var emulationState: EmulationState
private var emulationActivity: EmulationActivity? = null
private var perfStatsUpdater: (() -> Unit)? = null
private var thermalStatsUpdater: (() -> Unit)? = null
private var _binding: FragmentEmulationBinding? = null
private val binding get() = _binding!!
@ -77,6 +79,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private var isInFoldableLayout = false
private lateinit var powerManager: PowerManager
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is EmulationActivity) {
@ -102,6 +106,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
super.onCreate(savedInstanceState)
updateOrientation()
powerManager = requireContext().getSystemService(Context.POWER_SERVICE) as PowerManager
val intentUri: Uri? = requireActivity().intent.data
var intentGame: Game? = null
if (intentUri != null) {
@ -394,8 +400,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationState.updateSurface()
// Setup overlay
// Setup overlays
updateShowFpsOverlay()
updateThermalOverlay()
}
}
}
@ -553,6 +560,38 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
private fun updateThermalOverlay() {
if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) {
thermalStatsUpdater = {
if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
) {
val thermalStatus = when (powerManager.currentThermalStatus) {
PowerManager.THERMAL_STATUS_LIGHT -> "😥"
PowerManager.THERMAL_STATUS_MODERATE -> "🥵"
PowerManager.THERMAL_STATUS_SEVERE -> "🔥"
PowerManager.THERMAL_STATUS_CRITICAL,
PowerManager.THERMAL_STATUS_EMERGENCY,
PowerManager.THERMAL_STATUS_SHUTDOWN -> "☢️"
else -> "🙂"
}
if (_binding != null) {
binding.showThermalsText.text = thermalStatus
}
thermalStatsUpdateHandler.postDelayed(thermalStatsUpdater!!, 1000)
}
}
thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
binding.showThermalsText.visibility = View.VISIBLE
} else {
if (thermalStatsUpdater != null) {
thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
}
binding.showThermalsText.visibility = View.GONE
}
}
@SuppressLint("SourceLockedOrientationActivity")
private fun updateOrientation() {
emulationActivity?.let {
@ -641,6 +680,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
popup.menu.apply {
findItem(R.id.menu_toggle_fps).isChecked =
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
findItem(R.id.thermal_indicator).isChecked =
BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
findItem(R.id.menu_rel_stick_center).isChecked =
BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
@ -660,6 +701,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true
}
R.id.thermal_indicator -> {
it.isChecked = !it.isChecked
BooleanSetting.SHOW_THERMAL_OVERLAY.setBoolean(it.isChecked)
updateThermalOverlay()
true
}
R.id.menu_edit_overlay -> {
binding.drawerLayout.close()
binding.surfaceInputOverlay.requestFocus()
@ -850,7 +898,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
right = cutInsets.right
}
v.setPadding(left, cutInsets.top, right, 0)
v.updatePadding(left = left, top = cutInsets.top, right = right)
windowInsets
}
}
@ -1003,5 +1051,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
companion object {
private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
private val thermalStatsUpdateHandler = Handler(Looper.myLooper()!!)
}
}

View File

@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class GameFoldersFragment : Fragment() {
private var _binding: FragmentFoldersBinding? = null
@ -100,23 +101,16 @@ class GameFoldersFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarFolders.layoutParams as ViewGroup.MarginLayoutParams
mlpToolbar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets
binding.toolbarFolders.layoutParams = mlpToolbar
binding.toolbarFolders.updateMargins(left = leftInsets, right = rightInsets)
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonAdd.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonAdd.layoutParams = mlpFab
binding.buttonAdd.updateMargins(
left = leftInsets + fabSpacing,
right = rightInsets + fabSpacing,
bottom = barInsets.bottom + fabSpacing
)
val mlpListFolders = binding.listFolders.layoutParams as ViewGroup.MarginLayoutParams
mlpListFolders.leftMargin = leftInsets
mlpListFolders.rightMargin = rightInsets
binding.listFolders.layoutParams = mlpListFolders
binding.listFolders.updateMargins(left = leftInsets, right = rightInsets)
binding.listFolders.updatePadding(
bottom = barInsets.bottom +

View File

@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
import org.yuzu.yuzu_emu.model.GameVerificationResult
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.GameMetadata
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class GameInfoFragment : Fragment() {
private var _binding: FragmentGameInfoBinding? = null
@ -122,11 +123,13 @@ class GameInfoFragment : Fragment() {
titleId = R.string.verify_success,
descriptionId = R.string.operation_completed_successfully
)
GameVerificationResult.Failed ->
MessageDialogFragment.newInstance(
titleId = R.string.verify_failure,
descriptionId = R.string.verify_failure_description
)
GameVerificationResult.NotImplemented ->
MessageDialogFragment.newInstance(
titleId = R.string.verify_no_result,
@ -165,15 +168,8 @@ class GameInfoFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarInfo.layoutParams as ViewGroup.MarginLayoutParams
mlpToolbar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets
binding.toolbarInfo.layoutParams = mlpToolbar
val mlpScrollAbout = binding.scrollInfo.layoutParams as ViewGroup.MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.scrollInfo.layoutParams = mlpScrollAbout
binding.toolbarInfo.updateMargins(left = leftInsets, right = rightInsets)
binding.scrollInfo.updateMargins(left = leftInsets, right = rightInsets)
binding.contentInfo.updatePadding(bottom = barInsets.bottom)

View File

@ -46,6 +46,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.BufferedOutputStream
import java.io.File
@ -320,46 +321,25 @@ class GamePropertiesFragment : Fragment() {
val smallLayout = resources.getBoolean(R.bool.small_layout)
if (smallLayout) {
val mlpListAll =
binding.listAll.layoutParams as ViewGroup.MarginLayoutParams
mlpListAll.leftMargin = leftInsets
mlpListAll.rightMargin = rightInsets
binding.listAll.layoutParams = mlpListAll
binding.listAll.updateMargins(left = leftInsets, right = rightInsets)
} else {
if (ViewCompat.getLayoutDirection(binding.root) ==
ViewCompat.LAYOUT_DIRECTION_LTR
) {
val mlpListAll =
binding.listAll.layoutParams as ViewGroup.MarginLayoutParams
mlpListAll.rightMargin = rightInsets
binding.listAll.layoutParams = mlpListAll
val mlpIconLayout =
binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams
mlpIconLayout.topMargin = barInsets.top
mlpIconLayout.leftMargin = leftInsets
binding.iconLayout!!.layoutParams = mlpIconLayout
binding.listAll.updateMargins(right = rightInsets)
binding.iconLayout!!.updateMargins(top = barInsets.top, left = leftInsets)
} else {
val mlpListAll =
binding.listAll.layoutParams as ViewGroup.MarginLayoutParams
mlpListAll.leftMargin = leftInsets
binding.listAll.layoutParams = mlpListAll
val mlpIconLayout =
binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams
mlpIconLayout.topMargin = barInsets.top
mlpIconLayout.rightMargin = rightInsets
binding.iconLayout!!.layoutParams = mlpIconLayout
binding.listAll.updateMargins(left = leftInsets)
binding.iconLayout!!.updateMargins(top = barInsets.top, right = rightInsets)
}
}
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonStart.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonStart.layoutParams = mlpFab
binding.buttonStart.updateMargins(
left = leftInsets + fabSpacing,
right = rightInsets + fabSpacing,
bottom = barInsets.bottom + fabSpacing
)
binding.layoutAll.updatePadding(
top = barInsets.top,

View File

@ -12,7 +12,6 @@ import android.provider.DocumentsContract
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
@ -44,6 +43,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class HomeSettingsFragment : Fragment() {
private var _binding: FragmentHomeSettingsBinding? = null
@ -408,10 +408,7 @@ class HomeSettingsFragment : Fragment() {
bottom = barInsets.bottom
)
val mlpScrollSettings = binding.scrollViewSettings.layoutParams as MarginLayoutParams
mlpScrollSettings.leftMargin = leftInsets
mlpScrollSettings.rightMargin = rightInsets
binding.scrollViewSettings.layoutParams = mlpScrollSettings
binding.scrollViewSettings.updateMargins(left = leftInsets, right = rightInsets)
binding.linearLayoutSettings.updatePadding(bottom = spacingNavigation)

View File

@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.BufferedOutputStream
import java.io.File
import java.math.BigInteger
@ -172,16 +173,8 @@ class InstallableFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarInstallables.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.toolbarInstallables.layoutParams = mlpAppBar
val mlpScrollAbout =
binding.listInstallables.layoutParams as ViewGroup.MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.listInstallables.layoutParams = mlpScrollAbout
binding.toolbarInstallables.updateMargins(left = leftInsets, right = rightInsets)
binding.listInstallables.updateMargins(left = leftInsets, right = rightInsets)
binding.listInstallables.updatePadding(bottom = barInsets.bottom)

View File

@ -7,7 +7,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
@ -22,6 +21,7 @@ import org.yuzu.yuzu_emu.adapters.LicenseAdapter
import org.yuzu.yuzu_emu.databinding.FragmentLicensesBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.License
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class LicensesFragment : Fragment() {
private var _binding: FragmentLicensesBinding? = null
@ -122,15 +122,8 @@ class LicensesFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.appbarLicenses.layoutParams as MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.appbarLicenses.layoutParams = mlpAppBar
val mlpScrollAbout = binding.listLicenses.layoutParams as MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.listLicenses.layoutParams = mlpScrollAbout
binding.appbarLicenses.updateMargins(left = leftInsets, right = rightInsets)
binding.listLicenses.updateMargins(left = leftInsets, right = rightInsets)
binding.listLicenses.updatePadding(bottom = barInsets.bottom)

View File

@ -29,6 +29,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.model.SettingsViewModel
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class SettingsSearchFragment : Fragment() {
private var _binding: FragmentSettingsSearchBinding? = null
@ -174,15 +175,14 @@ class SettingsSearchFragment : Fragment() {
bottom = barInsets.bottom
)
val mlpSettingsList = binding.settingsList.layoutParams as ViewGroup.MarginLayoutParams
mlpSettingsList.leftMargin = leftInsets + sideMargin
mlpSettingsList.rightMargin = rightInsets + sideMargin
binding.settingsList.layoutParams = mlpSettingsList
val mlpDivider = binding.divider.layoutParams as ViewGroup.MarginLayoutParams
mlpDivider.leftMargin = leftInsets + sideMargin
mlpDivider.rightMargin = rightInsets + sideMargin
binding.divider.layoutParams = mlpDivider
binding.settingsList.updateMargins(
left = leftInsets + sideMargin,
right = rightInsets + sideMargin
)
binding.divider.updateMargins(
left = leftInsets + sideMargin,
right = rightInsets + sideMargin
)
windowInsets
}

View File

@ -8,7 +8,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class GamesFragment : Fragment() {
private var _binding: FragmentGamesBinding? = null
@ -169,15 +169,16 @@ class GamesFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpSwipe = binding.swipeRefresh.layoutParams as MarginLayoutParams
val left: Int
val right: Int
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
mlpSwipe.leftMargin = leftInsets + spacingNavigationRail
mlpSwipe.rightMargin = rightInsets
left = leftInsets + spacingNavigationRail
right = rightInsets
} else {
mlpSwipe.leftMargin = leftInsets
mlpSwipe.rightMargin = rightInsets + spacingNavigationRail
left = leftInsets
right = rightInsets + spacingNavigationRail
}
binding.swipeRefresh.layoutParams = mlpSwipe
binding.swipeRefresh.updateMargins(left = left, right = right)
binding.noticeText.updatePadding(bottom = spacingNavigation)

View File

@ -34,7 +34,6 @@ import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
@ -177,9 +176,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
// Dismiss previous notifications (should not happen unless a crash occurred)
EmulationActivity.stopForegroundService(this)
setInsets()
}
@ -298,11 +294,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
super.onResume()
}
override fun onDestroy() {
EmulationActivity.stopForegroundService(this)
super.onDestroy()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root

View File

@ -1,70 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity
/**
* A service that shows a permanent notification in the background to avoid the app getting
* cleared from memory by the system.
*/
class ForegroundService : Service() {
companion object {
const val EMULATION_RUNNING_NOTIFICATION = 0x1000
const val ACTION_STOP = "stop"
}
private fun showRunningNotification() {
// Intent is used to resume emulation if the notification is clicked
val contentIntent = PendingIntent.getActivity(
this,
0,
Intent(this, EmulationActivity::class.java),
PendingIntent.FLAG_IMMUTABLE
)
val builder =
NotificationCompat.Builder(this, getString(R.string.emulation_notification_channel_id))
.setSmallIcon(R.drawable.ic_stat_notification_logo)
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.emulation_notification_running))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.setVibrate(null)
.setSound(null)
.setContentIntent(contentIntent)
startForeground(EMULATION_RUNNING_NOTIFICATION, builder.build())
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onCreate() {
showRunningNotification()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) {
return START_NOT_STICKY
}
if (intent.action == ACTION_STOP) {
NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelfResult(startId)
}
return START_STICKY
}
override fun onDestroy() {
NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
}
}

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.utils
import android.view.View
import android.view.ViewGroup
object ViewUtils {
fun showView(view: View, length: Long = 300) {
@ -32,4 +33,28 @@ object ViewUtils {
view.visibility = View.INVISIBLE
}.start()
}
fun View.updateMargins(
left: Int = -1,
top: Int = -1,
right: Int = -1,
bottom: Int = -1
) {
val layoutParams = this.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.apply {
if (left != -1) {
leftMargin = left
}
if (top != -1) {
topMargin = top
}
if (right != -1) {
rightMargin = right
}
if (bottom != -1) {
bottomMargin = bottom
}
}
this.layoutParams = layoutParams
}
}

View File

@ -60,6 +60,8 @@ struct Values {
Settings::Category::Overlay};
Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> show_thermal_overlay{linkage, false, "show_thermal_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};

View File

@ -140,6 +140,7 @@
android:id="@+id/overlay_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginHorizontal="20dp"
android:fitsSystemWindows="true">
<com.google.android.material.textview.MaterialTextView
@ -150,7 +151,19 @@
android:layout_gravity="left"
android:clickable="false"
android:focusable="false"
android:paddingHorizontal="20dp"
android:textColor="@android:color/white"
android:shadowColor="@android:color/black"
android:shadowRadius="3"
tools:ignore="RtlHardcoded" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/show_thermals_text"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:clickable="false"
android:focusable="false"
android:textColor="@android:color/white"
android:shadowColor="@android:color/black"
android:shadowRadius="3"

View File

@ -6,6 +6,11 @@
android:title="@string/emulation_fps_counter"
android:checkable="true" />
<item
android:id="@+id/thermal_indicator"
android:title="@string/emulation_thermal_indicator"
android:checkable="true" />
<item
android:id="@+id/menu_edit_overlay"
android:title="@string/emulation_touch_overlay_edit" />

View File

@ -1,9 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="emulation_notification_channel_name">المحاكي نشط</string>
<string name="emulation_notification_channel_description">اظهار اشعار دائم عندما يكون المحاكي نشطاً</string>
<string name="emulation_notification_running">يوزو قيد التشغيل</string>
<string name="notice_notification_channel_name">الإشعارات والأخطاء</string>
<string name="notice_notification_channel_description">اظهار اشعار عند حصول اي مشكلة.</string>
<string name="notification_permission_not_granted">لم يتم منح إذن الإشعار</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">ئیمولەیشن کارایە</string>
<string name="emulation_notification_channel_description">ئاگادارکردنەوەیەکی بەردەوام نیشان دەدات کاتێک ئیمولەیشن کاردەکات.</string>
<string name="emulation_notification_running">یوزو کاردەکات</string>
<string name="notice_notification_channel_name">ئاگاداری و هەڵەکان</string>
<string name="notice_notification_channel_description">ئاگادارکردنەوەکان پیشان دەدات کاتێک شتێک بە هەڵەدا دەچێت.</string>
<string name="notification_permission_not_granted">مۆڵەتی ئاگادارکردنەوە نەدراوە!</string>

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="emulation_notification_channel_name">Emulace je aktivní</string>
<string name="notice_notification_channel_name">Upozornění a chyby</string>
<string name="notice_notification_channel_description">Ukáže oznámení v případě chyby.</string>
<string name="notification_permission_not_granted">Oznámení nejsou oprávněna!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">Emulation ist aktiv</string>
<string name="emulation_notification_channel_description">Zeigt eine dauerhafte Benachrichtigung an, wenn die Emulation läuft.</string>
<string name="emulation_notification_running">yuzu läuft</string>
<string name="notice_notification_channel_name">Hinweise und Fehler</string>
<string name="notice_notification_channel_description">Zeigt Benachrichtigungen an, wenn etwas schief läuft.</string>
<string name="notification_permission_not_granted">Berechtigung für Benachrichtigungen nicht erlaubt!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">Emulación activa</string>
<string name="emulation_notification_channel_description">Muestra una notificación persistente cuando la emulación está activa.</string>
<string name="emulation_notification_running">yuzu está ejecutándose</string>
<string name="notice_notification_channel_name">Avisos y errores</string>
<string name="notice_notification_channel_description">Mostrar notificaciones cuándo algo vaya mal.</string>
<string name="notification_permission_not_granted">¡Permisos de notificación no concedidos!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">L\'émulation est active</string>
<string name="emulation_notification_channel_description">Affiche une notification persistante lorsque l\'émulation est en cours d\'exécution.</string>
<string name="emulation_notification_running">yuzu est en cours d\'exécution</string>
<string name="notice_notification_channel_name">Avis et erreurs</string>
<string name="notice_notification_channel_description">Affiche des notifications en cas de problème.</string>
<string name="notification_permission_not_granted">Permission de notification non accordée !</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">אמולציה פעילה</string>
<string name="emulation_notification_channel_description">מציג התראה מתמשכת כאשר האמולציה פועלת.</string>
<string name="emulation_notification_running">yuzu רץ</string>
<string name="notice_notification_channel_name">התראות ותקלות</string>
<string name="notice_notification_channel_description">מציג התראות כאשר משהו הולך לא כשורה.</string>
<string name="notification_permission_not_granted">הרשאות התראות לא ניתנה!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">Emuláció aktív</string>
<string name="emulation_notification_channel_description">Állandó értesítést jelenít meg, amíg az emuláció fut.</string>
<string name="emulation_notification_running">A yuzu fut</string>
<string name="notice_notification_channel_name">Megjegyzések és hibák</string>
<string name="notice_notification_channel_description">Értesítések megjelenítése, ha valami rosszul sül el.</string>
<string name="notification_permission_not_granted">Nincs engedély az értesítés megjelenítéséhez!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">L\'emulatore è attivo</string>
<string name="emulation_notification_channel_description">Mostra una notifica persistente quando l\'emulatore è in esecuzione.</string>
<string name="emulation_notification_running">yuzu è in esecuzione</string>
<string name="notice_notification_channel_name">Avvisi ed errori</string>
<string name="notice_notification_channel_description">Mostra le notifiche quando qualcosa va storto.</string>
<string name="notification_permission_not_granted">Autorizzazione di notifica non concessa!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">エミュレーションが有効です</string>
<string name="emulation_notification_channel_description">エミュレーションの実行中に常設通知を表示します。</string>
<string name="emulation_notification_running">yuzu は実行中です</string>
<string name="notice_notification_channel_name">通知とエラー</string>
<string name="notice_notification_channel_description">問題の発生時に通知を表示します。</string>
<string name="notification_permission_not_granted">通知が許可されていません!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">에뮬레이션이 활성화됨</string>
<string name="emulation_notification_channel_description">에뮬레이션이 실행 중일 때 지속적으로 알림을 표시합니다.</string>
<string name="emulation_notification_running">yuzu가 실행 중입니다.</string>
<string name="notice_notification_channel_name">알림 및 오류</string>
<string name="notice_notification_channel_description">문제가 발생하면 알림을 표시합니다.</string>
<string name="notification_permission_not_granted">알림 권한이 부여되지 않았습니다!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">Emulering er aktiv</string>
<string name="emulation_notification_channel_description">Viser et vedvarende varsel når emuleringen kjører.</string>
<string name="emulation_notification_running">Yuzu kjører</string>
<string name="notice_notification_channel_name">Merknader og feil</string>
<string name="notice_notification_channel_description">Viser varsler når noe går galt.</string>
<string name="notification_permission_not_granted">Varslingstillatelse ikke gitt!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">Emulacja jest uruchomiona</string>
<string name="emulation_notification_channel_description">Pokaż trwałe powiadomienie gdy emulacja jest uruchomiona.</string>
<string name="emulation_notification_running">yuzu jest uruchomiony</string>
<string name="notice_notification_channel_name">Powiadomienia błędy</string>
<string name="notice_notification_channel_description">Pokaż powiadomienie gdy coś pójdzie źle</string>
<string name="notification_permission_not_granted">Nie zezwolono na powiadomienia!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">A emulação está Ativa</string>
<string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação estiver em andamento.</string>
<string name="emulation_notification_running">O Yuzu está em execução </string>
<string name="notice_notification_channel_name">Notificações e erros</string>
<string name="notice_notification_channel_description">Mostra notificações quando algo dá errado.</string>
<string name="notification_permission_not_granted">Acesso às notificações não concedido!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">Emulação está Ativa</string>
<string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação está a correr.</string>
<string name="emulation_notification_running">Yuzu está em execução </string>
<string name="notice_notification_channel_name">Notificações e erros</string>
<string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string>
<string name="notification_permission_not_granted">Permissões de notificação não permitidas </string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">Эмуляция активна</string>
<string name="emulation_notification_channel_description">Показывает постоянное уведомление, когда запущена эмуляция.</string>
<string name="emulation_notification_running">yuzu запущен</string>
<string name="notice_notification_channel_name">Уведомления и ошибки</string>
<string name="notice_notification_channel_description">Показывать уведомления, когда что-то пошло не так</string>
<string name="notification_permission_not_granted">Вы не предоставили разрешение на уведомления!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">Емуляція активна</string>
<string name="emulation_notification_channel_description">Показує постійне сповіщення, коли запущено емуляцію.</string>
<string name="emulation_notification_running">yuzu запущено</string>
<string name="notice_notification_channel_name">Сповіщення та помилки</string>
<string name="notice_notification_channel_description">Показувати сповіщення, коли щось пішло не так</string>
<string name="notification_permission_not_granted">Ви не надали дозвіл сповіщень!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">Giả lập đang chạy</string>
<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>
<string name="emulation_notification_running">yuzu đang chạy</string>
<string name="notice_notification_channel_name">Thông báo và lỗi</string>
<string name="notice_notification_channel_description">Hiển thị thông báo khi có sự cố xảy ra.</string>
<string name="notification_permission_not_granted">Ứng dụng không được cấp quyền thông báo!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">正在进行模拟</string>
<string name="emulation_notification_channel_description">在模拟运行时显示持久通知。</string>
<string name="emulation_notification_running">yuzu 正在运行</string>
<string name="notice_notification_channel_name">通知及错误提醒</string>
<string name="notice_notification_channel_description">当发生错误时显示通知。</string>
<string name="notification_permission_not_granted">未授予通知权限!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<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>
<string name="emulation_notification_channel_name">模擬進行中</string>
<string name="emulation_notification_channel_description">在模擬執行時顯示持續通知。</string>
<string name="emulation_notification_running">yuzu 正在執行</string>
<string name="notice_notification_channel_name">通知和錯誤</string>
<string name="notice_notification_channel_description">發生錯誤時顯示通知。</string>
<string name="notification_permission_not_granted">未授予通知權限!</string>

View File

@ -4,10 +4,6 @@
<!-- General application strings -->
<string name="app_name" translatable="false">yuzu</string>
<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>
<string name="emulation_notification_channel_name">Emulation is Active</string>
<string name="emulation_notification_channel_id" translatable="false">emulationIsActive</string>
<string name="emulation_notification_channel_description">Shows a persistent notification when emulation is running.</string>
<string name="emulation_notification_running">yuzu is running</string>
<string name="notice_notification_channel_name">Notices and errors</string>
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
@ -380,6 +376,7 @@
<string name="emulation_exit">Exit emulation</string>
<string name="emulation_done">Done</string>
<string name="emulation_fps_counter">FPS counter</string>
<string name="emulation_thermal_indicator">Thermal indicator</string>
<string name="emulation_toggle_controls">Toggle controls</string>
<string name="emulation_rel_stick_center">Relative stick center</string>
<string name="emulation_dpad_slide">D-pad slide</string>

View File

@ -8,8 +8,8 @@
#include <jni.h>
#include "common/android/id_cache.h"
#include "common/string_util.h"
#include "id_cache.h"
namespace Common::Android {

View File

@ -4,9 +4,9 @@
#include <jni.h>
#include "applets/software_keyboard.h"
#include "common/android/id_cache.h"
#include "common/assert.h"
#include "common/fs/fs_android.h"
#include "id_cache.h"
#include "video_core/rasterizer_interface.h"
static JavaVM* s_java_vm;

View File

@ -44,7 +44,7 @@ bool IsContentUri(const std::string& path) {
return path.find(prefix) == 0;
}
int OpenContentUri(const std::string& filepath, OpenMode openmode) {
s32 OpenContentUri(const std::string& filepath, OpenMode openmode) {
if (s_open_content_uri == nullptr)
return -1;
@ -63,7 +63,7 @@ int OpenContentUri(const std::string& filepath, OpenMode openmode) {
return env->CallStaticIntMethod(native_library, s_open_content_uri, j_filepath, j_mode);
}
std::uint64_t GetSize(const std::string& filepath) {
u64 GetSize(const std::string& filepath) {
if (s_get_size == nullptr) {
return 0;
}

View File

@ -512,10 +512,35 @@ add_library(core STATIC
hle/service/audio/hwopus.h
hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h
hle/service/bcat/news/newly_arrived_event_holder.cpp
hle/service/bcat/news/newly_arrived_event_holder.h
hle/service/bcat/news/news_data_service.cpp
hle/service/bcat/news/news_data_service.h
hle/service/bcat/news/news_database_service.cpp
hle/service/bcat/news/news_database_service.h
hle/service/bcat/news/news_service.cpp
hle/service/bcat/news/news_service.h
hle/service/bcat/news/overwrite_event_holder.cpp
hle/service/bcat/news/overwrite_event_holder.h
hle/service/bcat/news/service_creator.cpp
hle/service/bcat/news/service_creator.h
hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h
hle/service/bcat/bcat_module.cpp
hle/service/bcat/bcat_module.h
hle/service/bcat/bcat_result.h
hle/service/bcat/bcat_service.cpp
hle/service/bcat/bcat_service.h
hle/service/bcat/bcat_types.h
hle/service/bcat/bcat_util.h
hle/service/bcat/delivery_cache_directory_service.cpp
hle/service/bcat/delivery_cache_directory_service.h
hle/service/bcat/delivery_cache_file_service.cpp
hle/service/bcat/delivery_cache_file_service.h
hle/service/bcat/delivery_cache_progress_service.cpp
hle/service/bcat/delivery_cache_progress_service.h
hle/service/bcat/delivery_cache_storage_service.cpp
hle/service/bcat/delivery_cache_storage_service.h
hle/service/bcat/service_creator.cpp
hle/service/bcat/service_creator.h
hle/service/bpc/bpc.cpp
hle/service/bpc/bpc.h
hle/service/btdrv/btdrv.cpp
@ -548,8 +573,6 @@ add_library(core STATIC
hle/service/es/es.h
hle/service/eupld/eupld.cpp
hle/service/eupld/eupld.h
hle/service/event.cpp
hle/service/event.h
hle/service/fatal/fatal.cpp
hle/service/fatal/fatal.h
hle/service/fatal/fatal_p.cpp
@ -676,8 +699,6 @@ add_library(core STATIC
hle/service/mm/mm_u.h
hle/service/mnpp/mnpp_app.cpp
hle/service/mnpp/mnpp_app.h
hle/service/mutex.cpp
hle/service/mutex.h
hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h
hle/service/nfc/common/amiibo_crypto.cpp
@ -790,6 +811,15 @@ add_library(core STATIC
hle/service/nvnflinger/window.h
hle/service/olsc/olsc.cpp
hle/service/olsc/olsc.h
hle/service/os/event.cpp
hle/service/os/event.h
hle/service/os/multi_wait_holder.cpp
hle/service/os/multi_wait_holder.h
hle/service/os/multi_wait_utils.h
hle/service/os/multi_wait.cpp
hle/service/os/multi_wait.h
hle/service/os/mutex.cpp
hle/service/os/mutex.h
hle/service/pcie/pcie.cpp
hle/service/pcie/pcie.h
hle/service/pctl/pctl.cpp

View File

@ -172,6 +172,10 @@ u32 NCA::GetSDKVersion() const {
return reader->GetSdkAddonVersion();
}
u8 NCA::GetKeyGeneration() const {
return reader->GetKeyGeneration();
}
bool NCA::IsUpdate() const {
return is_update;
}

View File

@ -77,6 +77,7 @@ public:
u64 GetTitleId() const;
RightsId GetRightsId() const;
u32 GetSDKVersion() const;
u8 GetKeyGeneration() const;
bool IsUpdate() const;
VirtualFile GetRomFS() const;

View File

@ -91,6 +91,7 @@ constexpr Result ResultWriteNotPermitted{ErrorModule::FS, 6203};
constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325};
constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387};
constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388};
constexpr Result ResultPermissionDenied{ErrorModule::FS, 6400};
constexpr Result ResultBufferAllocationFailed{ErrorModule::FS, 6705};
} // namespace FileSys

View File

@ -9,8 +9,8 @@
#include "common/math_util.h"
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/os/event.h"
#include "core/hle/service/service.h"
#include "core/hle/service/am/am_types.h"

View File

@ -7,8 +7,8 @@
#include <memory>
#include <mutex>
#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/os/event.h"
union Result;

View File

@ -102,8 +102,16 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
return {};
}
// TODO: enable other versions of applets
enum : u8 {
Firmware1400 = 14,
Firmware1500 = 15,
Firmware1600 = 16,
Firmware1700 = 17,
};
auto process = std::make_unique<Process>(system);
if (!process->Initialize(program_id)) {
if (!process->Initialize(program_id, Firmware1400, Firmware1700)) {
// Couldn't initialize the guest process
return {};
}

View File

@ -3,6 +3,7 @@
#include "common/scope_exit.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/kernel/k_process.h"
@ -20,7 +21,7 @@ Process::~Process() {
this->Finalize();
}
bool Process::Initialize(u64 program_id) {
bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
// First, ensure we are not holding another process.
this->Finalize();
@ -29,21 +30,33 @@ bool Process::Initialize(u64 program_id) {
// Attempt to load program NCA.
const FileSys::RegisteredCache* bis_system{};
FileSys::VirtualFile nca{};
FileSys::VirtualFile nca_raw{};
// Get the program NCA from built-in storage.
bis_system = fsc.GetSystemNANDContents();
if (bis_system) {
nca = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
nca_raw = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
}
// Ensure we retrieved a program NCA.
if (!nca) {
if (!nca_raw) {
return false;
}
// Ensure we have a suitable version.
if (minimum_key_generation > 0) {
FileSys::NCA nca(nca_raw);
if (nca.GetStatus() == Loader::ResultStatus::Success &&
(nca.GetKeyGeneration() < minimum_key_generation ||
nca.GetKeyGeneration() > maximum_key_generation)) {
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id,
nca.GetKeyGeneration());
return false;
}
}
// Get the appropriate loader to parse this NCA.
auto app_loader = Loader::GetLoader(m_system, nca, program_id, 0);
auto app_loader = Loader::GetLoader(m_system, nca_raw, program_id, 0);
// Ensure we have a loader which can parse the NCA.
if (!app_loader) {

View File

@ -21,7 +21,7 @@ public:
explicit Process(Core::System& system);
~Process();
bool Initialize(u64 program_id);
bool Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
void Finalize();
bool Run();

View File

@ -33,18 +33,18 @@ void ProgressServiceBackend::SetTotalSize(u64 size) {
}
void ProgressServiceBackend::StartConnecting() {
impl.status = DeliveryCacheProgressImpl::Status::Connecting;
impl.status = DeliveryCacheProgressStatus::Connecting;
SignalUpdate();
}
void ProgressServiceBackend::StartProcessingDataList() {
impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList;
impl.status = DeliveryCacheProgressStatus::ProcessingDataList;
SignalUpdate();
}
void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name,
std::string_view file_name, u64 file_size) {
impl.status = DeliveryCacheProgressImpl::Status::Downloading;
impl.status = DeliveryCacheProgressStatus::Downloading;
impl.current_downloaded_bytes = 0;
impl.current_total_bytes = file_size;
std::memcpy(impl.current_directory.data(), dir_name.data(),
@ -65,7 +65,7 @@ void ProgressServiceBackend::FinishDownloadingFile() {
}
void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
impl.status = DeliveryCacheProgressImpl::Status::Committing;
impl.status = DeliveryCacheProgressStatus::Committing;
impl.current_file.fill(0);
impl.current_downloaded_bytes = 0;
impl.current_total_bytes = 0;
@ -76,7 +76,7 @@ void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
void ProgressServiceBackend::FinishDownload(Result result) {
impl.total_downloaded_bytes = impl.total_bytes;
impl.status = DeliveryCacheProgressImpl::Status::Done;
impl.status = DeliveryCacheProgressStatus::Done;
impl.result = result;
SignalUpdate();
}
@ -85,15 +85,15 @@ void ProgressServiceBackend::SignalUpdate() {
update_event->Signal();
}
Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
BcatBackend::BcatBackend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
Backend::~Backend() = default;
BcatBackend::~BcatBackend() = default;
NullBackend::NullBackend(DirectoryGetter getter) : Backend(std::move(getter)) {}
NullBcatBackend::NullBcatBackend(DirectoryGetter getter) : BcatBackend(std::move(getter)) {}
NullBackend::~NullBackend() = default;
NullBcatBackend::~NullBcatBackend() = default;
bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
bool NullBcatBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
title.build_id);
@ -101,8 +101,8 @@ bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& prog
return true;
}
bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
ProgressServiceBackend& progress) {
bool NullBcatBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
ProgressServiceBackend& progress) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id,
title.build_id, name);
@ -110,18 +110,18 @@ bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
return true;
}
bool NullBackend::Clear(u64 title_id) {
bool NullBcatBackend::Clear(u64 title_id) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
return true;
}
void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
void NullBcatBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
Common::HexToString(passphrase));
}
std::optional<std::vector<u8>> NullBackend::GetLaunchParameter(TitleIDVersion title) {
std::optional<std::vector<u8>> NullBcatBackend::GetLaunchParameter(TitleIDVersion title) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
title.build_id);
return std::nullopt;

View File

@ -10,6 +10,7 @@
#include "common/common_types.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
#include "core/hle/service/bcat/bcat_types.h"
#include "core/hle/service/kernel_helpers.h"
namespace Core {
@ -24,44 +25,6 @@ class KReadableEvent;
namespace Service::BCAT {
struct DeliveryCacheProgressImpl;
using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
using Passphrase = std::array<u8, 0x20>;
struct TitleIDVersion {
u64 title_id;
u64 build_id;
};
using DirectoryName = std::array<char, 0x20>;
using FileName = std::array<char, 0x20>;
struct DeliveryCacheProgressImpl {
enum class Status : s32 {
None = 0x0,
Queued = 0x1,
Connecting = 0x2,
ProcessingDataList = 0x3,
Downloading = 0x4,
Committing = 0x5,
Done = 0x9,
};
Status status;
Result result = ResultSuccess;
DirectoryName current_directory;
FileName current_file;
s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
s64 current_total_bytes; ///< Bytes total on current file.
s64 total_downloaded_bytes; ///< Bytes downloaded on overall download.
s64 total_bytes; ///< Bytes total on overall download.
INSERT_PADDING_BYTES(
0x198); ///< Appears to be unused in official code, possibly reserved for future use.
};
static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200,
"DeliveryCacheProgressImpl has incorrect size.");
// A class to manage the signalling to the game about BCAT download progress.
// Some of this class is implemented in module.cpp to avoid exposing the implementation structure.
class ProgressServiceBackend {
@ -107,10 +70,10 @@ private:
};
// A class representing an abstract backend for BCAT functionality.
class Backend {
class BcatBackend {
public:
explicit Backend(DirectoryGetter getter);
virtual ~Backend();
explicit BcatBackend(DirectoryGetter getter);
virtual ~BcatBackend();
// Called when the backend is needed to synchronize the data for the game with title ID and
// version in title. A ProgressServiceBackend object is provided to alert the application of
@ -135,10 +98,10 @@ protected:
};
// A backend of BCAT that provides no operation.
class NullBackend : public Backend {
class NullBcatBackend : public BcatBackend {
public:
explicit NullBackend(DirectoryGetter getter);
~NullBackend() override;
explicit NullBcatBackend(DirectoryGetter getter);
~NullBcatBackend() override;
bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
bool SynchronizeDirectory(TitleIDVersion title, std::string name,
@ -151,6 +114,7 @@ public:
std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
};
std::unique_ptr<Backend> CreateBackendFromSettings(Core::System& system, DirectoryGetter getter);
std::unique_ptr<BcatBackend> CreateBackendFromSettings(Core::System& system,
DirectoryGetter getter);
} // namespace Service::BCAT

View File

@ -1,24 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/bcat/news/service_creator.h"
#include "core/hle/service/bcat/service_creator.h"
#include "core/hle/service/server_manager.h"
namespace Service::BCAT {
BCAT::BCAT(Core::System& system_, std::shared_ptr<Module> module_,
FileSystem::FileSystemController& fsc_, const char* name_)
: Interface(system_, std::move(module_), fsc_, name_) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BCAT::CreateBcatService, "CreateBcatService"},
{1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"},
{2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"},
{3, nullptr, "CreateDeliveryCacheProgressService"},
{4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"},
};
// clang-format on
RegisterHandlers(functions);
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("bcat:a",
std::make_shared<IServiceCreator>(system, "bcat:a"));
server_manager->RegisterNamedService("bcat:m",
std::make_shared<IServiceCreator>(system, "bcat:m"));
server_manager->RegisterNamedService("bcat:u",
std::make_shared<IServiceCreator>(system, "bcat:u"));
server_manager->RegisterNamedService("bcat:s",
std::make_shared<IServiceCreator>(system, "bcat:s"));
server_manager->RegisterNamedService(
"news:a", std::make_shared<News::IServiceCreator>(system, 0xffffffff, "news:a"));
server_manager->RegisterNamedService(
"news:p", std::make_shared<News::IServiceCreator>(system, 0x1, "news:p"));
server_manager->RegisterNamedService(
"news:c", std::make_shared<News::IServiceCreator>(system, 0x2, "news:c"));
server_manager->RegisterNamedService(
"news:v", std::make_shared<News::IServiceCreator>(system, 0x4, "news:v"));
server_manager->RegisterNamedService(
"news:m", std::make_shared<News::IServiceCreator>(system, 0xd, "news:m"));
ServerManager::RunServer(std::move(server_manager));
}
BCAT::~BCAT() = default;
} // namespace Service::BCAT

View File

@ -3,7 +3,7 @@
#pragma once
#include "core/hle/service/bcat/bcat_module.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
@ -11,11 +11,6 @@ class System;
namespace Service::BCAT {
class BCAT final : public Module::Interface {
public:
explicit BCAT(Core::System& system_, std::shared_ptr<Module> module_,
FileSystem::FileSystemController& fsc_, const char* name_);
~BCAT() override;
};
void LoopProcess(Core::System& system);
} // namespace Service::BCAT

View File

@ -1,606 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cctype>
#include <mbedtls/md5.h>
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/bcat/bcat_module.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h"
namespace Service::BCAT {
constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
constexpr Result ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
constexpr Result ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
constexpr Result ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7};
// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files
// and if any of them have a non-zero result it just forwards that result. This is the FS error code
// for permission denied, which is the closest approximation of this scenario.
constexpr Result ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400};
using BCATDigest = std::array<u8, 0x10>;
namespace {
u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
u64 out{};
std::memcpy(&out, id.data(), sizeof(u64));
return out;
}
// The digest is only used to determine if a file is unique compared to others of the same name.
// Since the algorithm isn't ever checked in game, MD5 is safe.
BCATDigest DigestFile(const FileSys::VirtualFile& file) {
BCATDigest out{};
const auto bytes = file->ReadAllBytes();
mbedtls_md5_ret(bytes.data(), bytes.size(), out.data());
return out;
}
// For a name to be valid it must be non-empty, must have a null terminating character as the final
// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if
// file.
bool VerifyNameValidInternal(HLERequestContext& ctx, std::array<char, 0x20> name, char match_char) {
const auto null_chars = std::count(name.begin(), name.end(), 0);
const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) {
return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0';
});
if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') {
LOG_ERROR(Service_BCAT, "Name passed was invalid!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
return false;
}
return true;
}
bool VerifyNameValidDir(HLERequestContext& ctx, DirectoryName name) {
return VerifyNameValidInternal(ctx, name, '-');
}
bool VerifyNameValidFile(HLERequestContext& ctx, FileName name) {
return VerifyNameValidInternal(ctx, name, '.');
}
} // Anonymous namespace
struct DeliveryCacheDirectoryEntry {
FileName name;
u64 size;
BCATDigest digest;
};
class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
public:
explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_,
const DeliveryCacheProgressImpl& impl_)
: ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"},
{1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetEvent(HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(event);
}
void GetImpl(HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
ctx.WriteBuffer(impl);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
Kernel::KReadableEvent& event;
const DeliveryCacheProgressImpl& impl;
};
class IBcatService final : public ServiceFramework<IBcatService> {
public:
explicit IBcatService(Core::System& system_, Backend& backend_)
: ServiceFramework{system_, "IBcatService"}, backend{backend_},
progress{{
ProgressServiceBackend{system_, "Normal"},
ProgressServiceBackend{system_, "Directory"},
}} {
// clang-format off
static const FunctionInfo functions[] = {
{10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"},
{10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"},
{10200, nullptr, "CancelSyncDeliveryCacheRequest"},
{20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
{20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
{20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"},
{20301, nullptr, "RequestSuspendDeliveryTask"},
{20400, nullptr, "RegisterSystemApplicationDeliveryTask"},
{20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
{20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
{30100, &IBcatService::SetPassphrase, "SetPassphrase"},
{30101, nullptr, "Unknown30101"},
{30102, nullptr, "Unknown30102"},
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
{30203, nullptr, "UnblockDeliveryTask"},
{30210, nullptr, "SetDeliveryTaskTimer"},
{30300, nullptr, "RegisterSystemApplicationDeliveryTasks"},
{90100, nullptr, "EnumerateBackgroundDeliveryTask"},
{90101, nullptr, "Unknown90101"},
{90200, nullptr, "GetDeliveryList"},
{90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"},
{90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"},
{90300, nullptr, "GetPushNotificationLog"},
{90301, nullptr, "Unknown90301"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
enum class SyncType {
Normal,
Directory,
Count,
};
std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) {
auto& progress_backend{GetProgressBackend(type)};
return std::make_shared<IDeliveryCacheProgressService>(system, progress_backend.GetEvent(),
progress_backend.GetImpl());
}
void RequestSyncDeliveryCache(HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
backend.Synchronize({system.GetApplicationProcessProgramID(),
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
GetProgressBackend(SyncType::Normal));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface(CreateProgressService(SyncType::Normal));
}
void RequestSyncDeliveryCacheWithDirectoryName(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto name_raw = rp.PopRaw<DirectoryName>();
const auto name =
Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
LOG_DEBUG(Service_BCAT, "called, name={}", name);
backend.SynchronizeDirectory({system.GetApplicationProcessProgramID(),
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
name, GetProgressBackend(SyncType::Directory));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface(CreateProgressService(SyncType::Directory));
}
void SetPassphrase(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
const auto passphrase_raw = ctx.ReadBuffer();
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
Common::HexToString(passphrase_raw));
if (title_id == 0) {
LOG_ERROR(Service_BCAT, "Invalid title ID!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
}
if (passphrase_raw.size() > 0x40) {
LOG_ERROR(Service_BCAT, "Passphrase too large!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
return;
}
Passphrase passphrase{};
std::memcpy(passphrase.data(), passphrase_raw.data(),
std::min(passphrase.size(), passphrase_raw.size()));
backend.SetPassphrase(title_id, passphrase);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ClearDeliveryCacheStorage(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
if (title_id == 0) {
LOG_ERROR(Service_BCAT, "Invalid title ID!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
return;
}
if (!backend.Clear(title_id)) {
LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_FAILED_CLEAR_CACHE);
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
ProgressServiceBackend& GetProgressBackend(SyncType type) {
return progress.at(static_cast<size_t>(type));
}
const ProgressServiceBackend& GetProgressBackend(SyncType type) const {
return progress.at(static_cast<size_t>(type));
}
Backend& backend;
std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
};
void Module::Interface::CreateBcatService(HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IBcatService>(system, *backend);
}
class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
public:
explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_)
: ServiceFramework{system_, "IDeliveryCacheFileService"}, root(std::move(root_)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IDeliveryCacheFileService::Open, "Open"},
{1, &IDeliveryCacheFileService::Read, "Read"},
{2, &IDeliveryCacheFileService::GetSize, "GetSize"},
{3, &IDeliveryCacheFileService::GetDigest, "GetDigest"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void Open(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto dir_name_raw = rp.PopRaw<DirectoryName>();
const auto file_name_raw = rp.PopRaw<FileName>();
const auto dir_name =
Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
const auto file_name =
Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size());
LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name);
if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw))
return;
if (current_file != nullptr) {
LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_ENTITY_ALREADY_OPEN);
return;
}
const auto dir = root->GetSubdirectory(dir_name);
if (dir == nullptr) {
LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_FAILED_OPEN_ENTITY);
return;
}
current_file = dir->GetFile(file_name);
if (current_file == nullptr) {
LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_FAILED_OPEN_ENTITY);
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Read(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto offset{rp.PopRaw<u64>()};
auto size = ctx.GetWriteBufferSize();
LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size);
if (current_file == nullptr) {
LOG_ERROR(Service_BCAT, "There is no file currently open!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NO_OPEN_ENTITY);
}
size = std::min<u64>(current_file->GetSize() - offset, size);
const auto buffer = current_file->ReadBytes(size, offset);
ctx.WriteBuffer(buffer);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(buffer.size());
}
void GetSize(HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
if (current_file == nullptr) {
LOG_ERROR(Service_BCAT, "There is no file currently open!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NO_OPEN_ENTITY);
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(current_file->GetSize());
}
void GetDigest(HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
if (current_file == nullptr) {
LOG_ERROR(Service_BCAT, "There is no file currently open!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NO_OPEN_ENTITY);
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(DigestFile(current_file));
}
FileSys::VirtualDir root;
FileSys::VirtualFile current_file;
};
class IDeliveryCacheDirectoryService final
: public ServiceFramework<IDeliveryCacheDirectoryService> {
public:
explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_)
: ServiceFramework{system_, "IDeliveryCacheDirectoryService"}, root(std::move(root_)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IDeliveryCacheDirectoryService::Open, "Open"},
{1, &IDeliveryCacheDirectoryService::Read, "Read"},
{2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void Open(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto name_raw = rp.PopRaw<DirectoryName>();
const auto name =
Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
LOG_DEBUG(Service_BCAT, "called, name={}", name);
if (!VerifyNameValidDir(ctx, name_raw))
return;
if (current_dir != nullptr) {
LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_ENTITY_ALREADY_OPEN);
return;
}
current_dir = root->GetSubdirectory(name);
if (current_dir == nullptr) {
LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_FAILED_OPEN_ENTITY);
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Read(HLERequestContext& ctx) {
auto write_size = ctx.GetWriteBufferNumElements<DeliveryCacheDirectoryEntry>();
LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size);
if (current_dir == nullptr) {
LOG_ERROR(Service_BCAT, "There is no open directory!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NO_OPEN_ENTITY);
return;
}
const auto files = current_dir->GetFiles();
write_size = std::min<u64>(write_size, files.size());
std::vector<DeliveryCacheDirectoryEntry> entries(write_size);
std::transform(
files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) {
FileName name{};
std::memcpy(name.data(), file->GetName().data(),
std::min(file->GetName().size(), name.size()));
return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)};
});
ctx.WriteBuffer(entries);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry)));
}
void GetCount(HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
if (current_dir == nullptr) {
LOG_ERROR(Service_BCAT, "There is no open directory!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NO_OPEN_ENTITY);
return;
}
const auto files = current_dir->GetFiles();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(files.size()));
}
FileSys::VirtualDir root;
FileSys::VirtualDir current_dir;
};
class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
public:
explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_)
: ServiceFramework{system_, "IDeliveryCacheStorageService"}, root(std::move(root_)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"},
{1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"},
{10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"},
};
// clang-format on
RegisterHandlers(functions);
for (const auto& subdir : root->GetSubdirectories()) {
DirectoryName name{};
std::memcpy(name.data(), subdir->GetName().data(),
std::min(sizeof(DirectoryName) - 1, subdir->GetName().size()));
entries.push_back(name);
}
}
private:
void CreateFileService(HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDeliveryCacheFileService>(system, root);
}
void CreateDirectoryService(HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDeliveryCacheDirectoryService>(system, root);
}
void EnumerateDeliveryCacheDirectory(HLERequestContext& ctx) {
auto size = ctx.GetWriteBufferNumElements<DirectoryName>();
LOG_DEBUG(Service_BCAT, "called, size={:016X}", size);
size = std::min<u64>(size, entries.size() - next_read_index);
ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName));
next_read_index += size;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(size));
}
FileSys::VirtualDir root;
std::vector<DirectoryName> entries;
u64 next_read_index = 0;
};
void Module::Interface::CreateDeliveryCacheStorageService(HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
const auto title_id = system.GetApplicationProcessProgramID();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
}
void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
}
std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
DirectoryGetter getter) {
return std::make_unique<NullBackend>(std::move(getter));
}
Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
FileSystem::FileSystemController& fsc_, const char* name)
: ServiceFramework{system_, name}, fsc{fsc_}, module{std::move(module_)},
backend{CreateBackendFromSettings(system_,
[&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })} {}
Module::Interface::~Interface() = default;
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
auto module = std::make_shared<Module>();
server_manager->RegisterNamedService(
"bcat:a",
std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a"));
server_manager->RegisterNamedService(
"bcat:m",
std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m"));
server_manager->RegisterNamedService(
"bcat:u",
std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u"));
server_manager->RegisterNamedService(
"bcat:s",
std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:s"));
ServerManager::RunServer(std::move(server_manager));
}
} // namespace Service::BCAT

View File

@ -1,46 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service {
namespace FileSystem {
class FileSystemController;
} // namespace FileSystem
namespace BCAT {
class Backend;
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(Core::System& system_, std::shared_ptr<Module> module_,
FileSystem::FileSystemController& fsc_, const char* name);
~Interface() override;
void CreateBcatService(HLERequestContext& ctx);
void CreateDeliveryCacheStorageService(HLERequestContext& ctx);
void CreateDeliveryCacheStorageServiceWithApplicationId(HLERequestContext& ctx);
protected:
FileSystem::FileSystemController& fsc;
std::shared_ptr<Module> module;
std::unique_ptr<Backend> backend;
};
};
void LoopProcess(Core::System& system);
} // namespace BCAT
} // namespace Service

View File

@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::BCAT {
constexpr Result ResultInvalidArgument{ErrorModule::BCAT, 1};
constexpr Result ResultFailedOpenEntity{ErrorModule::BCAT, 2};
constexpr Result ResultEntityAlreadyOpen{ErrorModule::BCAT, 6};
constexpr Result ResultNoOpenEntry{ErrorModule::BCAT, 7};
} // namespace Service::BCAT

View File

@ -0,0 +1,132 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/hex_util.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/errors.h"
#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/bcat/bcat_result.h"
#include "core/hle/service/bcat/bcat_service.h"
#include "core/hle/service/bcat/bcat_util.h"
#include "core/hle/service/bcat/delivery_cache_progress_service.h"
#include "core/hle/service/bcat/delivery_cache_storage_service.h"
#include "core/hle/service/cmif_serialization.h"
namespace Service::BCAT {
static u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
u64 out{};
std::memcpy(&out, id.data(), sizeof(u64));
return out;
}
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_)
: ServiceFramework{system_, "IBcatService"}, backend{backend_},
progress{{
ProgressServiceBackend{system_, "Normal"},
ProgressServiceBackend{system_, "Directory"},
}} {
// clang-format off
static const FunctionInfo functions[] = {
{10100, D<&IBcatService::RequestSyncDeliveryCache>, "RequestSyncDeliveryCache"},
{10101, D<&IBcatService::RequestSyncDeliveryCacheWithDirectoryName>, "RequestSyncDeliveryCacheWithDirectoryName"},
{10200, nullptr, "CancelSyncDeliveryCacheRequest"},
{20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
{20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
{20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"},
{20301, nullptr, "RequestSuspendDeliveryTask"},
{20400, nullptr, "RegisterSystemApplicationDeliveryTask"},
{20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
{20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
{30100, D<&IBcatService::SetPassphrase>, "SetPassphrase"},
{30101, nullptr, "Unknown30101"},
{30102, nullptr, "Unknown30102"},
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
{30203, nullptr, "UnblockDeliveryTask"},
{30210, nullptr, "SetDeliveryTaskTimer"},
{30300, D<&IBcatService::RegisterSystemApplicationDeliveryTasks>, "RegisterSystemApplicationDeliveryTasks"},
{90100, nullptr, "EnumerateBackgroundDeliveryTask"},
{90101, nullptr, "Unknown90101"},
{90200, nullptr, "GetDeliveryList"},
{90201, D<&IBcatService::ClearDeliveryCacheStorage>, "ClearDeliveryCacheStorage"},
{90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"},
{90300, nullptr, "GetPushNotificationLog"},
{90301, nullptr, "Unknown90301"},
};
// clang-format on
RegisterHandlers(functions);
}
IBcatService::~IBcatService() = default;
Result IBcatService::RequestSyncDeliveryCache(
OutInterface<IDeliveryCacheProgressService> out_interface) {
LOG_DEBUG(Service_BCAT, "called");
auto& progress_backend{GetProgressBackend(SyncType::Normal)};
backend.Synchronize({system.GetApplicationProcessProgramID(),
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
GetProgressBackend(SyncType::Normal));
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
system, progress_backend.GetEvent(), progress_backend.GetImpl());
R_SUCCEED();
}
Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName(
const DirectoryName& name_raw, OutInterface<IDeliveryCacheProgressService> out_interface) {
const auto name = Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
LOG_DEBUG(Service_BCAT, "called, name={}", name);
auto& progress_backend{GetProgressBackend(SyncType::Directory)};
backend.SynchronizeDirectory({system.GetApplicationProcessProgramID(),
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
name, progress_backend);
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
system, progress_backend.GetEvent(), progress_backend.GetImpl());
R_SUCCEED();
}
Result IBcatService::SetPassphrase(u64 application_id,
InBuffer<BufferAttr_HipcPointer> passphrase_buffer) {
LOG_DEBUG(Service_BCAT, "called, application_id={:016X}, passphrase={}", application_id,
Common::HexToString(passphrase_buffer));
R_UNLESS(application_id != 0, ResultInvalidArgument);
R_UNLESS(passphrase_buffer.size() <= 0x40, ResultInvalidArgument);
Passphrase passphrase{};
std::memcpy(passphrase.data(), passphrase_buffer.data(),
std::min(passphrase.size(), passphrase_buffer.size()));
backend.SetPassphrase(application_id, passphrase);
R_SUCCEED();
}
Result IBcatService::RegisterSystemApplicationDeliveryTasks() {
LOG_WARNING(Service_BCAT, "(STUBBED) called");
R_SUCCEED();
}
Result IBcatService::ClearDeliveryCacheStorage(u64 application_id) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", application_id);
R_UNLESS(application_id != 0, ResultInvalidArgument);
R_UNLESS(backend.Clear(application_id), FileSys::ResultPermissionDenied);
R_SUCCEED();
}
ProgressServiceBackend& IBcatService::GetProgressBackend(SyncType type) {
return progress.at(static_cast<size_t>(type));
}
const ProgressServiceBackend& IBcatService::GetProgressBackend(SyncType type) const {
return progress.at(static_cast<size_t>(type));
}
} // namespace Service::BCAT

View File

@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/bcat/bcat_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::BCAT {
class BcatBackend;
class IDeliveryCacheStorageService;
class IDeliveryCacheProgressService;
class IBcatService final : public ServiceFramework<IBcatService> {
public:
explicit IBcatService(Core::System& system_, BcatBackend& backend_);
~IBcatService() override;
private:
Result RequestSyncDeliveryCache(OutInterface<IDeliveryCacheProgressService> out_interface);
Result RequestSyncDeliveryCacheWithDirectoryName(
const DirectoryName& name, OutInterface<IDeliveryCacheProgressService> out_interface);
Result SetPassphrase(u64 application_id, InBuffer<BufferAttr_HipcPointer> passphrase_buffer);
Result RegisterSystemApplicationDeliveryTasks();
Result ClearDeliveryCacheStorage(u64 application_id);
private:
ProgressServiceBackend& GetProgressBackend(SyncType type);
const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
BcatBackend& backend;
std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
};
} // namespace Service::BCAT

View File

@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <functional>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
namespace Service::BCAT {
using DirectoryName = std::array<char, 0x20>;
using FileName = std::array<char, 0x20>;
using BcatDigest = std::array<u8, 0x10>;
using Passphrase = std::array<u8, 0x20>;
using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
enum class SyncType {
Normal,
Directory,
Count,
};
enum class DeliveryCacheProgressStatus : s32 {
None = 0x0,
Queued = 0x1,
Connecting = 0x2,
ProcessingDataList = 0x3,
Downloading = 0x4,
Committing = 0x5,
Done = 0x9,
};
struct DeliveryCacheDirectoryEntry {
FileName name;
u64 size;
BcatDigest digest;
};
struct TitleIDVersion {
u64 title_id;
u64 build_id;
};
struct DeliveryCacheProgressImpl {
DeliveryCacheProgressStatus status;
Result result;
DirectoryName current_directory;
FileName current_file;
s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
s64 current_total_bytes; ///< Bytes total on current file.
s64 total_downloaded_bytes; ///< Bytes downloaded on overall download.
s64 total_bytes; ///< Bytes total on overall download.
INSERT_PADDING_BYTES_NOINIT(
0x198); ///< Appears to be unused in official code, possibly reserved for future use.
};
static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200,
"DeliveryCacheProgressImpl has incorrect size.");
static_assert(std::is_trivial_v<DeliveryCacheProgressImpl>,
"DeliveryCacheProgressImpl type must be trivially copyable.");
} // namespace Service::BCAT

View File

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <cctype>
#include <mbedtls/md5.h>
#include "core/hle/service/bcat/bcat_result.h"
#include "core/hle/service/bcat/bcat_types.h"
namespace Service::BCAT {
// For a name to be valid it must be non-empty, must have a null terminating character as the final
// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if
// file.
constexpr Result VerifyNameValidInternal(std::array<char, 0x20> name, char match_char) {
const auto null_chars = std::count(name.begin(), name.end(), 0);
const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) {
return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0';
});
if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') {
LOG_ERROR(Service_BCAT, "Name passed was invalid!");
return ResultInvalidArgument;
}
return ResultSuccess;
}
constexpr Result VerifyNameValidDir(DirectoryName name) {
return VerifyNameValidInternal(name, '-');
}
constexpr Result VerifyNameValidFile(FileName name) {
return VerifyNameValidInternal(name, '.');
}
} // namespace Service::BCAT

View File

@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/string_util.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/service/bcat/bcat_result.h"
#include "core/hle/service/bcat/bcat_util.h"
#include "core/hle/service/bcat/delivery_cache_directory_service.h"
#include "core/hle/service/cmif_serialization.h"
namespace Service::BCAT {
// The digest is only used to determine if a file is unique compared to others of the same name.
// Since the algorithm isn't ever checked in game, MD5 is safe.
static BcatDigest DigestFile(const FileSys::VirtualFile& file) {
BcatDigest out{};
const auto bytes = file->ReadAllBytes();
mbedtls_md5_ret(bytes.data(), bytes.size(), out.data());
return out;
}
IDeliveryCacheDirectoryService::IDeliveryCacheDirectoryService(Core::System& system_,
FileSys::VirtualDir root_)
: ServiceFramework{system_, "IDeliveryCacheDirectoryService"}, root(std::move(root_)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&IDeliveryCacheDirectoryService::Open>, "Open"},
{1, D<&IDeliveryCacheDirectoryService::Read>, "Read"},
{2, D<&IDeliveryCacheDirectoryService::GetCount>, "GetCount"},
};
// clang-format on
RegisterHandlers(functions);
}
IDeliveryCacheDirectoryService::~IDeliveryCacheDirectoryService() = default;
Result IDeliveryCacheDirectoryService::Open(const DirectoryName& dir_name_raw) {
const auto dir_name =
Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
LOG_DEBUG(Service_BCAT, "called, dir_name={}", dir_name);
R_TRY(VerifyNameValidDir(dir_name_raw));
R_UNLESS(current_dir == nullptr, ResultEntityAlreadyOpen);
const auto dir = root->GetSubdirectory(dir_name);
R_UNLESS(dir != nullptr, ResultFailedOpenEntity);
R_SUCCEED();
}
Result IDeliveryCacheDirectoryService::Read(
Out<s32> out_count, OutArray<DeliveryCacheDirectoryEntry, BufferAttr_HipcMapAlias> out_buffer) {
LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", out_buffer.size());
R_UNLESS(current_dir != nullptr, ResultNoOpenEntry);
const auto files = current_dir->GetFiles();
*out_count = static_cast<s32>(std::min(files.size(), out_buffer.size()));
std::transform(files.begin(), files.begin() + *out_count, out_buffer.begin(),
[](const auto& file) {
FileName name{};
std::memcpy(name.data(), file->GetName().data(),
std::min(file->GetName().size(), name.size()));
return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)};
});
R_SUCCEED();
}
Result IDeliveryCacheDirectoryService::GetCount(Out<s32> out_count) {
LOG_DEBUG(Service_BCAT, "called");
R_UNLESS(current_dir != nullptr, ResultNoOpenEntry);
*out_count = static_cast<s32>(current_dir->GetFiles().size());
R_SUCCEED();
}
} // namespace Service::BCAT

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/bcat/bcat_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::BCAT {
class IDeliveryCacheDirectoryService final
: public ServiceFramework<IDeliveryCacheDirectoryService> {
public:
explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_);
~IDeliveryCacheDirectoryService() override;
private:
Result Open(const DirectoryName& dir_name_raw);
Result Read(Out<s32> out_count,
OutArray<DeliveryCacheDirectoryEntry, BufferAttr_HipcMapAlias> out_buffer);
Result GetCount(Out<s32> out_count);
FileSys::VirtualDir root;
FileSys::VirtualDir current_dir;
};
} // namespace Service::BCAT

View File

@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/string_util.h"
#include "core/hle/service/bcat/bcat_result.h"
#include "core/hle/service/bcat/bcat_util.h"
#include "core/hle/service/bcat/delivery_cache_file_service.h"
#include "core/hle/service/cmif_serialization.h"
namespace Service::BCAT {
IDeliveryCacheFileService::IDeliveryCacheFileService(Core::System& system_,
FileSys::VirtualDir root_)
: ServiceFramework{system_, "IDeliveryCacheFileService"}, root(std::move(root_)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&IDeliveryCacheFileService::Open>, "Open"},
{1, D<&IDeliveryCacheFileService::Read>, "Read"},
{2, D<&IDeliveryCacheFileService::GetSize>, "GetSize"},
{3, D<&IDeliveryCacheFileService::GetDigest>, "GetDigest"},
};
// clang-format on
RegisterHandlers(functions);
}
IDeliveryCacheFileService::~IDeliveryCacheFileService() = default;
Result IDeliveryCacheFileService::Open(const DirectoryName& dir_name_raw,
const FileName& file_name_raw) {
const auto dir_name =
Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
const auto file_name =
Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size());
LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name);
R_TRY(VerifyNameValidDir(dir_name_raw));
R_TRY(VerifyNameValidDir(file_name_raw));
R_UNLESS(current_file == nullptr, ResultEntityAlreadyOpen);
const auto dir = root->GetSubdirectory(dir_name);
R_UNLESS(dir != nullptr, ResultFailedOpenEntity);
current_file = dir->GetFile(file_name);
R_UNLESS(current_file != nullptr, ResultFailedOpenEntity);
R_SUCCEED();
}
Result IDeliveryCacheFileService::Read(Out<u64> out_buffer_size, u64 offset,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer) {
LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, out_buffer.size());
R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
*out_buffer_size = std::min<u64>(current_file->GetSize() - offset, out_buffer.size());
const auto buffer = current_file->ReadBytes(*out_buffer_size, offset);
memcpy(out_buffer.data(), buffer.data(), buffer.size());
R_SUCCEED();
}
Result IDeliveryCacheFileService::GetSize(Out<u64> out_size) {
LOG_DEBUG(Service_BCAT, "called");
R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
*out_size = current_file->GetSize();
R_SUCCEED();
}
Result IDeliveryCacheFileService::GetDigest(Out<BcatDigest> out_digest) {
LOG_DEBUG(Service_BCAT, "called");
R_UNLESS(current_file != nullptr, ResultNoOpenEntry);
//*out_digest = DigestFile(current_file);
R_SUCCEED();
}
} // namespace Service::BCAT

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/bcat/bcat_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::BCAT {
class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
public:
explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_);
~IDeliveryCacheFileService() override;
private:
Result Open(const DirectoryName& dir_name_raw, const FileName& file_name_raw);
Result Read(Out<u64> out_buffer_size, u64 offset,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer);
Result GetSize(Out<u64> out_size);
Result GetDigest(Out<BcatDigest> out_digest);
FileSys::VirtualDir root;
FileSys::VirtualFile current_file;
};
} // namespace Service::BCAT

View File

@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/bcat/bcat_types.h"
#include "core/hle/service/bcat/delivery_cache_progress_service.h"
#include "core/hle/service/cmif_serialization.h"
namespace Service::BCAT {
IDeliveryCacheProgressService::IDeliveryCacheProgressService(Core::System& system_,
Kernel::KReadableEvent& event_,
const DeliveryCacheProgressImpl& impl_)
: ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&IDeliveryCacheProgressService::GetEvent>, "Get"},
{1, D<&IDeliveryCacheProgressService::GetImpl>, "Get"},
};
// clang-format on
RegisterHandlers(functions);
}
IDeliveryCacheProgressService::~IDeliveryCacheProgressService() = default;
Result IDeliveryCacheProgressService::GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_BCAT, "called");
*out_event = &event;
R_SUCCEED();
}
Result IDeliveryCacheProgressService::GetImpl(
OutLargeData<DeliveryCacheProgressImpl, BufferAttr_HipcPointer> out_impl) {
LOG_DEBUG(Service_BCAT, "called");
*out_impl = impl;
R_SUCCEED();
}
} // namespace Service::BCAT

View File

@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Service::BCAT {
struct DeliveryCacheProgressImpl;
class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
public:
explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_,
const DeliveryCacheProgressImpl& impl_);
~IDeliveryCacheProgressService() override;
private:
Result GetEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetImpl(OutLargeData<DeliveryCacheProgressImpl, BufferAttr_HipcPointer> out_impl);
Kernel::KReadableEvent& event;
const DeliveryCacheProgressImpl& impl;
};
} // namespace Service::BCAT

View File

@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/bcat/bcat_result.h"
#include "core/hle/service/bcat/delivery_cache_directory_service.h"
#include "core/hle/service/bcat/delivery_cache_file_service.h"
#include "core/hle/service/bcat/delivery_cache_storage_service.h"
#include "core/hle/service/cmif_serialization.h"
namespace Service::BCAT {
IDeliveryCacheStorageService::IDeliveryCacheStorageService(Core::System& system_,
FileSys::VirtualDir root_)
: ServiceFramework{system_, "IDeliveryCacheStorageService"}, root(std::move(root_)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&IDeliveryCacheStorageService::CreateFileService>, "CreateFileService"},
{1, D<&IDeliveryCacheStorageService::CreateDirectoryService>, "CreateDirectoryService"},
{10, D<&IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory>, "EnumerateDeliveryCacheDirectory"},
};
// clang-format on
RegisterHandlers(functions);
}
IDeliveryCacheStorageService::~IDeliveryCacheStorageService() = default;
Result IDeliveryCacheStorageService::CreateFileService(
OutInterface<IDeliveryCacheFileService> out_interface) {
LOG_DEBUG(Service_BCAT, "called");
*out_interface = std::make_shared<IDeliveryCacheFileService>(system, root);
R_SUCCEED();
}
Result IDeliveryCacheStorageService::CreateDirectoryService(
OutInterface<IDeliveryCacheDirectoryService> out_interface) {
LOG_DEBUG(Service_BCAT, "called");
*out_interface = std::make_shared<IDeliveryCacheDirectoryService>(system, root);
R_SUCCEED();
}
Result IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory(
Out<s32> out_directory_count,
OutArray<DirectoryName, BufferAttr_HipcMapAlias> out_directories) {
LOG_DEBUG(Service_BCAT, "called, size={:016X}", out_directories.size());
*out_directory_count =
static_cast<s32>(std::min(out_directories.size(), entries.size() - next_read_index));
memcpy(out_directories.data(), entries.data() + next_read_index,
*out_directory_count * sizeof(DirectoryName));
next_read_index += *out_directory_count;
R_SUCCEED();
}
} // namespace Service::BCAT

View File

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/bcat/bcat_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::BCAT {
class IDeliveryCacheFileService;
class IDeliveryCacheDirectoryService;
class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
public:
explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_);
~IDeliveryCacheStorageService() override;
private:
Result CreateFileService(OutInterface<IDeliveryCacheFileService> out_interface);
Result CreateDirectoryService(OutInterface<IDeliveryCacheDirectoryService> out_interface);
Result EnumerateDeliveryCacheDirectory(
Out<s32> out_directory_count,
OutArray<DirectoryName, BufferAttr_HipcMapAlias> out_directories);
FileSys::VirtualDir root;
std::vector<DirectoryName> entries;
std::size_t next_read_index = 0;
};
} // namespace Service::BCAT

View File

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/bcat/news/newly_arrived_event_holder.h"
#include "core/hle/service/cmif_serialization.h"
namespace Service::News {
INewlyArrivedEventHolder::INewlyArrivedEventHolder(Core::System& system_)
: ServiceFramework{system_, "INewlyArrivedEventHolder"}, service_context{
system_,
"INewlyArrivedEventHolder"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, C<&INewlyArrivedEventHolder::Get>, "Get"},
};
// clang-format on
RegisterHandlers(functions);
arrived_event = service_context.CreateEvent("INewlyArrivedEventHolder::ArrivedEvent");
}
INewlyArrivedEventHolder::~INewlyArrivedEventHolder() {
service_context.CloseEvent(arrived_event);
}
Result INewlyArrivedEventHolder::Get(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_INFO(Service_BCAT, "called");
*out_event = &arrived_event->GetReadableEvent();
R_SUCCEED();
}
} // namespace Service::News

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Service::News {
class INewlyArrivedEventHolder final : public ServiceFramework<INewlyArrivedEventHolder> {
public:
explicit INewlyArrivedEventHolder(Core::System& system_);
~INewlyArrivedEventHolder() override;
private:
Result Get(OutCopyHandle<Kernel::KReadableEvent> out_event);
Kernel::KEvent* arrived_event;
KernelHelpers::ServiceContext service_context;
};
} // namespace Service::News

View File

@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/bcat/news/news_data_service.h"
namespace Service::News {
INewsDataService::INewsDataService(Core::System& system_)
: ServiceFramework{system_, "INewsDataService"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Open"},
{1, nullptr, "OpenWithNewsRecordV1"},
{2, nullptr, "Read"},
{3, nullptr, "GetSize"},
{1001, nullptr, "OpenWithNewsRecord"},
};
// clang-format on
RegisterHandlers(functions);
}
INewsDataService::~INewsDataService() = default;
} // namespace Service::News

View File

@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::News {
class INewsDataService final : public ServiceFramework<INewsDataService> {
public:
explicit INewsDataService(Core::System& system_);
~INewsDataService() override;
};
} // namespace Service::News

View File

@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/bcat/news/news_database_service.h"
#include "core/hle/service/cmif_serialization.h"
namespace Service::News {
INewsDatabaseService::INewsDatabaseService(Core::System& system_)
: ServiceFramework{system_, "INewsDatabaseService"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetListV1"},
{1, C<&INewsDatabaseService::Count>, "Count"},
{2, nullptr, "CountWithKey"},
{3, nullptr, "UpdateIntegerValue"},
{4, nullptr, "UpdateIntegerValueWithAddition"},
{5, nullptr, "UpdateStringValue"},
{1000, nullptr, "GetList"},
};
// clang-format on
RegisterHandlers(functions);
}
INewsDatabaseService::~INewsDatabaseService() = default;
Result INewsDatabaseService::Count(Out<s32> out_count,
InBuffer<BufferAttr_HipcPointer> buffer_data) {
LOG_WARNING(Service_BCAT, "(STUBBED) called, buffer_size={}", buffer_data.size());
*out_count = 0;
R_SUCCEED();
}
} // namespace Service::News

View File

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::News {
class INewsDatabaseService final : public ServiceFramework<INewsDatabaseService> {
public:
explicit INewsDatabaseService(Core::System& system_);
~INewsDatabaseService() override;
private:
Result Count(Out<s32> out_count, InBuffer<BufferAttr_HipcPointer> buffer_data);
};
} // namespace Service::News

View File

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/bcat/news/news_service.h"
#include "core/hle/service/cmif_serialization.h"
namespace Service::News {
INewsService::INewsService(Core::System& system_) : ServiceFramework{system_, "INewsService"} {
// clang-format off
static const FunctionInfo functions[] = {
{10100, nullptr, "PostLocalNews"},
{20100, nullptr, "SetPassphrase"},
{30100, C<&INewsService::GetSubscriptionStatus>, "GetSubscriptionStatus"},
{30101, nullptr, "GetTopicList"},
{30110, nullptr, "Unknown30110"},
{30200, nullptr, "IsSystemUpdateRequired"},
{30201, nullptr, "Unknown30201"},
{30210, nullptr, "Unknown30210"},
{30300, nullptr, "RequestImmediateReception"},
{30400, nullptr, "DecodeArchiveFile"},
{30500, nullptr, "Unknown30500"},
{30900, nullptr, "Unknown30900"},
{30901, nullptr, "Unknown30901"},
{30902, nullptr, "Unknown30902"},
{40100, nullptr, "SetSubscriptionStatus"},
{40101, nullptr, "RequestAutoSubscription"},
{40200, nullptr, "ClearStorage"},
{40201, nullptr, "ClearSubscriptionStatusAll"},
{90100, nullptr, "GetNewsDatabaseDump"},
};
// clang-format on
RegisterHandlers(functions);
}
INewsService::~INewsService() = default;
Result INewsService::GetSubscriptionStatus(Out<u32> out_status,
InBuffer<BufferAttr_HipcPointer> buffer_data) {
LOG_WARNING(Service_BCAT, "(STUBBED) called, buffer_size={}", buffer_data.size());
*out_status = 0;
R_SUCCEED();
}
} // namespace Service::News

View File

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::News {
class INewsService final : public ServiceFramework<INewsService> {
public:
explicit INewsService(Core::System& system_);
~INewsService() override;
private:
Result GetSubscriptionStatus(Out<u32> out_status, InBuffer<BufferAttr_HipcPointer> buffer_data);
};
} // namespace Service::News

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/bcat/news/overwrite_event_holder.h"
#include "core/hle/service/cmif_serialization.h"
namespace Service::News {
IOverwriteEventHolder::IOverwriteEventHolder(Core::System& system_)
: ServiceFramework{system_, "IOverwriteEventHolder"}, service_context{system_,
"IOverwriteEventHolder"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, C<&IOverwriteEventHolder::Get>, "Get"},
};
// clang-format on
RegisterHandlers(functions);
overwrite_event = service_context.CreateEvent("IOverwriteEventHolder::OverwriteEvent");
}
IOverwriteEventHolder::~IOverwriteEventHolder() {
service_context.CloseEvent(overwrite_event);
}
Result IOverwriteEventHolder::Get(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_INFO(Service_BCAT, "called");
*out_event = &overwrite_event->GetReadableEvent();
R_SUCCEED();
}
} // namespace Service::News

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Service::News {
class IOverwriteEventHolder final : public ServiceFramework<IOverwriteEventHolder> {
public:
explicit IOverwriteEventHolder(Core::System& system_);
~IOverwriteEventHolder() override;
private:
Result Get(OutCopyHandle<Kernel::KReadableEvent> out_event);
Kernel::KEvent* overwrite_event;
KernelHelpers::ServiceContext service_context;
};
} // namespace Service::News

View File

@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/bcat/news/newly_arrived_event_holder.h"
#include "core/hle/service/bcat/news/news_data_service.h"
#include "core/hle/service/bcat/news/news_database_service.h"
#include "core/hle/service/bcat/news/news_service.h"
#include "core/hle/service/bcat/news/overwrite_event_holder.h"
#include "core/hle/service/bcat/news/service_creator.h"
#include "core/hle/service/cmif_serialization.h"
namespace Service::News {
IServiceCreator::IServiceCreator(Core::System& system_, u32 permissions_, const char* name_)
: ServiceFramework{system_, name_}, permissions{permissions_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, C<&IServiceCreator::CreateNewsService>, "CreateNewsService"},
{1, C<&IServiceCreator::CreateNewlyArrivedEventHolder>, "CreateNewlyArrivedEventHolder"},
{2, C<&IServiceCreator::CreateNewsDataService>, "CreateNewsDataService"},
{3, C<&IServiceCreator::CreateNewsDatabaseService>, "CreateNewsDatabaseService"},
{4, C<&IServiceCreator::CreateOverwriteEventHolder>, "CreateOverwriteEventHolder"},
};
// clang-format on
RegisterHandlers(functions);
}
IServiceCreator::~IServiceCreator() = default;
Result IServiceCreator::CreateNewsService(OutInterface<INewsService> out_interface) {
LOG_INFO(Service_BCAT, "called");
*out_interface = std::make_shared<INewsService>(system);
R_SUCCEED();
}
Result IServiceCreator::CreateNewlyArrivedEventHolder(
OutInterface<INewlyArrivedEventHolder> out_interface) {
LOG_INFO(Service_BCAT, "called");
*out_interface = std::make_shared<INewlyArrivedEventHolder>(system);
R_SUCCEED();
}
Result IServiceCreator::CreateNewsDataService(OutInterface<INewsDataService> out_interface) {
LOG_INFO(Service_BCAT, "called");
*out_interface = std::make_shared<INewsDataService>(system);
R_SUCCEED();
}
Result IServiceCreator::CreateNewsDatabaseService(
OutInterface<INewsDatabaseService> out_interface) {
LOG_INFO(Service_BCAT, "called");
*out_interface = std::make_shared<INewsDatabaseService>(system);
R_SUCCEED();
}
Result IServiceCreator::CreateOverwriteEventHolder(
OutInterface<IOverwriteEventHolder> out_interface) {
LOG_INFO(Service_BCAT, "called");
*out_interface = std::make_shared<IOverwriteEventHolder>(system);
R_SUCCEED();
}
} // namespace Service::News

View File

@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::News {
class INewsService;
class INewlyArrivedEventHolder;
class INewsDataService;
class INewsDatabaseService;
class IOverwriteEventHolder;
class IServiceCreator final : public ServiceFramework<IServiceCreator> {
public:
explicit IServiceCreator(Core::System& system_, u32 permissions_, const char* name_);
~IServiceCreator() override;
private:
Result CreateNewsService(OutInterface<INewsService> out_interface);
Result CreateNewlyArrivedEventHolder(OutInterface<INewlyArrivedEventHolder> out_interface);
Result CreateNewsDataService(OutInterface<INewsDataService> out_interface);
Result CreateNewsDatabaseService(OutInterface<INewsDatabaseService> out_interface);
Result CreateOverwriteEventHolder(OutInterface<IOverwriteEventHolder> out_interface);
u32 permissions;
};
} // namespace Service::News

View File

@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/bcat/bcat_service.h"
#include "core/hle/service/bcat/delivery_cache_storage_service.h"
#include "core/hle/service/bcat/service_creator.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/filesystem.h"
namespace Service::BCAT {
std::unique_ptr<BcatBackend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
DirectoryGetter getter) {
return std::make_unique<NullBcatBackend>(std::move(getter));
}
IServiceCreator::IServiceCreator(Core::System& system_, const char* name_)
: ServiceFramework{system_, name_}, fsc{system.GetFileSystemController()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&IServiceCreator::CreateBcatService>, "CreateBcatService"},
{1, D<&IServiceCreator::CreateDeliveryCacheStorageService>, "CreateDeliveryCacheStorageService"},
{2, D<&IServiceCreator::CreateDeliveryCacheStorageServiceWithApplicationId>, "CreateDeliveryCacheStorageServiceWithApplicationId"},
{3, nullptr, "CreateDeliveryCacheProgressService"},
{4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"},
};
// clang-format on
RegisterHandlers(functions);
backend =
CreateBackendFromSettings(system_, [this](u64 tid) { return fsc.GetBCATDirectory(tid); });
}
IServiceCreator::~IServiceCreator() = default;
Result IServiceCreator::CreateBcatService(ClientProcessId process_id,
OutInterface<IBcatService> out_interface) {
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
*out_interface = std::make_shared<IBcatService>(system, *backend);
R_SUCCEED();
}
Result IServiceCreator::CreateDeliveryCacheStorageService(
ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
const auto title_id = system.GetApplicationProcessProgramID();
*out_interface =
std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
R_SUCCEED();
}
Result IServiceCreator::CreateDeliveryCacheStorageServiceWithApplicationId(
u64 application_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
LOG_DEBUG(Service_BCAT, "called, application_id={:016X}", application_id);
*out_interface = std::make_shared<IDeliveryCacheStorageService>(
system, fsc.GetBCATDirectory(application_id));
R_SUCCEED();
}
} // namespace Service::BCAT

View File

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::FileSystem {
class FileSystemController;
}
namespace Service::BCAT {
class BcatBackend;
class IBcatService;
class IDeliveryCacheStorageService;
class IServiceCreator final : public ServiceFramework<IServiceCreator> {
public:
explicit IServiceCreator(Core::System& system_, const char* name_);
~IServiceCreator() override;
private:
Result CreateBcatService(ClientProcessId process_id, OutInterface<IBcatService> out_interface);
Result CreateDeliveryCacheStorageService(
ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface);
Result CreateDeliveryCacheStorageServiceWithApplicationId(
u64 application_id, OutInterface<IDeliveryCacheStorageService> out_interface);
std::unique_ptr<BcatBackend> backend;
Service::FileSystem::FileSystemController& fsc;
};
} // namespace Service::BCAT

View File

@ -270,7 +270,7 @@ class OutLargeData {
public:
static_assert(std::is_trivially_copyable_v<T>, "LargeData type must be trivially copyable");
static_assert((A & BufferAttr_In) == 0, "OutLargeData attr must not be In");
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize);
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_Out | BufferAttr_FixedSize);
using Type = T;
/* implicit */ OutLargeData(const OutLargeData& t) : raw(t.raw) {}

View File

@ -7,6 +7,7 @@
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
#include "core/hle/service/glue/time/worker.h"
#include "core/hle/service/os/multi_wait_utils.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/psc/time/static.h"
@ -143,82 +144,46 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
Common::SetCurrentThreadName("TimeWorker");
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
enum class EventType {
Exit = 0,
IpmModuleService_GetEvent = 1,
PowerStateChange = 2,
SignalAlarms = 3,
UpdateLocalSystemClock = 4,
UpdateNetworkSystemClock = 5,
UpdateEphemeralSystemClock = 6,
UpdateSteadyClock = 7,
UpdateFileTimestamp = 8,
AutoCorrect = 9,
Max = 10,
};
s32 num_objs{};
std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{};
std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{};
const auto AddWaiter{
[&](Kernel::KSynchronizationObject* synchronization_object, EventType type) {
// Open a new reference to the object.
synchronization_object->Open();
// Insert into the list.
wait_indices[num_objs] = type;
wait_objs[num_objs++] = synchronization_object;
}};
while (!stop_token.stop_requested()) {
SCOPE_EXIT({
for (s32 i = 0; i < num_objs; i++) {
wait_objs[i]->Close();
}
});
enum class EventType : s32 {
Exit = 0,
PowerStateChange = 1,
SignalAlarms = 2,
UpdateLocalSystemClock = 3,
UpdateNetworkSystemClock = 4,
UpdateEphemeralSystemClock = 5,
UpdateSteadyClock = 6,
UpdateFileTimestamp = 7,
AutoCorrect = 8,
};
s32 index{};
num_objs = {};
wait_objs = {};
if (m_pm_state_change_handler.m_priority != 0) {
AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
// TODO
// AddWaiter(gIPmModuleService::GetEvent(), 1);
AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange);
// TODO: gIPmModuleService::GetEvent() 1
index = WaitAny(m_system.Kernel(),
&m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent() // 1
);
} else {
AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
// TODO
// AddWaiter(gIPmModuleService::GetEvent(), 1);
AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange);
AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms);
AddWaiter(m_local_clock_event, EventType::UpdateLocalSystemClock);
AddWaiter(m_network_clock_event, EventType::UpdateNetworkSystemClock);
AddWaiter(m_ephemeral_clock_event, EventType::UpdateEphemeralSystemClock);
AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock);
AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp);
AddWaiter(m_standard_user_auto_correct_clock_event, EventType::AutoCorrect);
// TODO: gIPmModuleService::GetEvent() 1
index = WaitAny(m_system.Kernel(),
&m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent(), // 1
&m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
m_local_clock_event, // 3
m_network_clock_event, // 4
m_ephemeral_clock_event, // 5
&m_timer_steady_clock->GetReadableEvent(), // 6
&m_timer_file_system->GetReadableEvent(), // 7
m_standard_user_auto_correct_clock_event // 8
);
}
s32 out_index{-1};
Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(),
num_objs, -1);
ASSERT(out_index >= 0 && out_index < num_objs);
if (stop_token.stop_requested()) {
return;
}
switch (wait_indices[out_index]) {
switch (static_cast<EventType>(index)) {
case EventType::Exit:
return;
case EventType::IpmModuleService_GetEvent:
// TODO
// IPmModuleService::GetEvent()
// clear the event
// Handle power state change event
break;
case EventType::PowerStateChange:
m_alarm_worker.GetEvent().Clear();
if (m_pm_state_change_handler.m_priority <= 1) {
@ -235,19 +200,19 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
m_local_clock_event->Clear();
Service::PSC::Time::SystemClockContext context{};
auto res = m_local_clock->GetSystemClockContext(&context);
ASSERT(res == ResultSuccess);
R_ASSERT(m_local_clock->GetSystemClockContext(&context));
m_set_sys->SetUserSystemClockContext(context);
m_file_timestamp_worker.SetFilesystemPosixTime();
} break;
break;
}
case EventType::UpdateNetworkSystemClock: {
m_network_clock_event->Clear();
Service::PSC::Time::SystemClockContext context{};
auto res = m_network_clock->GetSystemClockContext(&context);
ASSERT(res == ResultSuccess);
R_ASSERT(m_network_clock->GetSystemClockContext(&context));
m_set_sys->SetNetworkSystemClockContext(context);
s64 time{};
@ -267,7 +232,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
}
m_file_timestamp_worker.SetFilesystemPosixTime();
} break;
break;
}
case EventType::UpdateEphemeralSystemClock: {
m_ephemeral_clock_event->Clear();
@ -295,7 +261,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
if (!g_ig_report_ephemeral_clock_context_set) {
g_ig_report_ephemeral_clock_context_set = true;
}
} break;
break;
}
case EventType::UpdateSteadyClock:
m_timer_steady_clock->Clear();
@ -314,21 +281,20 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) {
m_standard_user_auto_correct_clock_event->Clear();
bool automatic_correction{};
auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
&automatic_correction);
ASSERT(res == ResultSuccess);
R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
&automatic_correction));
Service::PSC::Time::SteadyClockTimePoint time_point{};
res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point);
ASSERT(res == ResultSuccess);
R_ASSERT(
m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
} break;
break;
}
default:
UNREACHABLE();
break;
}
}
}

View File

@ -36,7 +36,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService(
"hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings));
server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system));
server_manager->RegisterNamedService("hidbus", std::make_shared<Hidbus>(system));
server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system));
server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system));

Some files were not shown because too many files have changed in this diff Show More