summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts31
-rw-r--r--src/android/app/src/main/AndroidManifest.xml1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt26
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt29
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt89
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt26
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt75
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt35
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt138
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt27
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt28
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt40
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt20
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt4
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_switch.xml55
-rw-r--r--src/android/app/src/main/res/layout/list_item_settings_header.xml28
-rw-r--r--src/android/app/src/main/res/values/arrays.xml11
-rw-r--r--src/android/app/src/main/res/values/strings.xml20
-rw-r--r--src/core/hle/kernel/k_thread.cpp15
-rw-r--r--src/core/hle/kernel/k_thread.h4
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.cpp44
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.h9
-rw-r--r--src/core/hle/service/nfc/common/device.cpp164
-rw-r--r--src/core/hle/service/nfc/common/device.h11
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp4
-rw-r--r--src/core/hle/service/nfc/mifare_result.h2
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp13
-rw-r--r--src/core/hle/service/nfc/nfc_result.h3
-rw-r--r--src/core/hle/service/nfc/nfc_types.h37
-rw-r--r--src/core/hle/service/nfp/nfp_types.h26
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp2
-rw-r--r--src/input_common/drivers/virtual_amiibo.h1
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp27
-rw-r--r--src/video_core/renderer_opengl/gl_shader_context.h6
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp72
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h11
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h11
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp51
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h9
-rw-r--r--src/video_core/texture_cache/image_view_base.cpp52
-rw-r--r--src/video_core/texture_cache/image_view_base.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h30
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h12
-rw-r--r--src/video_core/textures/texture.cpp7
94 files changed, 1148 insertions, 597 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index a637db78a..bab4f4d0f 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -2,7 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import android.annotation.SuppressLint
+import kotlin.collections.setOf
import org.jetbrains.kotlin.konan.properties.Properties
+import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
plugins {
id("com.android.application")
@@ -10,6 +12,7 @@ plugins {
id("kotlin-parcelize")
kotlin("plugin.serialization") version "1.8.21"
id("androidx.navigation.safeargs.kotlin")
+ id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
}
/**
@@ -44,16 +47,6 @@ android {
jniLibs.useLegacyPackaging = true
}
- lint {
- // This is important as it will run lint but not abort on error
- // Lint has some overly obnoxious "errors" that should really be warnings
- abortOnError = false
-
- //Uncomment disable lines for test builds...
- //disable 'MissingTranslation'bin
- //disable 'ExtraTranslation'
- }
-
defaultConfig {
// TODO If this is ever modified, change application_id in strings.xml
applicationId = "org.yuzu.yuzu_emu"
@@ -167,6 +160,24 @@ android {
}
}
+tasks.getByPath("preBuild").dependsOn("ktlintCheck")
+
+ktlint {
+ version.set("0.47.1")
+ android.set(true)
+ ignoreFailures.set(false)
+ disabledRules.set(
+ setOf(
+ "no-wildcard-imports",
+ "package-name",
+ "import-ordering"
+ )
+ )
+ reporters {
+ reporter(ReporterType.CHECKSTYLE)
+ }
+}
+
dependencies {
implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.appcompat:appcompat:1.6.1")
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index a6f87fc2e..e31ad69e2 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -53,6 +53,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<activity
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
android:theme="@style/Theme.Yuzu.Main"
+ android:screenOrientation="userLandscape"
android:supportsPictureInPicture="true"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
android:exported="true">
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index f3bfbe7eb..f860cdd4b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -14,18 +14,18 @@ import android.widget.TextView
import androidx.annotation.Keep
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
-import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
-import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
import org.yuzu.yuzu_emu.utils.FileUtil.exists
+import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
+import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
import org.yuzu.yuzu_emu.utils.Log.error
import org.yuzu.yuzu_emu.utils.Log.verbose
import org.yuzu.yuzu_emu.utils.Log.warning
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
-import java.lang.ref.WeakReference
/**
* Class which contains methods that interact
@@ -76,7 +76,9 @@ object NativeLibrary {
fun openContentUri(path: String?, openmode: String?): Int {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
- } else openContentUri(appContext, path, openmode)
+ } else {
+ openContentUri(appContext, path, openmode)
+ }
}
@Keep
@@ -84,7 +86,9 @@ object NativeLibrary {
fun getSize(path: String?): Long {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.getFileSize(path)
- } else getFileSize(appContext, path)
+ } else {
+ getFileSize(appContext, path)
+ }
}
@Keep
@@ -92,7 +96,9 @@ object NativeLibrary {
fun exists(path: String?): Boolean {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.exists(path)
- } else exists(appContext, path)
+ } else {
+ exists(appContext, path)
+ }
}
@Keep
@@ -100,7 +106,9 @@ object NativeLibrary {
fun isDirectory(path: String?): Boolean {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.isDirectory(path)
- } else isDirectory(appContext, path)
+ } else {
+ isDirectory(appContext, path)
+ }
}
/**
@@ -454,7 +462,9 @@ object NativeLibrary {
Html.FROM_HTML_MODE_LEGACY
)
)
- .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> emulationActivity.finish() }
+ .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
+ emulationActivity.finish()
+ }
.setOnDismissListener { emulationActivity.finish() }
emulationActivity.runOnUiThread {
val alert = builder.create()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
index 4c947b786..04ab6a220 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
@@ -7,12 +7,12 @@ import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
+import java.io.File
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
-import java.io.File
-fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
+fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
class YuzuApplication : Application() {
private fun createNotificationChannels() {
@@ -21,7 +21,9 @@ class YuzuApplication : Application() {
getString(R.string.emulation_notification_channel_name),
NotificationManager.IMPORTANCE_LOW
)
- emulationChannel.description = getString(R.string.emulation_notification_channel_description)
+ emulationChannel.description = getString(
+ R.string.emulation_notification_channel_description
+ )
emulationChannel.setSound(null, null)
emulationChannel.vibrationPattern = null
@@ -48,7 +50,7 @@ class YuzuApplication : Application() {
GpuDriverHelper.initializeDriverParameters(applicationContext)
NativeLibrary.logDeviceInfo()
- createNotificationChannels();
+ createNotificationChannels()
}
companion object {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 5ca519f0a..f0a6753a9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -33,6 +33,7 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.navigation.fragment.NavHostFragment
+import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
@@ -45,7 +46,6 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ThemeHelper
-import kotlin.math.roundToInt
class EmulationActivity : AppCompatActivity(), SensorEventListener {
private lateinit var binding: ActivityEmulationBinding
@@ -256,7 +256,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
}
}
- private fun PictureInPictureParams.Builder.getPictureInPictureAspectBuilder(): PictureInPictureParams.Builder {
+ private fun PictureInPictureParams.Builder.getPictureInPictureAspectBuilder():
+ PictureInPictureParams.Builder {
val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.int) {
0 -> Rational(16, 9)
1 -> Rational(4, 3)
@@ -267,7 +268,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return this.apply { aspectRatio?.let { setAspectRatio(it) } }
}
- private fun PictureInPictureParams.Builder.getPictureInPictureActionsBuilder(): PictureInPictureParams.Builder {
+ private fun PictureInPictureParams.Builder.getPictureInPictureActionsBuilder():
+ PictureInPictureParams.Builder {
val pictureInPictureActions: MutableList<RemoteAction> = mutableListOf()
val pendingFlags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
@@ -310,7 +312,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- pictureInPictureParamsBuilder.setAutoEnterEnabled(BooleanSetting.PICTURE_IN_PICTURE.boolean)
+ pictureInPictureParamsBuilder.setAutoEnterEnabled(
+ BooleanSetting.PICTURE_IN_PICTURE.boolean
+ )
}
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
}
@@ -341,7 +345,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
} else {
try {
unregisterReceiver(pictureInPictureReceiver)
- } catch (ignored : Exception) {
+ } catch (ignored: Exception) {
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index 83d08841b..e91277d35 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -28,10 +28,9 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
import org.yuzu.yuzu_emu.databinding.CardGameBinding
-import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.model.Game
-import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
import org.yuzu.yuzu_emu.model.GamesViewModel
class GameAdapter(private val activity: AppCompatActivity) :
@@ -60,7 +59,10 @@ class GameAdapter(private val activity: AppCompatActivity) :
override fun onClick(view: View) {
val holder = view.tag as GameViewHolder
- val gameExists = DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(holder.game.path))?.exists() == true
+ val gameExists = DocumentFile.fromSingleUri(
+ YuzuApplication.appContext,
+ Uri.parse(holder.game.path)
+ )?.exists() == true
if (!gameExists) {
Toast.makeText(
YuzuApplication.appContext,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index b719dd539..d3df3bc81 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -58,11 +58,12 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L
)
when (option.titleId) {
- R.string.get_early_access -> binding.optionLayout.background =
- ContextCompat.getDrawable(
- binding.optionCard.context,
- R.drawable.premium_background
- )
+ R.string.get_early_access ->
+ binding.optionLayout.background =
+ ContextCompat.getDrawable(
+ binding.optionCard.context,
+ R.drawable.premium_background
+ )
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt
index 82a6712b6..e058067c9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard.kt
@@ -12,10 +12,10 @@ import android.view.WindowInsets
import android.view.inputmethod.InputMethodManager
import androidx.annotation.Keep
import androidx.core.view.ViewCompat
+import java.io.Serializable
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.applets.keyboard.ui.KeyboardDialogFragment
-import java.io.Serializable
@Keep
object SoftwareKeyboard {
@@ -40,19 +40,22 @@ object SoftwareKeyboard {
// There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result.
val handler = Handler(Looper.myLooper()!!)
val delayMs = 500
- handler.postDelayed(object : Runnable {
- override fun run() {
- val insets = ViewCompat.getRootWindowInsets(overlayView)
- val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime())
- if (isKeyboardVisible) {
- handler.postDelayed(this, delayMs.toLong())
- return
- }
+ handler.postDelayed(
+ object : Runnable {
+ override fun run() {
+ val insets = ViewCompat.getRootWindowInsets(overlayView)
+ val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime())
+ if (isKeyboardVisible) {
+ handler.postDelayed(this, delayMs.toLong())
+ return
+ }
- // No longer visible, submit the result.
- NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
- }
- }, delayMs.toLong())
+ // No longer visible, submit the result.
+ NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
+ }
+ },
+ delayMs.toLong()
+ )
}
@JvmStatic
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
index 3b1559c80..a18efef19 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
@@ -20,7 +20,10 @@ object DiskShaderCacheProgress {
emulationActivity.getString(R.string.loading),
emulationActivity.getString(R.string.preparing_shaders)
)
- fragment.show(emulationActivity.supportFragmentManager, ShaderProgressDialogFragment.TAG)
+ fragment.show(
+ emulationActivity.supportFragmentManager,
+ ShaderProgressDialogFragment.TAG
+ )
}
synchronized(finishLock) { finishLock.wait() }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
index 2c68c9ac3..8a8e0a6e8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
@@ -62,7 +62,9 @@ class ShaderProgressDialogFragment : DialogFragment() {
shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg ->
alertDialog.setMessage(msg)
}
- synchronized(DiskShaderCacheProgress.finishLock) { DiskShaderCacheProgress.finishLock.notifyAll() }
+ synchronized(DiskShaderCacheProgress.finishLock) {
+ DiskShaderCacheProgress.finishLock.notifyAll()
+ }
}
override fun onDestroyView() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt
index 4c3a9ca80..f3be156b5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/DocumentProvider.kt
@@ -13,11 +13,11 @@ import android.os.ParcelFileDescriptor
import android.provider.DocumentsContract
import android.provider.DocumentsProvider
import android.webkit.MimeTypeMap
+import java.io.*
import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.getPublicFilesDir
-import java.io.*
class DocumentProvider : DocumentsProvider() {
private val baseDirectory: File
@@ -44,7 +44,7 @@ class DocumentProvider : DocumentsProvider() {
DocumentsContract.Document.COLUMN_SIZE
)
- const val AUTHORITY : String = BuildConfig.APPLICATION_ID + ".user"
+ const val AUTHORITY: String = BuildConfig.APPLICATION_ID + ".user"
const val ROOT_ID: String = "root"
}
@@ -58,7 +58,11 @@ class DocumentProvider : DocumentsProvider() {
private fun getFile(documentId: String): File {
if (documentId.startsWith(ROOT_ID)) {
val file = baseDirectory.resolve(documentId.drop(ROOT_ID.length + 1))
- if (!file.exists()) throw FileNotFoundException("${file.absolutePath} ($documentId) not found")
+ if (!file.exists()) {
+ throw FileNotFoundException(
+ "${file.absolutePath} ($documentId) not found"
+ )
+ }
return file
} else {
throw FileNotFoundException("'$documentId' is not in any known root")
@@ -80,7 +84,8 @@ class DocumentProvider : DocumentsProvider() {
add(DocumentsContract.Root.COLUMN_SUMMARY, null)
add(
DocumentsContract.Root.COLUMN_FLAGS,
- DocumentsContract.Root.FLAG_SUPPORTS_CREATE or DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
+ DocumentsContract.Root.FLAG_SUPPORTS_CREATE or
+ DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
)
add(DocumentsContract.Root.COLUMN_TITLE, context!!.getString(R.string.app_name))
add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocumentId(baseDirectory))
@@ -127,11 +132,13 @@ class DocumentProvider : DocumentsProvider() {
try {
if (DocumentsContract.Document.MIME_TYPE_DIR == mimeType) {
- if (!newFile.mkdir())
+ if (!newFile.mkdir()) {
throw IOException("Failed to create directory")
+ }
} else {
- if (!newFile.createNewFile())
+ if (!newFile.createNewFile()) {
throw IOException("Failed to create file")
+ }
}
} catch (e: IOException) {
throw FileNotFoundException("Couldn't create document '${newFile.path}': ${e.message}")
@@ -142,8 +149,9 @@ class DocumentProvider : DocumentsProvider() {
override fun deleteDocument(documentId: String?) {
val file = getFile(documentId!!)
- if (!file.delete())
+ if (!file.delete()) {
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
+ }
}
override fun removeDocument(documentId: String, parentDocumentId: String?) {
@@ -151,38 +159,55 @@ class DocumentProvider : DocumentsProvider() {
val file = getFile(documentId)
if (parent == file || file.parentFile == null || file.parentFile!! == parent) {
- if (!file.delete())
+ if (!file.delete()) {
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
+ }
} else {
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
}
}
override fun renameDocument(documentId: String?, displayName: String?): String {
- if (displayName == null)
- throw FileNotFoundException("Couldn't rename document '$documentId' as the new name is null")
+ if (displayName == null) {
+ throw FileNotFoundException(
+ "Couldn't rename document '$documentId' as the new name is null"
+ )
+ }
val sourceFile = getFile(documentId!!)
val sourceParentFile = sourceFile.parentFile
- ?: throw FileNotFoundException("Couldn't rename document '$documentId' as it has no parent")
+ ?: throw FileNotFoundException(
+ "Couldn't rename document '$documentId' as it has no parent"
+ )
val destFile = sourceParentFile.resolve(displayName)
try {
- if (!sourceFile.renameTo(destFile))
- throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'")
+ if (!sourceFile.renameTo(destFile)) {
+ throw FileNotFoundException(
+ "Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'"
+ )
+ }
} catch (e: Exception) {
- throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': ${e.message}")
+ throw FileNotFoundException(
+ "Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': " +
+ "${e.message}"
+ )
}
return getDocumentId(destFile)
}
private fun copyDocument(
- sourceDocumentId: String, sourceParentDocumentId: String,
+ sourceDocumentId: String,
+ sourceParentDocumentId: String,
targetParentDocumentId: String?
): String {
- if (!isChildDocument(sourceParentDocumentId, sourceDocumentId))
- throw FileNotFoundException("Couldn't copy document '$sourceDocumentId' as its parent is not '$sourceParentDocumentId'")
+ if (!isChildDocument(sourceParentDocumentId, sourceDocumentId)) {
+ throw FileNotFoundException(
+ "Couldn't copy document '$sourceDocumentId' as its parent is not " +
+ "'$sourceParentDocumentId'"
+ )
+ }
return copyDocument(sourceDocumentId, targetParentDocumentId)
}
@@ -193,8 +218,13 @@ class DocumentProvider : DocumentsProvider() {
val newFile = parent.resolveWithoutConflict(oldFile.name)
try {
- if (!(newFile.createNewFile() && newFile.setWritable(true) && newFile.setReadable(true)))
+ if (!(
+ newFile.createNewFile() && newFile.setWritable(true) &&
+ newFile.setReadable(true)
+ )
+ ) {
throw IOException("Couldn't create new file")
+ }
FileInputStream(oldFile).use { inStream ->
FileOutputStream(newFile).use { outStream ->
@@ -209,12 +239,14 @@ class DocumentProvider : DocumentsProvider() {
}
override fun moveDocument(
- sourceDocumentId: String, sourceParentDocumentId: String?,
+ sourceDocumentId: String,
+ sourceParentDocumentId: String?,
targetParentDocumentId: String?
): String {
try {
val newDocumentId = copyDocument(
- sourceDocumentId, sourceParentDocumentId!!,
+ sourceDocumentId,
+ sourceParentDocumentId!!,
targetParentDocumentId
)
removeDocument(sourceDocumentId, sourceParentDocumentId)
@@ -245,24 +277,30 @@ class DocumentProvider : DocumentsProvider() {
add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, localDocumentId)
add(
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
- if (localFile == baseDirectory) context!!.getString(R.string.app_name) else localFile.name
+ if (localFile == baseDirectory) {
+ context!!.getString(R.string.app_name)
+ } else {
+ localFile.name
+ }
)
add(DocumentsContract.Document.COLUMN_SIZE, localFile.length())
add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile))
add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified())
add(DocumentsContract.Document.COLUMN_FLAGS, flags)
- if (localFile == baseDirectory)
+ if (localFile == baseDirectory) {
add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_yuzu)
+ }
}
return cursor
}
private fun getTypeForFile(file: File): Any {
- return if (file.isDirectory)
+ return if (file.isDirectory) {
DocumentsContract.Document.MIME_TYPE_DIR
- else
+ } else {
getTypeForName(file.name)
+ }
}
private fun getTypeForName(name: String): Any {
@@ -270,8 +308,9 @@ class DocumentProvider : DocumentsProvider() {
if (lastDot >= 0) {
val extension = name.substring(lastDot + 1)
val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
- if (mime != null)
+ if (mime != null) {
return mime
+ }
}
return "application/octect-stream"
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index 63b4df273..d41933766 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -8,6 +8,9 @@ enum class BooleanSetting(
override val section: String,
override val defaultValue: Boolean
) : AbstractBooleanSetting {
+ CPU_DEBUG_MODE("cpu_debug_mode", Settings.SECTION_CPU, false),
+ FASTMEM("cpuopt_fastmem", Settings.SECTION_CPU, true),
+ FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.SECTION_CPU, true),
PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true),
USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false);
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index 6bcb7bee0..88afb2223 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -4,11 +4,11 @@
package org.yuzu.yuzu_emu.features.settings.model
import android.text.TextUtils
+import java.util.*
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
-import java.util.*
class Settings {
private var gameId: String? = null
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
index 63f95690c..6621289fd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
@@ -8,6 +8,7 @@ enum class StringSetting(
override val section: String,
override val defaultValue: String
) : AbstractStringSetting {
+ AUDIO_OUTPUT_ENGINE("output_engine", Settings.SECTION_AUDIO, "auto"),
CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0");
override var string: String = defaultValue
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
index 0f8edbfb0..a67001311 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
@@ -3,12 +3,8 @@
package org.yuzu.yuzu_emu.features.settings.model.view
-import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
-
class HeaderSetting(
- setting: AbstractSetting?,
- titleId: Int,
- descriptionId: Int
-) : SettingsItem(setting, titleId, descriptionId) {
+ titleId: Int
+) : SettingsItem(null, titleId, 0) {
override val type = TYPE_HEADER
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
index 9eac9904e..7306ec458 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
@@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
-import org.yuzu.yuzu_emu.features.settings.model.IntSetting
class SingleChoiceSetting(
setting: AbstractIntSetting?,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
index 842648ce4..92d0167ae 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
@@ -3,13 +3,11 @@
package org.yuzu.yuzu_emu.features.settings.model.view
+import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
-import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
-import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.utils.Log
-import kotlin.math.roundToInt
class SliderSetting(
setting: AbstractSetting?,
@@ -19,7 +17,7 @@ class SliderSetting(
val max: Int,
val units: String,
val key: String? = null,
- val defaultValue: Int? = null,
+ val defaultValue: Int? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SLIDER
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
index 9e9b00d10..3b6731dcd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
@@ -5,24 +5,25 @@ package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
-import org.yuzu.yuzu_emu.features.settings.model.StringSetting
class StringSingleChoiceSetting(
- val key: String? = null,
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int,
- val choicesId: Array<String>,
- private val valuesId: Array<String>?,
+ val choices: Array<String>,
+ val values: Array<String>?,
+ val key: String? = null,
private val defaultValue: String? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_STRING_SINGLE_CHOICE
fun getValueAt(index: Int): String? {
- if (valuesId == null) return null
- return if (index >= 0 && index < valuesId.size) {
- valuesId[index]
- } else ""
+ if (values == null) return null
+ return if (index >= 0 && index < values.size) {
+ values[index]
+ } else {
+ ""
+ }
}
val selectedValue: String
@@ -35,8 +36,8 @@ class StringSingleChoiceSetting(
val selectValueIndex: Int
get() {
val selectedValue = selectedValue
- for (i in valuesId!!.indices) {
- if (valuesId[i] == selectedValue) {
+ for (i in values!!.indices) {
+ if (values[i] == selectedValue) {
return i
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
index a3ef59c2f..8a9d13a92 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
@@ -3,8 +3,6 @@
package org.yuzu.yuzu_emu.features.settings.model.view
-import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
-
class SubmenuSetting(
titleId: Int,
descriptionId: Int,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
index da7062b87..a5af5a7ae 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
@@ -8,18 +8,18 @@ import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.View
+import android.view.ViewGroup.MarginLayoutParams
import android.widget.Toast
+import androidx.activity.OnBackPressedCallback
+import androidx.activity.result.ActivityResultLauncher
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
-import android.view.ViewGroup.MarginLayoutParams
-import androidx.activity.OnBackPressedCallback
-import androidx.activity.result.ActivityResultLauncher
import androidx.core.view.updatePadding
import com.google.android.material.color.MaterialColors
-import org.yuzu.yuzu_emu.NativeLibrary
+import java.io.IOException
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
@@ -30,7 +30,6 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.*
-import java.io.IOException
class SettingsActivity : AppCompatActivity(), SettingsActivityView {
private val presenter = SettingsActivityPresenter(this)
@@ -60,7 +59,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
setSupportActionBar(binding.toolbarSettings)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
- if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) {
+ if (InsetsHelper.getSystemGestureType(applicationContext) !=
+ InsetsHelper.GESTURE_NAVIGATION
+ ) {
binding.navigationBarShade.setBackgroundColor(
ThemeHelper.getColorWithOpacity(
MaterialColors.getColor(
@@ -76,7 +77,8 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() = navigateBack()
- })
+ }
+ )
setInsets()
}
@@ -149,11 +151,13 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
private fun areSystemAnimationsEnabled(): Boolean {
val duration = android.provider.Settings.Global.getFloat(
contentResolver,
- android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, 1f
+ android.provider.Settings.Global.ANIMATOR_DURATION_SCALE,
+ 1f
)
val transition = android.provider.Settings.Global.getFloat(
contentResolver,
- android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE, 1f
+ android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE,
+ 1f
)
return duration != 0f && transition != 0f
}
@@ -208,7 +212,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment?
private fun setInsets() {
- ViewCompat.setOnApplyWindowInsetsListener(binding.frameContent) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.frameContent
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
view.updatePadding(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
index 4361d95fb..93e677b21 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
@@ -6,12 +6,12 @@ package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.os.Bundle
import android.text.TextUtils
+import java.io.File
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.Log
-import java.io.File
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
val settings: Settings get() = activityView.settings
@@ -46,9 +46,15 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
private fun prepareDirectoriesIfNeeded() {
val configFile =
- File(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini")
+ File(
+ "${DirectoryInitialization.userDirectory}/config/" +
+ "${SettingsFile.FILE_NAME_CONFIG}.ini"
+ )
if (!configFile.exists()) {
- Log.error(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini")
+ Log.error(
+ "${DirectoryInitialization.userDirectory}/config/" +
+ "${SettingsFile.FILE_NAME_CONFIG}.ini"
+ )
Log.error("yuzu config file could not be found!")
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index 1eb4899fc..ce0b92c90 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
@@ -13,7 +13,6 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.setFragmentResultListener
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -139,7 +138,7 @@ class SettingsAdapter(
clickedItem = item
dialog = MaterialAlertDialogBuilder(context)
.setTitle(item.nameId)
- .setSingleChoiceItems(item.choicesId, item.selectValueIndex, this)
+ .setSingleChoiceItems(item.choices, item.selectValueIndex, this)
.show()
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index 867147950..70a74c4dd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -50,7 +50,10 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
settingsAdapter = SettingsAdapter(this, requireActivity())
- val dividerDecoration = MaterialDividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
+ val dividerDecoration = MaterialDividerItemDecoration(
+ requireContext(),
+ LinearLayoutManager.VERTICAL
+ )
dividerDecoration.isLastItemDecorated = false
binding.listSettings.apply {
adapter = settingsAdapter
@@ -99,7 +102,9 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
}
private fun setInsets() {
- ViewCompat.setOnApplyWindowInsetsListener(binding.listSettings) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.listSettings
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(bottom = insets.bottom)
windowInsets
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index b611389a1..59c1d9d54 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -7,7 +7,6 @@ import android.content.SharedPreferences
import android.os.Build
import android.text.TextUtils
import androidx.preference.PreferenceManager
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
@@ -43,7 +42,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
}
fun putSetting(setting: AbstractSetting) {
- if (setting.section == null) {
+ if (setting.section == null || setting.key == null) {
return
}
@@ -236,7 +235,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics))
sl.apply {
-
add(
SingleChoiceSetting(
IntSetting.RENDERER_ACCURACY,
@@ -355,18 +353,31 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio))
- sl.add(
- SliderSetting(
- IntSetting.AUDIO_VOLUME,
- R.string.audio_volume,
- R.string.audio_volume_description,
- 0,
- 100,
- "%",
- IntSetting.AUDIO_VOLUME.key,
- IntSetting.AUDIO_VOLUME.defaultValue
- )
- )
+ sl.apply {
+ add(
+ StringSingleChoiceSetting(
+ StringSetting.AUDIO_OUTPUT_ENGINE,
+ R.string.audio_output_engine,
+ 0,
+ settingsActivity.resources.getStringArray(R.array.outputEngineEntries),
+ settingsActivity.resources.getStringArray(R.array.outputEngineValues),
+ StringSetting.AUDIO_OUTPUT_ENGINE.key,
+ StringSetting.AUDIO_OUTPUT_ENGINE.defaultValue
+ )
+ )
+ add(
+ SliderSetting(
+ IntSetting.AUDIO_VOLUME,
+ R.string.audio_volume,
+ R.string.audio_volume_description,
+ 0,
+ 100,
+ "%",
+ IntSetting.AUDIO_VOLUME.key,
+ IntSetting.AUDIO_VOLUME.defaultValue
+ )
+ )
+ }
}
private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
@@ -469,6 +480,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug))
sl.apply {
+ add(HeaderSetting(R.string.gpu))
add(
SingleChoiceSetting(
IntSetting.RENDERER_BACKEND,
@@ -489,6 +501,39 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.RENDERER_DEBUG.defaultValue
)
)
+
+ add(HeaderSetting(R.string.cpu))
+ add(
+ SwitchSetting(
+ BooleanSetting.CPU_DEBUG_MODE,
+ R.string.cpu_debug_mode,
+ R.string.cpu_debug_mode_description,
+ BooleanSetting.CPU_DEBUG_MODE.key,
+ BooleanSetting.CPU_DEBUG_MODE.defaultValue
+ )
+ )
+
+ val fastmem = object : AbstractBooleanSetting {
+ override var boolean: Boolean
+ get() =
+ BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean
+ set(value) {
+ BooleanSetting.FASTMEM.boolean = value
+ BooleanSetting.FASTMEM_EXCLUSIVES.boolean = value
+ }
+ override val key: String? = null
+ override val section: String = Settings.SECTION_CPU
+ override val isRuntimeEditable: Boolean = false
+ override val valueAsString: String = ""
+ override val defaultValue: Any = true
+ }
+ add(
+ SwitchSetting(
+ fastmem,
+ R.string.fastmem,
+ 0
+ )
+ )
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index 04c045e77..7955532ee 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -4,15 +4,15 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
-import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
-import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
-import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
-import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
+import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
+import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
index de764a27f..e4e321bd3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
@@ -26,6 +26,14 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
for (i in values.indices) {
if (values[i] == item.selectedValue) {
binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i]
+ return
+ }
+ }
+ } else if (item is StringSingleChoiceSetting) {
+ for (i in item.values!!.indices) {
+ if (item.values[i] == item.selectedValue) {
+ binding.textSettingDescription.text = item.choices[i]
+ return
}
}
} else {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
index b163bd6ca..54f531795 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
@@ -6,8 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import android.widget.CompoundButton
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
-import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
index e29bca11d..70a52df5d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
@@ -3,6 +3,8 @@
package org.yuzu.yuzu_emu.features.settings.utils
+import java.io.*
+import java.util.*
import org.ini4j.Wini
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -13,8 +15,6 @@ import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
import org.yuzu.yuzu_emu.utils.BiMap
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.Log
-import java.io.*
-import java.util.*
/**
* Contains static methods for interacting with .ini files in which settings are stored.
@@ -137,9 +137,12 @@ object SettingsFile {
for (settingKey in sortedKeySet) {
val setting = settings[settingKey]
NativeLibrary.setUserSetting(
- gameId, mapSectionNameFromIni(
+ gameId,
+ mapSectionNameFromIni(
section.name
- ), setting!!.key, setting.valueAsString
+ ),
+ setting!!.key,
+ setting.valueAsString
)
}
}
@@ -148,13 +151,17 @@ object SettingsFile {
private fun mapSectionNameFromIni(generalSectionName: String): String? {
return if (sectionsMap.getForward(generalSectionName) != null) {
sectionsMap.getForward(generalSectionName)
- } else generalSectionName
+ } else {
+ generalSectionName
+ }
}
private fun mapSectionNameToIni(generalSectionName: String): String {
return if (sectionsMap.getBackward(generalSectionName) != null) {
sectionsMap.getBackward(generalSectionName).toString()
- } else generalSectionName
+ } else {
+ generalSectionName
+ }
}
fun getSettingsFile(fileName: String): File {
@@ -237,5 +244,21 @@ object SettingsFile {
val setting = settings[key]
parser.put(header, setting!!.key, setting.valueAsString)
}
+
+ BooleanSetting.values().forEach {
+ if (!keySet.contains(it.key)) {
+ parser.put(header, it.key, it.valueAsString)
+ }
+ }
+ IntSetting.values().forEach {
+ if (!keySet.contains(it.key)) {
+ parser.put(header, it.key, it.valueAsString)
+ }
+ }
+ StringSetting.values().forEach {
+ if (!keySet.contains(it.key)) {
+ parser.put(header, it.key, it.valueAsString)
+ }
+ }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
index c92e2755c..2ff827c6b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
@@ -66,7 +66,11 @@ class AboutFragment : Fragment() {
true
}
- binding.buttonContributors.setOnClickListener { openLink(getString(R.string.contributors_link)) }
+ binding.buttonContributors.setOnClickListener {
+ openLink(
+ getString(R.string.contributors_link)
+ )
+ }
binding.buttonLicenses.setOnClickListener {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
@@ -101,7 +105,9 @@ class AboutFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
index d8bbc1ce4..dbc16da4a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EarlyAccessFragment.kt
@@ -49,7 +49,11 @@ class EarlyAccessFragment : Fragment() {
parentFragmentManager.primaryNavigationFragment?.findNavController()?.popBackStack()
}
- binding.getEarlyAccessButton.setOnClickListener { openLink(getString(R.string.play_store_link)) }
+ binding.getEarlyAccessButton.setOnClickListener {
+ openLink(
+ getString(R.string.play_store_link)
+ )
+ }
setInsets()
}
@@ -60,7 +64,9 @@ class EarlyAccessFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 4b2305892..4643418c1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -83,21 +83,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
onReturnFromSettings = context.activityResultRegistry.register(
- "SettingsResult", ActivityResultContracts.StartActivityForResult()
- ) {
- binding.surfaceEmulation.setAspectRatio(
- when (IntSetting.RENDERER_ASPECT_RATIO.int) {
- 0 -> Rational(16, 9)
- 1 -> Rational(4, 3)
- 2 -> Rational(21, 9)
- 3 -> Rational(16, 10)
- 4 -> null // Stretch
- else -> Rational(16, 9)
- }
- )
- emulationActivity?.buildPictureInPictureParams()
- updateScreenLayout()
- }
+ "SettingsResult",
+ ActivityResultContracts.StartActivityForResult()
+ ) { updateScreenLayout() }
} else {
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
}
@@ -191,9 +179,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
requireActivity(),
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
- if (binding.drawerLayout.isOpen) binding.drawerLayout.close() else binding.drawerLayout.open()
+ if (binding.drawerLayout.isOpen) {
+ binding.drawerLayout.close()
+ } else {
+ binding.drawerLayout.open()
+ }
}
- })
+ }
+ )
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -236,17 +229,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
DirectoryInitialization.start(requireContext())
}
- binding.surfaceEmulation.setAspectRatio(
- when (IntSetting.RENDERER_ASPECT_RATIO.int) {
- 0 -> Rational(16, 9)
- 1 -> Rational(4, 3)
- 2 -> Rational(21, 9)
- 3 -> Rational(16, 10)
- 4 -> null // Stretch
- else -> Rational(16, 9)
- }
- )
-
updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated)
@@ -309,44 +291,66 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
@SuppressLint("SourceLockedOrientationActivity")
- private fun updateScreenLayout() {
+ private fun updateOrientation() {
emulationActivity?.let {
it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.int) {
- Settings.LayoutOption_MobileLandscape -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
- Settings.LayoutOption_MobilePortrait -> ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
+ Settings.LayoutOption_MobileLandscape ->
+ ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
+ Settings.LayoutOption_MobilePortrait ->
+ ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
Settings.LayoutOption_Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
}
}
- onConfigurationChanged(resources.configuration)
}
- private fun updateFoldableLayout(emulationActivity: EmulationActivity, newLayoutInfo: WindowLayoutInfo) {
- val isFolding = (newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let {
- if (it.isSeparating) {
- emulationActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
- if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) {
- // Restrict emulation and overlays to the top of the screen
- binding.emulationContainer.layoutParams.height = it.bounds.top
- binding.overlayContainer.layoutParams.height = it.bounds.top
- // Restrict input and menu drawer to the bottom of the screen
- binding.inputContainer.layoutParams.height = it.bounds.bottom
- binding.inGameMenu.layoutParams.height = it.bounds.bottom
-
- isInFoldableLayout = true
- binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE
- refreshInputOverlay()
- }
+ private fun updateScreenLayout() {
+ binding.surfaceEmulation.setAspectRatio(
+ when (IntSetting.RENDERER_ASPECT_RATIO.int) {
+ 0 -> Rational(16, 9)
+ 1 -> Rational(4, 3)
+ 2 -> Rational(21, 9)
+ 3 -> Rational(16, 10)
+ 4 -> null // Stretch
+ else -> Rational(16, 9)
}
- it.isSeparating
- } ?: false
+ )
+ emulationActivity?.buildPictureInPictureParams()
+ updateOrientation()
+ }
+
+ private fun updateFoldableLayout(
+ emulationActivity: EmulationActivity,
+ newLayoutInfo: WindowLayoutInfo
+ ) {
+ val isFolding =
+ (newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let {
+ if (it.isSeparating) {
+ emulationActivity.requestedOrientation =
+ ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) {
+ // Restrict emulation and overlays to the top of the screen
+ binding.emulationContainer.layoutParams.height = it.bounds.top
+ binding.overlayContainer.layoutParams.height = it.bounds.top
+ // Restrict input and menu drawer to the bottom of the screen
+ binding.inputContainer.layoutParams.height = it.bounds.bottom
+ binding.inGameMenu.layoutParams.height = it.bounds.bottom
+
+ isInFoldableLayout = true
+ binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE
+ refreshInputOverlay()
+ }
+ }
+ it.isSeparating
+ } ?: false
if (!isFolding) {
binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
isInFoldableLayout = false
- updateScreenLayout()
+ updateOrientation()
+ onConfigurationChanged(resources.configuration)
}
binding.emulationContainer.requestLayout()
binding.inputContainer.requestLayout()
@@ -516,18 +520,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
inputScaleSlider.apply {
valueTo = 150F
value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
- addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
- inputScaleValue.text = "${value.toInt()}%"
- setControlScale(value.toInt())
- })
+ addOnChangeListener(
+ Slider.OnChangeListener { _, value, _ ->
+ inputScaleValue.text = "${value.toInt()}%"
+ setControlScale(value.toInt())
+ }
+ )
}
inputOpacitySlider.apply {
valueTo = 100F
value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat()
- addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
- inputOpacityValue.text = "${value.toInt()}%"
- setControlOpacity(value.toInt())
- })
+ addOnChangeListener(
+ Slider.OnChangeListener { _, value, _ ->
+ inputOpacityValue.text = "${value.toInt()}%"
+ setControlOpacity(value.toInt())
+ }
+ )
}
inputScaleValue.text = "${inputScaleSlider.value.toInt()}%"
inputOpacityValue.text = "${inputOpacitySlider.value.toInt()}%"
@@ -559,7 +567,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
private fun setInsets() {
- ViewCompat.setOnApplyWindowInsetsListener(binding.inGameMenu) { v: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.inGameMenu
+ ) { v: View, windowInsets: WindowInsetsCompat ->
val cutInsets: Insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
var left = 0
var right = 0
@@ -679,8 +689,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
state = State.PAUSED
}
- State.PAUSED -> Log.warning("[EmulationFragment] Surface cleared while emulation paused.")
- else -> Log.warning("[EmulationFragment] Surface cleared while emulation stopped.")
+ State.PAUSED -> Log.warning(
+ "[EmulationFragment] Surface cleared while emulation paused."
+ )
+ else -> Log.warning(
+ "[EmulationFragment] Surface cleared while emulation stopped."
+ )
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 536163eb6..6f8adbba5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -103,7 +103,9 @@ class HomeSettingsFragment : Fragment() {
R.string.select_games_folder,
R.string.select_games_folder_description,
R.drawable.ic_add
- ) { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) },
+ ) {
+ mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
+ },
HomeSetting(
R.string.manage_save_data,
R.string.import_export_saves_description,
@@ -225,7 +227,11 @@ class HomeSettingsFragment : Fragment() {
val intent = Intent(action)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.data = DocumentsContract.buildRootUri(authority, DocumentProvider.ROOT_ID)
- intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ intent.addFlags(
+ Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
+ Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ )
return intent
}
@@ -307,7 +313,9 @@ class HomeSettingsFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
index 36e63bb9e..e1495ee8c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
@@ -15,6 +15,14 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import java.io.BufferedOutputStream
+import java.io.File
+import java.io.FileOutputStream
+import java.io.FilenameFilter
+import java.time.LocalDateTime
+import java.time.format.DateTimeFormatter
+import java.util.zip.ZipEntry
+import java.util.zip.ZipOutputStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -24,14 +32,6 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.getPublicFilesDir
import org.yuzu.yuzu_emu.utils.FileUtil
-import java.io.BufferedOutputStream
-import java.io.File
-import java.io.FileOutputStream
-import java.io.FilenameFilter
-import java.time.LocalDateTime
-import java.time.format.DateTimeFormatter
-import java.util.zip.ZipEntry
-import java.util.zip.ZipOutputStream
class ImportExportSavesFragment : DialogFragment() {
private val context = YuzuApplication.appContext
@@ -98,7 +98,7 @@ class ImportExportSavesFragment : DialogFragment() {
val outputZipFile = File(
tempFolder,
"yuzu saves - ${
- LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
}.zip"
)
outputZipFile.createNewFile()
@@ -106,12 +106,14 @@ class ImportExportSavesFragment : DialogFragment() {
saveFolder.walkTopDown().forEach { file ->
val zipFileName =
file.absolutePath.removePrefix(savesFolderRoot).removePrefix("/")
- if (zipFileName == "")
+ if (zipFileName == "") {
return@forEach
+ }
val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}")
zos.putNextEntry(entry)
- if (file.isFile)
+ if (file.isFile) {
file.inputStream().use { fis -> fis.copyTo(zos) }
+ }
}
}
lastZipCreated = outputZipFile
@@ -137,7 +139,8 @@ class ImportExportSavesFragment : DialogFragment() {
withContext(Dispatchers.Main) {
val file = DocumentFile.fromSingleUri(
- context, DocumentsContract.buildDocumentUri(
+ context,
+ DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
index c7880d8cc..739b26f99 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
@@ -14,7 +14,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.TaskViewModel
-
class IndeterminateProgressDialogFragment : DialogFragment() {
private val taskViewModel: TaskViewModel by activityViewModels()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
index 59141e823..b6e9129f7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
@@ -113,7 +113,9 @@ class LicensesFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
index adbe3696b..dd6c895fd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
@@ -20,6 +20,7 @@ import androidx.fragment.app.activityViewModels
import androidx.preference.PreferenceManager
import info.debatty.java.stringsimilarity.Jaccard
import info.debatty.java.stringsimilarity.JaroWinkler
+import java.util.Locale
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.GameAdapter
@@ -29,8 +30,6 @@ import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
-import org.yuzu.yuzu_emu.utils.Log
-import java.util.Locale
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
@@ -130,15 +129,15 @@ class SearchFragment : Fragment() {
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
R.id.chip_retail -> baseList.filter {
- FileUtil.hasExtension(it.path, "xci")
- || FileUtil.hasExtension(it.path, "nsp")
+ FileUtil.hasExtension(it.path, "xci") ||
+ FileUtil.hasExtension(it.path, "nsp")
}
else -> baseList
}
- if (binding.searchText.text.toString().isEmpty()
- && binding.chipGroup.checkedChipId != View.NO_ID
+ if (binding.searchText.text.toString().isEmpty() &&
+ binding.chipGroup.checkedChipId != View.NO_ID
) {
gamesViewModel.setSearchedGames(filteredList)
return
@@ -173,14 +172,16 @@ class SearchFragment : Fragment() {
private fun focusSearch() {
if (_binding != null) {
binding.searchText.requestFocus()
- val imm =
- requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
+ val imm = requireActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT)
}
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index 258773380..6c4ddaf6b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -25,6 +25,7 @@ import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.google.android.material.transition.MaterialFadeThrough
+import java.io.File
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.SetupAdapter
@@ -35,7 +36,6 @@ import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.GameHelper
-import java.io.File
class SetupFragment : Fragment() {
private var _binding: FragmentSetupBinding? = null
@@ -82,7 +82,8 @@ class SetupFragment : Fragment() {
requireActivity().finish()
}
}
- })
+ }
+ )
requireActivity().window.navigationBarColor =
ContextCompat.getColor(requireContext(), android.R.color.transparent)
@@ -148,14 +149,20 @@ class SetupFragment : Fragment() {
R.drawable.ic_add,
true,
R.string.add_games,
- { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) },
+ {
+ mainActivity.getGamesDirectory.launch(
+ Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
+ )
+ },
true,
R.string.add_games_warning,
R.string.add_games_warning_description,
R.string.add_games_warning_help,
{
val preferences =
- PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
+ PreferenceManager.getDefaultSharedPreferences(
+ YuzuApplication.appContext
+ )
preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()
}
)
@@ -260,7 +267,9 @@ class SetupFragment : Fragment() {
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
- if (!it && !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
+ if (!it &&
+ !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
+ ) {
PermissionDeniedDialogFragment().show(
childFragmentManager,
PermissionDeniedDialogFragment.TAG
@@ -315,7 +324,9 @@ class SetupFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
view.setPadding(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt
index be5e4c86c..bdd6ea628 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/AutofitGridLayoutManager.kt
@@ -44,7 +44,9 @@ class AutofitGridLayoutManager(
override fun onLayoutChildren(recycler: Recycler, state: RecyclerView.State) {
val width = width
val height = height
- if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) {
+ if (columnWidth > 0 && width > 0 && height > 0 &&
+ (isColumnWidthChanged || lastWidth != width || lastHeight != height)
+ ) {
val totalSpace: Int = if (orientation == VERTICAL) {
width - paddingRight - paddingLeft
} else {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
index 35d8000c5..6a048e39f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
@@ -4,9 +4,9 @@
package org.yuzu.yuzu_emu.model
import android.os.Parcelable
+import java.util.HashSet
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
-import java.util.HashSet
@Parcelize
@Serializable
@@ -23,8 +23,9 @@ class Game(
val keyLastPlayedTime get() = "${gameId}_LastPlayed"
override fun equals(other: Any?): Boolean {
- if (other !is Game)
+ if (other !is Game) {
return false
+ }
return hashCode() == other.hashCode()
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index d9b301210..1fe42f922 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -10,6 +10,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager
+import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -20,7 +21,6 @@ import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.GameHelper
-import java.util.Locale
@OptIn(ExperimentalSerializationApi::class)
class GamesViewModel : ViewModel() {
@@ -99,8 +99,9 @@ class GamesViewModel : ViewModel() {
}
fun reloadGames(directoryChanged: Boolean) {
- if (isReloading.value == true)
+ if (isReloading.value == true) {
return
+ }
_isReloading.postValue(true)
viewModelScope.launch {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
index d12d08e9f..6251ec783 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.overlay
import android.app.Activity
import android.content.Context
import android.content.SharedPreferences
-import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Point
@@ -24,6 +23,8 @@ import android.view.WindowInsets
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import androidx.window.layout.WindowMetricsCalculator
+import kotlin.math.max
+import kotlin.math.min
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.NativeLibrary.ButtonType
import org.yuzu.yuzu_emu.NativeLibrary.StickType
@@ -31,14 +32,13 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
-import kotlin.math.max
-import kotlin.math.min
/**
* Draws the interactive input overlay on top of the
* [SurfaceView] that is rendering emulation.
*/
-class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context, attrs),
+class InputOverlay(context: Context, attrs: AttributeSet?) :
+ SurfaceView(context, attrs),
OnTouchListener {
private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet()
private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet()
@@ -95,7 +95,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
var shouldUpdateView = false
val playerIndex =
- if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device
+ if (NativeLibrary.isHandheldOnly()) {
+ NativeLibrary.ConsoleDevice
+ } else {
+ NativeLibrary.Player1Device
+ }
for (button in overlayButtons) {
if (!button.updateStatus(event)) {
@@ -158,8 +162,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
shouldUpdateView = true
}
- if (shouldUpdateView)
+ if (shouldUpdateView) {
invalidate()
+ }
if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) {
return true
@@ -243,9 +248,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// If no button is being moved now, remember the currently touched button to move.
if (buttonBeingConfigured == null &&
button.bounds.contains(
- fingerPositionX,
- fingerPositionY
- )
+ fingerPositionX,
+ fingerPositionY
+ )
) {
buttonBeingConfigured = button
buttonBeingConfigured!!.onConfigureTouch(event)
@@ -309,9 +314,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null &&
joystick.bounds.contains(
- fingerPositionX,
- fingerPositionY
- )
+ fingerPositionX,
+ fingerPositionY
+ )
) {
joystickBeingConfigured = joystick
joystickBeingConfigured!!.onConfigureTouch(event)
@@ -668,7 +673,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
R.integer.SWITCH_STICK_L_Y_FOLDABLE
)
- private fun getResourceValue(orientation: String, position: Int) : Float {
+ private fun getResourceValue(orientation: String, position: Int): Float {
return when (orientation) {
PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
@@ -820,7 +825,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
* @param context Context for getting the vector drawable
* @param drawableId The ID of the drawable to scale.
* @param scale The scale factor for the bitmap.
- * @return The scaled [Bitmap]
+ * @return The scaled [Bitmap]
*/
private fun getBitmap(context: Context, drawableId: Int, scale: Float): Bitmap {
val vectorDrawable = ContextCompat.getDrawable(context, drawableId) as VectorDrawable
@@ -854,7 +859,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
* Gets the safe screen size for drawing the overlay
*
* @param context Context for getting the window metrics
- * @return A pair of points, the first being the top left corner of the safe area,
+ * @return A pair of points, the first being the top left corner of the safe area,
* the second being the bottom right corner of the safe area
*/
private fun getSafeScreenSize(context: Context): Pair<Point, Point> {
@@ -872,10 +877,16 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
if (insets != null) {
- if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2)
+ if (insets.boundingRectTop.bottom != 0 &&
+ insets.boundingRectTop.bottom > maxY / 2
+ ) {
maxY = insets.boundingRectTop.bottom.toFloat()
- if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2)
+ }
+ if (insets.boundingRectRight.left != 0 &&
+ insets.boundingRectRight.left > maxX / 2
+ ) {
maxX = insets.boundingRectRight.left.toFloat()
+ }
minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
index 43d664d21..8aef6f5a5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
@@ -133,7 +133,10 @@ class InputOverlayDrawableDpad(
downButtonState = axisY > VIRT_AXIS_DEADZONE
leftButtonState = axisX < -VIRT_AXIS_DEADZONE
rightButtonState = axisX > VIRT_AXIS_DEADZONE
- return oldUpState != upButtonState || oldDownState != downButtonState || oldLeftState != leftButtonState || oldRightState != rightButtonState
+ return oldUpState != upButtonState ||
+ oldDownState != downButtonState ||
+ oldLeftState != leftButtonState ||
+ oldRightState != rightButtonState
}
return false
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
index f1d32192a..fb48f584d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
@@ -9,12 +9,12 @@ import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.view.MotionEvent
-import org.yuzu.yuzu_emu.NativeLibrary
-import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
+import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
/**
* Custom [BitmapDrawable] that is capable
@@ -241,14 +241,22 @@ class InputOverlayDrawableJoystick(
private fun setInnerBounds() {
var x = virtBounds.centerX() + (xAxis * (virtBounds.width() / 2)).toInt()
var y = virtBounds.centerY() + (yAxis * (virtBounds.height() / 2)).toInt()
- if (x > virtBounds.centerX() + virtBounds.width() / 2) x =
- virtBounds.centerX() + virtBounds.width() / 2
- if (x < virtBounds.centerX() - virtBounds.width() / 2) x =
- virtBounds.centerX() - virtBounds.width() / 2
- if (y > virtBounds.centerY() + virtBounds.height() / 2) y =
- virtBounds.centerY() + virtBounds.height() / 2
- if (y < virtBounds.centerY() - virtBounds.height() / 2) y =
- virtBounds.centerY() - virtBounds.height() / 2
+ if (x > virtBounds.centerX() + virtBounds.width() / 2) {
+ x =
+ virtBounds.centerX() + virtBounds.width() / 2
+ }
+ if (x < virtBounds.centerX() - virtBounds.width() / 2) {
+ x =
+ virtBounds.centerX() - virtBounds.width() / 2
+ }
+ if (y > virtBounds.centerY() + virtBounds.height() / 2) {
+ y =
+ virtBounds.centerY() + virtBounds.height() / 2
+ }
+ if (y < virtBounds.centerY() - virtBounds.height() / 2) {
+ y =
+ virtBounds.centerY() - virtBounds.height() / 2
+ }
val width = pressedStateInnerBitmap.bounds.width() / 2
val height = pressedStateInnerBitmap.bounds.height() / 2
defaultStateInnerBitmap.setBounds(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index 97eef40d2..b0156dca5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -99,7 +99,9 @@ class GamesFragment : Fragment() {
}
shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData ->
if (shouldSwapData) {
- (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value!!)
+ (binding.gridGames.adapter as GameAdapter).submitList(
+ gamesViewModel.games.value!!
+ )
gamesViewModel.setShouldSwapData(false)
}
}
@@ -128,7 +130,9 @@ class GamesFragment : Fragment() {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_large)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 041d16f3a..cc1d87f1b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -26,6 +26,9 @@ import androidx.preference.PreferenceManager
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationBarView
+import java.io.File
+import java.io.FilenameFilter
+import java.io.IOException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -43,9 +46,6 @@ import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.*
-import java.io.File
-import java.io.FilenameFilter
-import java.io.IOException
class MainActivity : AppCompatActivity(), ThemeProvider {
private lateinit var binding: ActivityMainBinding
@@ -86,7 +86,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
ThemeHelper.SYSTEM_BAR_ALPHA
)
)
- if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) {
+ if (InsetsHelper.getSystemGestureType(applicationContext) !=
+ InsetsHelper.GESTURE_NAVIGATION
+ ) {
binding.navigationBarShade.setBackgroundColor(
ThemeHelper.getColorWithOpacity(
MaterialColors.getColor(
@@ -172,7 +174,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding.navigationView.height.toFloat() * 2
translationY(0f)
} else {
- if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) {
+ if (ViewCompat.getLayoutDirection(binding.navigationView) ==
+ ViewCompat.LAYOUT_DIRECTION_LTR
+ ) {
binding.navigationView.translationX =
binding.navigationView.width.toFloat() * -2
translationX(0f)
@@ -189,7 +193,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (smallLayout) {
translationY(binding.navigationView.height.toFloat() * 2)
} else {
- if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) {
+ if (ViewCompat.getLayoutDirection(binding.navigationView) ==
+ ViewCompat.LAYOUT_DIRECTION_LTR
+ ) {
translationX(binding.navigationView.width.toFloat() * -2)
} else {
translationX(binding.navigationView.width.toFloat() * 2)
@@ -234,7 +240,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
private fun setInsets() =
- ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
+ ViewCompat.setOnApplyWindowInsetsListener(
+ binding.root
+ ) { _: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val mlpStatusShade = binding.statusBarShade.layoutParams as MarginLayoutParams
mlpStatusShade.height = insets.top
@@ -256,8 +264,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
contentResolver.takePersistableUriPermission(
result,
@@ -281,8 +290,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
if (!FileUtil.hasExtension(result, "keys")) {
MessageDialogFragment.newInstance(
@@ -324,8 +334,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getFirmware =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
val inputZip = contentResolver.openInputStream(result)
if (inputZip == null) {
@@ -376,8 +387,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getAmiiboKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
if (!FileUtil.hasExtension(result, "bin")) {
MessageDialogFragment.newInstance(
@@ -418,8 +430,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getDriver =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
- if (result == null)
+ if (result == null) {
return@registerForActivityResult
+ }
val takeFlags =
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -470,8 +483,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val installGameUpdate =
registerForActivityResult(ActivityResultContracts.OpenDocument()) {
- if (it == null)
+ if (it == null) {
return@registerForActivityResult
+ }
IndeterminateProgressDialogFragment.newInstance(
this@MainActivity,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt
index 791cea904..eeefcdf20 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ControllerMappingHelper.kt
@@ -19,7 +19,9 @@ class ControllerMappingHelper {
// The two analog triggers generate analog motion events as well as a keycode.
// We always prefer to use the analog values, so throw away the button press
keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2
- } else false
+ } else {
+ false
+ }
}
/**
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
index 36c479e6c..2ee63697e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
@@ -4,8 +4,8 @@
package org.yuzu.yuzu_emu.utils
import android.content.Context
-import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException
+import org.yuzu.yuzu_emu.NativeLibrary
object DirectoryInitialization {
private var userPath: String? = null
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt
index f8abae445..cf226ad94 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt
@@ -5,10 +5,10 @@ package org.yuzu.yuzu_emu.utils
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
-import org.yuzu.yuzu_emu.YuzuApplication
-import org.yuzu.yuzu_emu.model.MinimalDocumentFile
import java.io.File
import java.util.*
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.model.MinimalDocumentFile
class DocumentsTree {
private var root: DocumentsNode? = null
@@ -29,7 +29,9 @@ class DocumentsTree {
val node = resolvePath(filepath)
return if (node == null || node.isDirectory) {
0
- } else FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
+ } else {
+ FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
+ }
}
fun exists(filepath: String): Boolean {
@@ -111,7 +113,9 @@ class DocumentsTree {
fun isNativePath(path: String): Boolean {
return if (path.isNotEmpty()) {
path[0] == '/'
- } else false
+ } else {
+ false
+ }
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
index 492b1ad91..9f3bbe56f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
@@ -9,8 +9,6 @@ import android.net.Uri
import android.provider.DocumentsContract
import android.provider.OpenableColumns
import androidx.documentfile.provider.DocumentFile
-import org.yuzu.yuzu_emu.YuzuApplication
-import org.yuzu.yuzu_emu.model.MinimalDocumentFile
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
@@ -19,6 +17,8 @@ import java.io.InputStream
import java.net.URLDecoder
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.model.MinimalDocumentFile
object FileUtil {
const val PATH_TREE = "tree"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
index dc9b7c744..086d17606 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
@@ -54,7 +54,7 @@ class ForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) {
- return START_NOT_STICKY;
+ return START_NOT_STICKY
}
if (intent.action == ACTION_STOP) {
NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
index 42b207618..ee9f3e570 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
@@ -6,12 +6,12 @@ package org.yuzu.yuzu_emu.utils
import android.content.SharedPreferences
import android.net.Uri
import androidx.preference.PreferenceManager
+import java.util.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.Game
-import java.util.*
object GameHelper {
const val KEY_GAME_PATH = "game_path"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
index 528011d7f..dad159481 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
@@ -5,14 +5,14 @@ package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.net.Uri
-import org.yuzu.yuzu_emu.NativeLibrary
-import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.zip.ZipInputStream
+import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
object GpuDriverHelper {
private const val META_JSON_FILENAME = "meta.json"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt
index 70bdb4097..a4e64070a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt
@@ -3,12 +3,12 @@
package org.yuzu.yuzu_emu.utils
-import org.json.JSONException
-import org.json.JSONObject
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
+import org.json.JSONException
+import org.json.JSONObject
class GpuDriverMetadata(metadataFilePath: String) {
var name: String? = null
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
index 24e999b29..e963dfbc1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
@@ -5,8 +5,8 @@ package org.yuzu.yuzu_emu.utils
import android.view.KeyEvent
import android.view.MotionEvent
-import org.yuzu.yuzu_emu.NativeLibrary
import kotlin.math.sqrt
+import org.yuzu.yuzu_emu.NativeLibrary
class InputHandler {
fun initialize() {
@@ -68,7 +68,11 @@ class InputHandler {
6 -> NativeLibrary.Player6Device
7 -> NativeLibrary.Player7Device
8 -> NativeLibrary.Player8Device
- else -> if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device
+ else -> if (NativeLibrary.isHandheldOnly()) {
+ NativeLibrary.ConsoleDevice
+ } else {
+ NativeLibrary.Player1Device
+ }
}
}
@@ -107,7 +111,11 @@ class InputHandler {
}
private fun getAxisToButton(axis: Float): Int {
- return if (axis > 0.5f) NativeLibrary.ButtonState.PRESSED else NativeLibrary.ButtonState.RELEASED
+ return if (axis > 0.5f) {
+ NativeLibrary.ButtonState.PRESSED
+ } else {
+ NativeLibrary.ButtonState.RELEASED
+ }
}
private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) {
@@ -287,7 +295,6 @@ class InputHandler {
}
}
-
private fun setJoyconAxisInput(event: MotionEvent, axis: Int) {
// Joycon support is half dead. Right joystick doesn't work
val playerNumber = getPlayerNumber(event.device.controllerNumber)
@@ -355,6 +362,4 @@ class InputHandler {
)
}
}
-
-
-} \ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
index 19c53c481..595f0d284 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt
@@ -4,9 +4,7 @@
package org.yuzu.yuzu_emu.utils
import android.annotation.SuppressLint
-import android.app.Activity
import android.content.Context
-import android.graphics.Rect
object InsetsHelper {
const val THREE_BUTTON_NAVIGATION = 0
@@ -20,12 +18,8 @@ object InsetsHelper {
resources.getIdentifier("config_navBarInteractionMode", "integer", "android")
return if (resourceId != 0) {
resources.getInteger(resourceId)
- } else 0
- }
-
- fun getBottomPaddingRequired(activity: Activity): Int {
- val visibleFrame = Rect()
- activity.window.decorView.getWindowVisibleDisplayFrame(visibleFrame)
- return visibleFrame.bottom - visibleFrame.top - activity.resources.displayMetrics.heightPixels
+ } else {
+ 0
+ }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
index 344dd8a0a..68ed66565 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
@@ -13,8 +13,8 @@ import android.nfc.tech.NfcA
import android.os.Build
import android.os.Handler
import android.os.Looper
-import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException
+import org.yuzu.yuzu_emu.NativeLibrary
class NfcReader(private val activity: Activity) {
private var nfcAdapter: NfcAdapter? = null
@@ -25,10 +25,13 @@ class NfcReader(private val activity: Activity) {
pendingIntent = PendingIntent.getActivity(
activity,
- 0, Intent(activity, activity.javaClass),
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
+ 0,
+ Intent(activity, activity.javaClass),
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
- else PendingIntent.FLAG_UPDATE_CURRENT
+ } else {
+ PendingIntent.FLAG_UPDATE_CURRENT
+ }
)
val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
@@ -45,9 +48,9 @@ class NfcReader(private val activity: Activity) {
fun onNewIntent(intent: Intent) {
val action = intent.action
- if (NfcAdapter.ACTION_TAG_DISCOVERED != action
- && NfcAdapter.ACTION_TECH_DISCOVERED != action
- && NfcAdapter.ACTION_NDEF_DISCOVERED != action
+ if (NfcAdapter.ACTION_TAG_DISCOVERED != action &&
+ NfcAdapter.ACTION_TECH_DISCOVERED != action &&
+ NfcAdapter.ACTION_NDEF_DISCOVERED != action
) {
return
}
@@ -84,7 +87,7 @@ class NfcReader(private val activity: Activity) {
}
private fun ntag215ReadAll(amiibo: NfcA): ByteArray? {
- val bufferSize = amiibo.maxTransceiveLength;
+ val bufferSize = amiibo.maxTransceiveLength
val tagSize = 0x21C
val pageSize = 4
val lastPage = tagSize / pageSize - 1
@@ -103,7 +106,7 @@ class NfcReader(private val activity: Activity) {
val data = ntag215FastRead(amiibo, dataStart, dataEnd - 1)
System.arraycopy(data, 0, tagData, i, (dataEnd - dataStart) * pageSize)
} catch (e: IOException) {
- return null;
+ return null
}
}
return tagData
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
index 87ee7f2e6..00e58faec 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/SerializableHelper.kt
@@ -11,30 +11,34 @@ import java.io.Serializable
object SerializableHelper {
inline fun <reified T : Serializable> Bundle.serializable(key: String): T? {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getSerializable(key, T::class.java)
- else
+ } else {
getSerializable(key) as? T
+ }
}
inline fun <reified T : Serializable> Intent.serializable(key: String): T? {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getSerializableExtra(key, T::class.java)
- else
+ } else {
getSerializableExtra(key) as? T
+ }
}
inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelable(key, T::class.java)
- else
+ } else {
getParcelable(key) as? T
+ }
}
inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelableExtra(key, T::class.java)
- else
+ } else {
getParcelableExtra(key) as? T
+ }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
index e55767c0f..f312e24cf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
@@ -3,21 +3,19 @@
package org.yuzu.yuzu_emu.utils
-import android.app.Activity
import android.content.res.Configuration
import android.graphics.Color
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
-import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.preference.PreferenceManager
+import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.ui.main.ThemeProvider
-import kotlin.math.roundToInt
object ThemeHelper {
const val SYSTEM_BAR_ALPHA = 0.9f
@@ -36,8 +34,8 @@ object ThemeHelper {
// Using a specific night mode check because this could apply incorrectly when using the
// light app mode, dark system mode, and black backgrounds. Launching the settings activity
// will then show light mode colors/navigation bars but with black backgrounds.
- if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
- && isNightMode(activity)
+ if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) &&
+ isNightMode(activity)
) {
activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark)
}
@@ -46,8 +44,10 @@ object ThemeHelper {
@ColorInt
fun getColorWithOpacity(@ColorInt color: Int, alphaFactor: Float): Int {
return Color.argb(
- (alphaFactor * Color.alpha(color)).roundToInt(), Color.red(color),
- Color.green(color), Color.blue(color)
+ (alphaFactor * Color.alpha(color)).roundToInt(),
+ Color.red(color),
+ Color.green(color),
+ Color.blue(color)
)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
index d89a89983..685ccaa76 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
@@ -38,8 +38,8 @@ class FixedRatioSurfaceView @JvmOverloads constructor(
newWidth = width
newHeight = (width / aspectRatio).roundToInt()
}
- val left = (width - newWidth) / 2;
- val top = (height - newHeight) / 2;
+ val left = (width - newWidth) / 2
+ val top = (height - newHeight) / 2
setLeftTopRightBottom(left, top, left + newWidth, top + newHeight)
} else {
setLeftTopRightBottom(0, 0, width, height)
diff --git a/src/android/app/src/main/res/layout/list_item_setting_switch.xml b/src/android/app/src/main/res/layout/list_item_setting_switch.xml
index 599d845ad..a5767adee 100644
--- a/src/android/app/src/main/res/layout/list_item_setting_switch.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting_switch.xml
@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:minHeight="72dp"
+ android:paddingVertical="@dimen/spacing_large"
android:paddingStart="@dimen/spacing_large"
- android:paddingEnd="24dp"
- android:paddingVertical="@dimen/spacing_large">
+ android:paddingEnd="24dp">
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_widget"
@@ -19,32 +19,35 @@
android:layout_alignParentEnd="true"
android:layout_centerVertical="true" />
- <com.google.android.material.textview.MaterialTextView
- style="@style/TextAppearance.Material3.BodySmall"
- android:id="@+id/text_setting_description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_alignStart="@+id/text_setting_name"
- android:layout_below="@+id/text_setting_name"
- android:layout_marginEnd="@dimen/spacing_large"
- android:layout_marginTop="@dimen/spacing_small"
- android:layout_toStartOf="@+id/switch_widget"
- android:textAlignment="viewStart"
- tools:text="@string/frame_limit_enable_description" />
-
- <com.google.android.material.textview.MaterialTextView
- style="@style/TextAppearance.Material3.HeadlineMedium"
- android:id="@+id/text_setting_name"
- android:layout_width="0dp"
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
+ android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/spacing_large"
android:layout_toStartOf="@+id/switch_widget"
- android:textSize="16sp"
- android:textAlignment="viewStart"
- app:lineHeight="28dp"
- tools:text="@string/frame_limit_enable" />
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/text_setting_name"
+ style="@style/TextAppearance.Material3.HeadlineMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart"
+ android:textSize="16sp"
+ app:lineHeight="28dp"
+ tools:text="@string/frame_limit_enable" />
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/text_setting_description"
+ style="@style/TextAppearance.Material3.BodySmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/spacing_small"
+ android:textAlignment="viewStart"
+ tools:text="@string/frame_limit_enable_description" />
+
+ </LinearLayout>
</RelativeLayout>
diff --git a/src/android/app/src/main/res/layout/list_item_settings_header.xml b/src/android/app/src/main/res/layout/list_item_settings_header.xml
index abd24df6f..cf85bc0da 100644
--- a/src/android/app/src/main/res/layout/list_item_settings_header.xml
+++ b/src/android/app/src/main/res/layout/list_item_settings_header.xml
@@ -1,20 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.google.android.material.textview.MaterialTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/text_header_name"
+ style="@style/TextAppearance.Material3.TitleSmall"
android:layout_width="match_parent"
- android:layout_height="48dp"
- android:paddingVertical="4dp"
- android:paddingHorizontal="@dimen/spacing_large">
-
- <com.google.android.material.textview.MaterialTextView
- style="@style/TextAppearance.Material3.TitleSmall"
- android:id="@+id/text_header_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:textColor="?attr/colorPrimary"
- android:textAlignment="viewStart"
- android:textStyle="bold"
- tools:text="CPU Settings" />
-
-</FrameLayout>
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:paddingHorizontal="@dimen/spacing_large"
+ android:paddingVertical="16dp"
+ android:textAlignment="viewStart"
+ android:textColor="?attr/colorPrimary"
+ android:textStyle="bold"
+ tools:text="CPU Settings" />
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 7f7b1938c..6d092f7a9 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -236,4 +236,15 @@
<item>2</item>
</integer-array>
+ <string-array name="outputEngineEntries">
+ <item>@string/auto</item>
+ <item>@string/cubeb</item>
+ <item>@string/string_null</item>
+ </string-array>
+ <string-array name="outputEngineValues">
+ <item>auto</item>
+ <item>cubeb</item>
+ <item>null</item>
+ </string-array>
+
</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index b5bc249d4..cc1d8c39d 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<resources>
+<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<!-- General application strings -->
<string name="app_name" translatable="false">yuzu</string>
@@ -158,7 +158,6 @@
<string name="set_custom_rtc">Set custom RTC</string>
<!-- Graphics settings strings -->
- <string name="renderer_api">API</string>
<string name="renderer_accuracy">Accuracy level</string>
<string name="renderer_resolution">Resolution (Handheld/Docked)</string>
<string name="renderer_vsync">VSync mode</string>
@@ -172,12 +171,21 @@
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously, reducing stutter but may introduce glitches.</string>
<string name="renderer_reactive_flushing">Use reactive flushing</string>
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
- <string name="renderer_debug">Graphics debugging</string>
- <string name="renderer_debug_description">Sets the graphics API to a slow debugging mode.</string>
<string name="use_disk_shader_cache">Disk shader cache</string>
<string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string>
+ <!-- Debug settings strings -->
+ <string name="cpu">CPU</string>
+ <string name="cpu_debug_mode">CPU Debugging</string>
+ <string name="cpu_debug_mode_description">Puts the CPU in a slow debugging mode.</string>
+ <string name="gpu">GPU</string>
+ <string name="renderer_api">API</string>
+ <string name="renderer_debug">Graphics debugging</string>
+ <string name="renderer_debug_description">Sets the graphics API to a slow debugging mode.</string>
+ <string name="fastmem">Fastmem</string>
+
<!-- Audio settings strings -->
+ <string name="audio_output_engine">Output engine</string>
<string name="audio_volume">Volume</string>
<string name="audio_volume_description">Specifies the volume of audio output.</string>
@@ -196,6 +204,7 @@
<string name="learn_more">Learn more</string>
<string name="auto">Auto</string>
<string name="submit">Submit</string>
+ <string name="string_null">Null</string>
<!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string>
@@ -366,6 +375,9 @@
<string name="theme_mode_light">Light</string>
<string name="theme_mode_dark">Dark</string>
+ <!-- Audio output engines -->
+ <string name="cubeb">cubeb</string>
+
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Black backgrounds</string>
<string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string>
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 70480b725..908811e2c 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -4,6 +4,8 @@
#include <algorithm>
#include <atomic>
#include <cinttypes>
+#include <condition_variable>
+#include <mutex>
#include <optional>
#include <vector>
@@ -1313,7 +1315,8 @@ void KThread::RequestDummyThreadWait() {
ASSERT(this->IsDummyThread());
// We will block when the scheduler lock is released.
- m_dummy_thread_runnable.store(false);
+ std::scoped_lock lock{m_dummy_thread_mutex};
+ m_dummy_thread_runnable = false;
}
void KThread::DummyThreadBeginWait() {
@@ -1323,7 +1326,8 @@ void KThread::DummyThreadBeginWait() {
}
// Block until runnable is no longer false.
- m_dummy_thread_runnable.wait(false);
+ std::unique_lock lock{m_dummy_thread_mutex};
+ m_dummy_thread_cv.wait(lock, [this] { return m_dummy_thread_runnable; });
}
void KThread::DummyThreadEndWait() {
@@ -1331,8 +1335,11 @@ void KThread::DummyThreadEndWait() {
ASSERT(this->IsDummyThread());
// Wake up the waiting thread.
- m_dummy_thread_runnable.store(true);
- m_dummy_thread_runnable.notify_one();
+ {
+ std::scoped_lock lock{m_dummy_thread_mutex};
+ m_dummy_thread_runnable = true;
+ }
+ m_dummy_thread_cv.notify_one();
}
void KThread::BeginWait(KThreadQueue* queue) {
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index f9814ac8f..37fe5db77 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -892,7 +892,9 @@ private:
std::shared_ptr<Common::Fiber> m_host_context{};
ThreadType m_thread_type{};
StepState m_step_state{};
- std::atomic<bool> m_dummy_thread_runnable{true};
+ bool m_dummy_thread_runnable{true};
+ std::mutex m_dummy_thread_mutex{};
+ std::condition_variable m_dummy_thread_cv{};
// For debugging
std::vector<KSynchronizationObject*> m_wait_objects_for_debugging{};
diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
index b2bcb68c3..bc232c334 100644
--- a/src/core/hle/service/nfc/common/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
@@ -36,12 +36,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
// Validate UUID
constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3`
- if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) !=
- ntag_file.uuid.uid[3]) {
+ if ((CT ^ ntag_file.uuid.part1[0] ^ ntag_file.uuid.part1[1] ^ ntag_file.uuid.part1[2]) !=
+ ntag_file.uuid.crc_check1) {
return false;
}
- if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^
- ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) {
+ if ((ntag_file.uuid.part2[0] ^ ntag_file.uuid.part2[1] ^ ntag_file.uuid.part2[2] ^
+ ntag_file.uuid.nintendo_id) != ntag_file.uuid_crc_check2) {
return false;
}
@@ -74,8 +74,9 @@ bool IsAmiiboValid(const NTAG215File& ntag_file) {
NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
NTAG215File encoded_data{};
- encoded_data.uid = nfc_data.uuid.uid;
- encoded_data.nintendo_id = nfc_data.uuid.nintendo_id;
+ encoded_data.uid = nfc_data.uuid;
+ encoded_data.uid_crc_check2 = nfc_data.uuid_crc_check2;
+ encoded_data.internal_number = nfc_data.internal_number;
encoded_data.static_lock = nfc_data.static_lock;
encoded_data.compability_container = nfc_data.compability_container;
encoded_data.hmac_data = nfc_data.user_memory.hmac_data;
@@ -94,7 +95,6 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc;
encoded_data.application_area = nfc_data.user_memory.application_area;
encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag;
- encoded_data.lock_bytes = nfc_data.uuid.lock_bytes;
encoded_data.model_info = nfc_data.user_memory.model_info;
encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt;
encoded_data.dynamic_lock = nfc_data.dynamic_lock;
@@ -108,9 +108,9 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
EncryptedNTAG215File nfc_data{};
- nfc_data.uuid.uid = encoded_data.uid;
- nfc_data.uuid.nintendo_id = encoded_data.nintendo_id;
- nfc_data.uuid.lock_bytes = encoded_data.lock_bytes;
+ nfc_data.uuid = encoded_data.uid;
+ nfc_data.uuid_crc_check2 = encoded_data.uid_crc_check2;
+ nfc_data.internal_number = encoded_data.internal_number;
nfc_data.static_lock = encoded_data.static_lock;
nfc_data.compability_container = encoded_data.compability_container;
nfc_data.user_memory.hmac_data = encoded_data.hmac_data;
@@ -139,23 +139,12 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
return nfc_data;
}
-u32 GetTagPassword(const TagUuid& uuid) {
- // Verify that the generated password is correct
- u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]);
- password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8;
- password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16;
- password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24;
- return password;
-}
-
HashSeed GetSeed(const NTAG215File& data) {
HashSeed seed{
.magic = data.write_counter,
.padding = {},
.uid_1 = data.uid,
- .nintendo_id_1 = data.nintendo_id,
.uid_2 = data.uid,
- .nintendo_id_2 = data.nintendo_id,
.keygen_salt = data.keygen_salt,
};
@@ -177,10 +166,11 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed
output.insert(output.end(), key.magic_bytes.begin(),
key.magic_bytes.begin() + key.magic_length);
- output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end());
- output.emplace_back(seed.nintendo_id_1);
- output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end());
- output.emplace_back(seed.nintendo_id_2);
+ std::array<u8, sizeof(NFP::TagUuid)> seed_uuid{};
+ memcpy(seed_uuid.data(), &seed.uid_1, sizeof(NFP::TagUuid));
+ output.insert(output.end(), seed_uuid.begin(), seed_uuid.end());
+ memcpy(seed_uuid.data(), &seed.uid_2, sizeof(NFP::TagUuid));
+ output.insert(output.end(), seed_uuid.begin(), seed_uuid.end());
for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) {
output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i]));
@@ -264,8 +254,8 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
// Copy the rest of the data directly
out_data.uid = in_data.uid;
- out_data.nintendo_id = in_data.nintendo_id;
- out_data.lock_bytes = in_data.lock_bytes;
+ out_data.uid_crc_check2 = in_data.uid_crc_check2;
+ out_data.internal_number = in_data.internal_number;
out_data.static_lock = in_data.static_lock;
out_data.compability_container = in_data.compability_container;
diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.h b/src/core/hle/service/nfc/common/amiibo_crypto.h
index bf3044ed9..6a3e0841e 100644
--- a/src/core/hle/service/nfc/common/amiibo_crypto.h
+++ b/src/core/hle/service/nfc/common/amiibo_crypto.h
@@ -24,10 +24,8 @@ using DrgbOutput = std::array<u8, 0x20>;
struct HashSeed {
u16_be magic;
std::array<u8, 0xE> padding;
- NFC::UniqueSerialNumber uid_1;
- u8 nintendo_id_1;
- NFC::UniqueSerialNumber uid_2;
- u8 nintendo_id_2;
+ TagUuid uid_1;
+ TagUuid uid_2;
std::array<u8, 0x20> keygen_salt;
};
static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size");
@@ -69,9 +67,6 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data);
/// Converts from encoded file format to encrypted file format
EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data);
-/// Returns password needed to allow write access to protected memory
-u32 GetTagPassword(const TagUuid& uuid);
-
// Generates Seed needed for key derivation
HashSeed GetSeed(const NTAG215File& data);
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index b14f682b5..f4b180b06 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -242,34 +242,39 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
return ResultWrongDeviceState;
}
- UniqueSerialNumber uuid = encrypted_tag_data.uuid.uid;
-
- // Generate random UUID to bypass amiibo load limits
- if (Settings::values.random_amiibo_id) {
- Common::TinyMT rng{};
- rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
- rng.GenerateRandomBytes(uuid.data(), sizeof(UniqueSerialNumber));
- uuid[3] = 0x88 ^ uuid[0] ^ uuid[1] ^ uuid[2];
- }
+ UniqueSerialNumber uuid{};
+ u8 uuid_length{};
+ NfcProtocol protocol{NfcProtocol::TypeA};
+ TagType tag_type{TagType::Type2};
if (is_mifare) {
- tag_info = {
- .uuid = uuid,
- .uuid_extension = {},
- .uuid_length = static_cast<u8>(uuid.size()),
- .protocol = NfcProtocol::TypeA,
- .tag_type = TagType::Type4,
+ tag_type = TagType::Mifare;
+ uuid_length = sizeof(NFP::NtagTagUuid);
+ memcpy(uuid.data(), mifare_data.data(), uuid_length);
+ } else {
+ tag_type = TagType::Type2;
+ uuid_length = sizeof(NFP::NtagTagUuid);
+ NFP::NtagTagUuid nUuid{
+ .part1 = encrypted_tag_data.uuid.part1,
+ .part2 = encrypted_tag_data.uuid.part2,
+ .nintendo_id = encrypted_tag_data.uuid.nintendo_id,
};
- return ResultSuccess;
+ memcpy(uuid.data(), &nUuid, uuid_length);
+
+ // Generate random UUID to bypass amiibo load limits
+ if (Settings::values.random_amiibo_id) {
+ Common::TinyMT rng{};
+ rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
+ rng.GenerateRandomBytes(uuid.data(), uuid_length);
+ }
}
// Protocol and tag type may change here
tag_info = {
.uuid = uuid,
- .uuid_extension = {},
- .uuid_length = static_cast<u8>(uuid.size()),
- .protocol = NfcProtocol::TypeA,
- .tag_type = TagType::Type2,
+ .uuid_length = uuid_length,
+ .protocol = protocol,
+ .tag_type = tag_type,
};
return ResultSuccess;
@@ -277,8 +282,38 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameters,
std::span<MifareReadBlockData> read_block_data) const {
+ if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return ResultTagRemoved;
+ }
+ return ResultWrongDeviceState;
+ }
+
Result result = ResultSuccess;
+ TagInfo tag_info{};
+ result = GetTagInfo(tag_info, true);
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) {
+ return ResultInvalidTagType;
+ }
+
+ if (parameters.size() == 0) {
+ return ResultInvalidArgument;
+ }
+
+ const auto unknown = parameters[0].sector_key.unknown;
+ for (std::size_t i = 0; i < parameters.size(); i++) {
+ if (unknown != parameters[i].sector_key.unknown) {
+ return ResultInvalidArgument;
+ }
+ }
+
for (std::size_t i = 0; i < parameters.size(); i++) {
result = ReadMifare(parameters[i], read_block_data[i]);
if (result.IsError()) {
@@ -293,17 +328,8 @@ Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter,
MifareReadBlockData& read_block_data) const {
const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock);
read_block_data.sector_number = parameter.sector_number;
-
- if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
-
if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
- return Mifare::ResultReadError;
+ return ResultMifareError288;
}
// TODO: Use parameter.sector_key to read encrypted data
@@ -315,6 +341,28 @@ Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter,
Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) {
Result result = ResultSuccess;
+ TagInfo tag_info{};
+ result = GetTagInfo(tag_info, true);
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) {
+ return ResultInvalidTagType;
+ }
+
+ if (parameters.size() == 0) {
+ return ResultInvalidArgument;
+ }
+
+ const auto unknown = parameters[0].sector_key.unknown;
+ for (std::size_t i = 0; i < parameters.size(); i++) {
+ if (unknown != parameters[i].sector_key.unknown) {
+ return ResultInvalidArgument;
+ }
+ }
+
for (std::size_t i = 0; i < parameters.size(); i++) {
result = WriteMifare(parameters[i]);
if (result.IsError()) {
@@ -324,7 +372,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet
if (!npad_device->WriteNfc(mifare_data)) {
LOG_ERROR(Service_NFP, "Error writing to file");
- return Mifare::ResultReadError;
+ return ResultMifareError288;
}
return result;
@@ -342,7 +390,7 @@ Result NfcDevice::WriteMifare(const MifareWriteBlockParameter& parameter) {
}
if (mifare_data.size() < sector_index + sizeof(DataBlock)) {
- return Mifare::ResultReadError;
+ return ResultMifareError288;
}
// TODO: Use parameter.sector_key to encrypt the data
@@ -366,7 +414,7 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
LOG_ERROR(Service_NFP, "Not an amiibo");
- return ResultNotAnAmiibo;
+ return ResultInvalidTagType;
}
// The loaded amiibo is not encrypted
@@ -381,14 +429,14 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
}
if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
- bool has_backup = HasBackup(encrypted_tag_data.uuid.uid).IsSuccess();
+ bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess();
LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup);
return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData;
}
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
- WriteBackupData(encrypted_tag_data.uuid.uid, data);
+ WriteBackupData(encrypted_tag_data.uuid, data);
device_state = DeviceState::TagMounted;
mount_target = mount_target_;
@@ -492,7 +540,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
}
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
- WriteBackupData(encrypted_tag_data.uuid.uid, data);
+ WriteBackupData(encrypted_tag_data.uuid, data);
}
if (!npad_device->WriteNfc(data)) {
@@ -520,7 +568,7 @@ Result NfcDevice::Restore() {
return result;
}
- result = ReadBackupData(tag_info.uuid, data);
+ result = ReadBackupData(tag_info.uuid, tag_info.uuid_length, data);
if (result.IsError()) {
return result;
@@ -548,7 +596,7 @@ Result NfcDevice::Restore() {
}
if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) {
- return ResultNotAnAmiibo;
+ return ResultInvalidTagType;
}
if (!is_plain_amiibo) {
@@ -1194,10 +1242,12 @@ Result NfcDevice::BreakTag(NFP::BreakType break_type) {
return FlushWithBreak(break_type);
}
-Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const {
+Result NfcDevice::HasBackup(const UniqueSerialNumber& uid, std::size_t uuid_size) const {
+ ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
constexpr auto backup_dir = "backup";
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
- const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
+ const auto file_name =
+ fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) {
return ResultUnableToAccessBackupFile;
@@ -1206,10 +1256,19 @@ Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const {
return ResultSuccess;
}
-Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const {
+Result NfcDevice::HasBackup(const NFP::TagUuid& tag_uid) const {
+ UniqueSerialNumber uuid{};
+ memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
+ return HasBackup(uuid, sizeof(NFP::TagUuid));
+}
+
+Result NfcDevice::ReadBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
+ std::span<u8> data) const {
+ ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
constexpr auto backup_dir = "backup";
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
- const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
+ const auto file_name =
+ fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
Common::FS::FileAccessMode::Read,
@@ -1228,12 +1287,21 @@ Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u
return ResultSuccess;
}
-Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data) {
+Result NfcDevice::ReadBackupData(const NFP::TagUuid& tag_uid, std::span<u8> data) const {
+ UniqueSerialNumber uuid{};
+ memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
+ return ReadBackupData(uuid, sizeof(NFP::TagUuid), data);
+}
+
+Result NfcDevice::WriteBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
+ std::span<const u8> data) {
+ ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
constexpr auto backup_dir = "backup";
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
- const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
+ const auto file_name =
+ fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
- if (HasBackup(uid).IsError()) {
+ if (HasBackup(uid, uuid_size).IsError()) {
if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) {
return ResultBackupPathAlreadyExist;
}
@@ -1260,6 +1328,12 @@ Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<
return ResultSuccess;
}
+Result NfcDevice::WriteBackupData(const NFP::TagUuid& tag_uid, std::span<const u8> data) {
+ UniqueSerialNumber uuid{};
+ memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
+ return WriteBackupData(uuid, sizeof(NFP::TagUuid), data);
+}
+
Result NfcDevice::WriteNtf(std::span<const u8> data) {
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
index 6f049b687..7560210d6 100644
--- a/src/core/hle/service/nfc/common/device.h
+++ b/src/core/hle/service/nfc/common/device.h
@@ -86,9 +86,14 @@ public:
Result GetAll(NFP::NfpData& data) const;
Result SetAll(const NFP::NfpData& data);
Result BreakTag(NFP::BreakType break_type);
- Result HasBackup(const NFC::UniqueSerialNumber& uid) const;
- Result ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const;
- Result WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data);
+ Result HasBackup(const UniqueSerialNumber& uid, std::size_t uuid_size) const;
+ Result HasBackup(const NFP::TagUuid& tag_uid) const;
+ Result ReadBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
+ std::span<u8> data) const;
+ Result ReadBackupData(const NFP::TagUuid& tag_uid, std::span<u8> data) const;
+ Result WriteBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
+ std::span<const u8> data);
+ Result WriteBackupData(const NFP::TagUuid& tag_uid, std::span<const u8> data);
Result WriteNtf(std::span<const u8> data);
u64 GetHandle() const;
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
index cffd602df..b0456508e 100644
--- a/src/core/hle/service/nfc/common/device_manager.cpp
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -550,7 +550,7 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons
}
if (result.IsSuccess()) {
- result = device->ReadBackupData(tag_info.uuid, data);
+ result = device->ReadBackupData(tag_info.uuid, tag_info.uuid_length, data);
result = VerifyDeviceResult(device, result);
}
@@ -569,7 +569,7 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat
}
if (result.IsSuccess()) {
- result = device->WriteBackupData(tag_info.uuid, data);
+ result = device->WriteBackupData(tag_info.uuid, tag_info.uuid_length, data);
result = VerifyDeviceResult(device, result);
}
diff --git a/src/core/hle/service/nfc/mifare_result.h b/src/core/hle/service/nfc/mifare_result.h
index 4b60048a5..16a9171e6 100644
--- a/src/core/hle/service/nfc/mifare_result.h
+++ b/src/core/hle/service/nfc/mifare_result.h
@@ -12,6 +12,6 @@ constexpr Result ResultInvalidArgument(ErrorModule::NFCMifare, 65);
constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73);
constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80);
constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97);
-constexpr Result ResultReadError(ErrorModule::NFCMifare, 288);
+constexpr Result ResultNotAMifare(ErrorModule::NFCMifare, 288);
} // namespace Service::NFC::Mifare
diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp
index 198d0f2b9..130fb7f78 100644
--- a/src/core/hle/service/nfc/nfc_interface.cpp
+++ b/src/core/hle/service/nfc/nfc_interface.cpp
@@ -142,9 +142,13 @@ void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
void NfcInterface::StartDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
- const auto tag_protocol{rp.PopEnum<NfcProtocol>()};
- LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol);
+ auto tag_protocol{NfcProtocol::All};
+
+ if (backend_type == BackendType::Nfc) {
+ tag_protocol = rp.PopEnum<NfcProtocol>();
+ }
+ LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol);
auto result = GetManager()->StartDetection(device_handle, tag_protocol);
result = TranslateResultToServiceError(result);
@@ -355,7 +359,7 @@ Result NfcInterface::TranslateResultToNfp(Result result) const {
if (result == ResultApplicationAreaExist) {
return NFP::ResultApplicationAreaExist;
}
- if (result == ResultNotAnAmiibo) {
+ if (result == ResultInvalidTagType) {
return NFP::ResultNotAnAmiibo;
}
if (result == ResultUnableToAccessBackupFile) {
@@ -381,6 +385,9 @@ Result NfcInterface::TranslateResultToMifare(Result result) const {
if (result == ResultTagRemoved) {
return Mifare::ResultTagRemoved;
}
+ if (result == ResultInvalidTagType) {
+ return Mifare::ResultNotAMifare;
+ }
LOG_WARNING(Service_NFC, "Result conversion not handled");
return result;
}
diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h
index 59a808740..715c0e80c 100644
--- a/src/core/hle/service/nfc/nfc_result.h
+++ b/src/core/hle/service/nfc/nfc_result.h
@@ -24,7 +24,8 @@ constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136);
constexpr Result ResultCorruptedData(ErrorModule::NFC, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168);
-constexpr Result ResultNotAnAmiibo(ErrorModule::NFC, 178);
+constexpr Result ResultInvalidTagType(ErrorModule::NFC, 178);
constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216);
+constexpr Result ResultMifareError288(ErrorModule::NFC, 288);
} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc_types.h b/src/core/hle/service/nfc/nfc_types.h
index c7ebd1fdb..68e724442 100644
--- a/src/core/hle/service/nfc/nfc_types.h
+++ b/src/core/hle/service/nfc/nfc_types.h
@@ -35,32 +35,35 @@ enum class State : u32 {
// This is nn::nfc::TagType
enum class TagType : u32 {
- None,
- Type1, // ISO14443A RW 96-2k bytes 106kbit/s
- Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
- Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
- Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
- Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
+ None = 0,
+ Type1 = 1U << 0, // ISO14443A RW. Topaz
+ Type2 = 1U << 1, // ISO14443A RW. Ultralight, NTAGX, ST25TN
+ Type3 = 1U << 2, // ISO14443A RW/RO. Sony FeliCa
+ Type4A = 1U << 3, // ISO14443A RW/RO. DESFire
+ Type4B = 1U << 4, // ISO14443B RW/RO. DESFire
+ Type5 = 1U << 5, // ISO15693 RW/RO. SLI, SLIX, ST25TV
+ Mifare = 1U << 6, // Mifare classic. Skylanders
+ All = 0xFFFFFFFF,
};
enum class PackedTagType : u8 {
- None,
- Type1, // ISO14443A RW 96-2k bytes 106kbit/s
- Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
- Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
- Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
- Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
+ None = 0,
+ Type1 = 1U << 0, // ISO14443A RW. Topaz
+ Type2 = 1U << 1, // ISO14443A RW. Ultralight, NTAGX, ST25TN
+ Type3 = 1U << 2, // ISO14443A RW/RO. Sony FeliCa
+ Type4A = 1U << 3, // ISO14443A RW/RO. DESFire
+ Type4B = 1U << 4, // ISO14443B RW/RO. DESFire
+ Type5 = 1U << 5, // ISO15693 RW/RO. SLI, SLIX, ST25TV
+ Mifare = 1U << 6, // Mifare classic. Skylanders
+ All = 0xFF,
};
// This is nn::nfc::NfcProtocol
-// Verify this enum. It might be completely wrong default protocol is 0x48
enum class NfcProtocol : u32 {
None,
TypeA = 1U << 0, // ISO14443A
TypeB = 1U << 1, // ISO14443B
TypeF = 1U << 2, // Sony FeliCa
- Unknown1 = 1U << 3,
- Unknown2 = 1U << 5,
All = 0xFFFFFFFFU,
};
@@ -69,8 +72,7 @@ enum class TestWaveType : u32 {
Unknown,
};
-using UniqueSerialNumber = std::array<u8, 7>;
-using UniqueSerialNumberExtension = std::array<u8, 3>;
+using UniqueSerialNumber = std::array<u8, 10>;
// This is nn::nfc::DeviceHandle
using DeviceHandle = u64;
@@ -78,7 +80,6 @@ using DeviceHandle = u64;
// This is nn::nfc::TagInfo
struct TagInfo {
UniqueSerialNumber uuid;
- UniqueSerialNumberExtension uuid_extension;
u8 uuid_length;
INSERT_PADDING_BYTES(0x15);
NfcProtocol protocol;
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index 7d36d5ee6..aed12a7f8 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -85,7 +85,7 @@ enum class CabinetMode : u8 {
StartFormatter,
};
-using LockBytes = std::array<u8, 2>;
+using UuidPart = std::array<u8, 3>;
using HashData = std::array<u8, 0x20>;
using ApplicationArea = std::array<u8, 0xD8>;
using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
@@ -93,12 +93,20 @@ using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
// This is nn::nfp::TagInfo
using TagInfo = NFC::TagInfo;
+struct NtagTagUuid {
+ UuidPart part1;
+ UuidPart part2;
+ u8 nintendo_id;
+};
+static_assert(sizeof(NtagTagUuid) == 7, "NtagTagUuid is an invalid size");
+
struct TagUuid {
- NFC::UniqueSerialNumber uid;
+ UuidPart part1;
+ u8 crc_check1;
+ UuidPart part2;
u8 nintendo_id;
- LockBytes lock_bytes;
};
-static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size");
+static_assert(sizeof(TagUuid) == 8, "TagUuid is an invalid size");
struct WriteDate {
u16 year;
@@ -231,7 +239,8 @@ struct EncryptedAmiiboFile {
static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
struct NTAG215File {
- LockBytes lock_bytes; // Tag UUID
+ u8 uid_crc_check2;
+ u8 internal_number;
u16 static_lock; // Set defined pages as read only
u32 compability_container; // Defines available memory
HashData hmac_data; // Hash
@@ -250,8 +259,7 @@ struct NTAG215File {
u32_be register_info_crc;
ApplicationArea application_area; // Encrypted Game data
HashData hmac_tag; // Hash
- NFC::UniqueSerialNumber uid; // Unique serial number
- u8 nintendo_id; // Tag UUID
+ TagUuid uid;
AmiiboModelInfo model_info;
HashData keygen_salt; // Salt
u32 dynamic_lock; // Dynamic lock
@@ -264,7 +272,9 @@ static_assert(std::is_trivially_copyable_v<NTAG215File>, "NTAG215File must be tr
#pragma pack()
struct EncryptedNTAG215File {
- TagUuid uuid; // Unique serial number
+ TagUuid uuid;
+ u8 uuid_crc_check2;
+ u8 internal_number;
u16 static_lock; // Set defined pages as read only
u32 compability_container; // Defines available memory
EncryptedAmiiboFile user_memory; // Writable data
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index f8bafe553..6435b8af8 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -82,6 +82,7 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
switch (nfc_file.GetSize()) {
case AmiiboSize:
case AmiiboSizeWithoutPassword:
+ case AmiiboSizeWithSignature:
data.resize(AmiiboSize);
if (nfc_file.Read(data) < AmiiboSizeWithoutPassword) {
return Info::NotAnAmiibo;
@@ -109,6 +110,7 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
switch (data.size_bytes()) {
case AmiiboSize:
case AmiiboSizeWithoutPassword:
+ case AmiiboSizeWithSignature:
nfc_data.resize(AmiiboSize);
break;
case MifareSize:
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 34e97cd91..09ca09e68 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -57,6 +57,7 @@ public:
private:
static constexpr std::size_t AmiiboSize = 0x21C;
static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8;
+ static constexpr std::size_t AmiiboSizeWithSignature = AmiiboSize + 0x20;
static constexpr std::size_t MifareSize = 0x400;
std::string file_path{};
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
index 1a0cea9b7..3151c0db8 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
@@ -87,7 +87,8 @@ void ComputePipeline::Configure() {
texture_cache.SynchronizeComputeDescriptors();
boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
- std::array<GLuint, MAX_TEXTURES> samplers;
+ boost::container::static_vector<VideoCommon::SamplerId, MAX_TEXTURES> samplers;
+ std::array<GLuint, MAX_TEXTURES> gl_samplers;
std::array<GLuint, MAX_TEXTURES> textures;
std::array<GLuint, MAX_IMAGES> images;
GLsizei sampler_binding{};
@@ -131,7 +132,6 @@ void ComputePipeline::Configure() {
for (u32 index = 0; index < desc.count; ++index) {
const auto handle{read_handle(desc, index)};
views.push_back({handle.first});
- samplers[sampler_binding++] = 0;
}
}
for (const auto& desc : info.image_buffer_descriptors) {
@@ -142,8 +142,8 @@ void ComputePipeline::Configure() {
const auto handle{read_handle(desc, index)};
views.push_back({handle.first});
- Sampler* const sampler = texture_cache.GetComputeSampler(handle.second);
- samplers[sampler_binding++] = sampler->Handle();
+ VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
+ samplers.push_back(sampler);
}
}
for (const auto& desc : info.image_descriptors) {
@@ -186,10 +186,17 @@ void ComputePipeline::Configure() {
const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers +
num_image_buffers};
+ const VideoCommon::SamplerId* samplers_it{samplers.data()};
texture_binding += num_texture_buffers;
image_binding += num_image_buffers;
u32 texture_scaling_mask{};
+
+ for (const auto& desc : info.texture_buffer_descriptors) {
+ for (u32 index = 0; index < desc.count; ++index) {
+ gl_samplers[sampler_binding++] = 0;
+ }
+ }
for (const auto& desc : info.texture_descriptors) {
for (u32 index = 0; index < desc.count; ++index) {
ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
@@ -198,6 +205,12 @@ void ComputePipeline::Configure() {
texture_scaling_mask |= 1u << texture_binding;
}
++texture_binding;
+
+ const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))};
+ const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
+ !image_view.SupportsAnisotropy()};
+ gl_samplers[sampler_binding++] =
+ use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle();
}
}
u32 image_scaling_mask{};
@@ -228,7 +241,7 @@ void ComputePipeline::Configure() {
if (texture_binding != 0) {
ASSERT(texture_binding == sampler_binding);
glBindTextures(0, texture_binding, textures.data());
- glBindSamplers(0, sampler_binding, samplers.data());
+ glBindSamplers(0, sampler_binding, gl_samplers.data());
}
if (image_binding != 0) {
glBindImageTextures(0, image_binding, images.data());
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 89000d6e0..c58f760b8 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -275,9 +275,9 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
template <typename Spec>
void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
- std::array<GLuint, MAX_TEXTURES> samplers;
+ std::array<VideoCommon::SamplerId, MAX_TEXTURES> samplers;
size_t views_index{};
- GLsizei sampler_binding{};
+ size_t samplers_index{};
texture_cache.SynchronizeGraphicsDescriptors();
@@ -337,7 +337,6 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
for (u32 index = 0; index < desc.count; ++index) {
const auto handle{read_handle(desc, index)};
views[views_index++] = {handle.first};
- samplers[sampler_binding++] = 0;
}
}
}
@@ -351,8 +350,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
const auto handle{read_handle(desc, index)};
views[views_index++] = {handle.first};
- Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)};
- samplers[sampler_binding++] = sampler->Handle();
+ VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
+ samplers[samplers_index++] = sampler;
}
}
if constexpr (Spec::has_images) {
@@ -445,10 +444,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
program_manager.BindSourcePrograms(source_programs);
}
const VideoCommon::ImageViewInOut* views_it{views.data()};
+ const VideoCommon::SamplerId* samplers_it{samplers.data()};
GLsizei texture_binding = 0;
GLsizei image_binding = 0;
+ GLsizei sampler_binding{};
std::array<GLuint, MAX_TEXTURES> textures;
std::array<GLuint, MAX_IMAGES> images;
+ std::array<GLuint, MAX_TEXTURES> gl_samplers;
const auto prepare_stage{[&](size_t stage) {
buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]);
buffer_cache.BindHostStageBuffers(stage);
@@ -465,6 +467,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
u32 stage_image_binding{};
const auto& info{stage_infos[stage]};
+ if constexpr (Spec::has_texture_buffers) {
+ for (const auto& desc : info.texture_buffer_descriptors) {
+ for (u32 index = 0; index < desc.count; ++index) {
+ gl_samplers[sampler_binding++] = 0;
+ }
+ }
+ }
for (const auto& desc : info.texture_descriptors) {
for (u32 index = 0; index < desc.count; ++index) {
ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
@@ -474,6 +483,12 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
}
++texture_binding;
++stage_texture_binding;
+
+ const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))};
+ const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
+ !image_view.SupportsAnisotropy()};
+ gl_samplers[sampler_binding++] =
+ use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle();
}
}
for (const auto& desc : info.image_descriptors) {
@@ -534,7 +549,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
if (texture_binding != 0) {
ASSERT(texture_binding == sampler_binding);
glBindTextures(0, texture_binding, textures.data());
- glBindSamplers(0, sampler_binding, samplers.data());
+ glBindSamplers(0, sampler_binding, gl_samplers.data());
}
if (image_binding != 0) {
glBindImageTextures(0, image_binding, images.data());
diff --git a/src/video_core/renderer_opengl/gl_shader_context.h b/src/video_core/renderer_opengl/gl_shader_context.h
index 207a75d42..d12cd06fa 100644
--- a/src/video_core/renderer_opengl/gl_shader_context.h
+++ b/src/video_core/renderer_opengl/gl_shader_context.h
@@ -16,9 +16,9 @@ struct ShaderPools {
inst.ReleaseContents();
}
- Shader::ObjectPool<Shader::IR::Inst> inst;
- Shader::ObjectPool<Shader::IR::Block> block;
- Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block;
+ Shader::ObjectPool<Shader::IR::Inst> inst{8192};
+ Shader::ObjectPool<Shader::IR::Block> block{32};
+ Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32};
};
struct Context {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 1c5dbcdd8..3b446be07 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -1268,36 +1268,48 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const TSCEntry& config) {
UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1);
- sampler.Create();
- const GLuint handle = sampler.handle;
- glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u));
- glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v));
- glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p));
- glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode);
- glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func);
- glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag);
- glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min);
- glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias());
- glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod());
- glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod());
- glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data());
-
- if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) {
- const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f);
- glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropy);
- } else {
- LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required");
- }
- if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) {
- glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter);
- } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) {
- LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required");
- }
- if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) {
- glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless);
- } else if (seamless == GL_FALSE) {
- // We default to false because it's more common
- LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required");
+ const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f);
+
+ const auto create_sampler = [&](const f32 anisotropy) {
+ OGLSampler new_sampler;
+ new_sampler.Create();
+ const GLuint handle = new_sampler.handle;
+ glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u));
+ glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v));
+ glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p));
+ glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode);
+ glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func);
+ glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag);
+ glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min);
+ glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias());
+ glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod());
+ glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod());
+ glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data());
+
+ if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) {
+ glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, anisotropy);
+ } else {
+ LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required");
+ }
+ if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) {
+ glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter);
+ } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) {
+ LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required");
+ }
+ if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) {
+ glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless);
+ } else if (seamless == GL_FALSE) {
+ // We default to false because it's more common
+ LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required");
+ }
+ return new_sampler;
+ };
+
+ sampler = create_sampler(max_anisotropy);
+
+ const f32 max_anisotropy_default = static_cast<f32>(1U << config.max_anisotropy);
+ if (max_anisotropy > max_anisotropy_default) {
+ sampler_default_anisotropy = create_sampler(max_anisotropy_default);
}
}
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 1148b73d7..3676eaaa9 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -309,12 +309,21 @@ class Sampler {
public:
explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
- GLuint Handle() const noexcept {
+ [[nodiscard]] GLuint Handle() const noexcept {
return sampler.handle;
}
+ [[nodiscard]] GLuint HandleWithDefaultAnisotropy() const noexcept {
+ return sampler_default_anisotropy.handle;
+ }
+
+ [[nodiscard]] bool HasAddedAnisotropy() const noexcept {
+ return static_cast<bool>(sampler_default_anisotropy.handle);
+ }
+
private:
OGLSampler sampler;
+ OGLSampler sampler_default_anisotropy;
};
class Framebuffer {
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index 983e1c2e1..71c783709 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -178,7 +178,7 @@ public:
inline void PushImageDescriptors(TextureCache& texture_cache,
GuestDescriptorQueue& guest_descriptor_queue,
const Shader::Info& info, RescalingPushConstant& rescaling,
- const VkSampler*& samplers,
+ const VideoCommon::SamplerId*& samplers,
const VideoCommon::ImageViewInOut*& views) {
const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors);
const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors);
@@ -187,10 +187,15 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
for (const auto& desc : info.texture_descriptors) {
for (u32 index = 0; index < desc.count; ++index) {
const VideoCommon::ImageViewId image_view_id{(views++)->id};
- const VkSampler sampler{*(samplers++)};
+ const VideoCommon::SamplerId sampler_id{*(samplers++)};
ImageView& image_view{texture_cache.GetImageView(image_view_id)};
const VkImageView vk_image_view{image_view.Handle(desc.type)};
- guest_descriptor_queue.AddSampledImage(vk_image_view, sampler);
+ const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
+ const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
+ !image_view.SupportsAnisotropy()};
+ const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
+ : sampler.Handle()};
+ guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
rescaling.PushTexture(texture_cache.IsRescaling(image_view));
}
}
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 733e70d9d..73e585c2b 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -115,7 +115,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
static constexpr size_t max_elements = 64;
boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views;
- boost::container::static_vector<VkSampler, max_elements> samplers;
+ boost::container::static_vector<VideoCommon::SamplerId, max_elements> samplers;
const auto& qmd{kepler_compute.launch_description};
const auto& cbufs{qmd.const_buffer_config};
@@ -160,8 +160,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
const auto handle{read_handle(desc, index)};
views.push_back({handle.first});
- Sampler* const sampler = texture_cache.GetComputeSampler(handle.second);
- samplers.push_back(sampler->Handle());
+ VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
+ samplers.push_back(sampler);
}
}
for (const auto& desc : info.image_descriptors) {
@@ -192,7 +192,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
buffer_cache.BindHostComputeBuffers();
RescalingPushConstant rescaling;
- const VkSampler* samplers_it{samplers.data()};
+ const VideoCommon::SamplerId* samplers_it{samplers.data()};
const VideoCommon::ImageViewInOut* views_it{views.data()};
PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it,
views_it);
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 506b78f08..c1595642e 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -298,7 +298,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) {
template <typename Spec>
void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views;
- std::array<VkSampler, MAX_IMAGE_ELEMENTS> samplers;
+ std::array<VideoCommon::SamplerId, MAX_IMAGE_ELEMENTS> samplers;
size_t sampler_index{};
size_t view_index{};
@@ -367,8 +367,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
const auto handle{read_handle(desc, index)};
views[view_index++] = {handle.first};
- Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)};
- samplers[sampler_index++] = sampler->Handle();
+ VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
+ samplers[sampler_index++] = sampler;
}
}
if constexpr (Spec::has_images) {
@@ -453,7 +453,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
RescalingPushConstant rescaling;
RenderAreaPushConstant render_area;
- const VkSampler* samplers_it{samplers.data()};
+ const VideoCommon::SamplerId* samplers_it{samplers.data()};
const VideoCommon::ImageViewInOut* views_it{views.data()};
const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
buffer_cache.BindHostStageBuffers(stage);
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
index b128c4f6e..5eeda08d2 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -3,6 +3,7 @@
#include <thread>
+#include "common/polyfill_ranges.h"
#include "common/settings.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/vulkan_common/vulkan_device.h"
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 15aa7e224..e323ea0fd 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -92,9 +92,9 @@ struct ShaderPools {
inst.ReleaseContents();
}
- Shader::ObjectPool<Shader::IR::Inst> inst;
- Shader::ObjectPool<Shader::IR::Block> block;
- Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block;
+ Shader::ObjectPool<Shader::IR::Inst> inst{8192};
+ Shader::ObjectPool<Shader::IR::Block> block{32};
+ Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32};
};
class PipelineCache : public VideoCommon::ShaderCache {
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 8711e2a87..f025f618b 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1802,27 +1802,36 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
// Some games have samplers with garbage. Sanitize them here.
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
- sampler = device.GetLogical().CreateSampler(VkSamplerCreateInfo{
- .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
- .pNext = pnext,
- .flags = 0,
- .magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
- .minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
- .mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
- .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
- .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
- .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
- .mipLodBias = tsc.LodBias(),
- .anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
- .maxAnisotropy = max_anisotropy,
- .compareEnable = tsc.depth_compare_enabled,
- .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
- .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
- .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
- .borderColor =
- arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
- .unnormalizedCoordinates = VK_FALSE,
- });
+ const auto create_sampler = [&](const f32 anisotropy) {
+ return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
+ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+ .pNext = pnext,
+ .flags = 0,
+ .magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
+ .minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
+ .mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
+ .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
+ .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
+ .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
+ .mipLodBias = tsc.LodBias(),
+ .anisotropyEnable = static_cast<VkBool32>(anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
+ .maxAnisotropy = anisotropy,
+ .compareEnable = tsc.depth_compare_enabled,
+ .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
+ .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
+ .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
+ .borderColor =
+ arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
+ .unnormalizedCoordinates = VK_FALSE,
+ });
+ };
+
+ sampler = create_sampler(max_anisotropy);
+
+ const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
+ if (max_anisotropy > max_anisotropy_default) {
+ sampler_default_anisotropy = create_sampler(max_anisotropy_default);
+ }
}
Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 0f7a5ffd4..f14525dcb 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -279,8 +279,17 @@ public:
return *sampler;
}
+ [[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept {
+ return *sampler_default_anisotropy;
+ }
+
+ [[nodiscard]] bool HasAddedAnisotropy() const noexcept {
+ return static_cast<bool>(sampler_default_anisotropy);
+ }
+
private:
vk::Sampler sampler;
+ vk::Sampler sampler_default_anisotropy;
};
class Framebuffer {
diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp
index d134b6738..0c5f4450d 100644
--- a/src/video_core/texture_cache/image_view_base.cpp
+++ b/src/video_core/texture_cache/image_view_base.cpp
@@ -45,4 +45,56 @@ ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_in
ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {}
+bool ImageViewBase::SupportsAnisotropy() const noexcept {
+ const bool has_mips = range.extent.levels > 1;
+ const bool is_2d = type == ImageViewType::e2D || type == ImageViewType::e2DArray;
+ if (!has_mips || !is_2d) {
+ return false;
+ }
+
+ switch (format) {
+ case PixelFormat::R8_UNORM:
+ case PixelFormat::R8_SNORM:
+ case PixelFormat::R8_SINT:
+ case PixelFormat::R8_UINT:
+ case PixelFormat::BC4_UNORM:
+ case PixelFormat::BC4_SNORM:
+ case PixelFormat::BC5_UNORM:
+ case PixelFormat::BC5_SNORM:
+ case PixelFormat::R32G32_FLOAT:
+ case PixelFormat::R32G32_SINT:
+ case PixelFormat::R32_FLOAT:
+ case PixelFormat::R16_FLOAT:
+ case PixelFormat::R16_UNORM:
+ case PixelFormat::R16_SNORM:
+ case PixelFormat::R16_UINT:
+ case PixelFormat::R16_SINT:
+ case PixelFormat::R16G16_UNORM:
+ case PixelFormat::R16G16_FLOAT:
+ case PixelFormat::R16G16_UINT:
+ case PixelFormat::R16G16_SINT:
+ case PixelFormat::R16G16_SNORM:
+ case PixelFormat::R8G8_UNORM:
+ case PixelFormat::R8G8_SNORM:
+ case PixelFormat::R8G8_SINT:
+ case PixelFormat::R8G8_UINT:
+ case PixelFormat::R32G32_UINT:
+ case PixelFormat::R32_UINT:
+ case PixelFormat::R32_SINT:
+ case PixelFormat::G4R4_UNORM:
+ // Depth formats
+ case PixelFormat::D32_FLOAT:
+ case PixelFormat::D16_UNORM:
+ // Stencil formats
+ case PixelFormat::S8_UINT:
+ // DepthStencil formats
+ case PixelFormat::D24_UNORM_S8_UINT:
+ case PixelFormat::S8_UINT_D24_UNORM:
+ case PixelFormat::D32_FLOAT_S8_UINT:
+ return false;
+ default:
+ return true;
+ }
+}
+
} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/image_view_base.h b/src/video_core/texture_cache/image_view_base.h
index a25ae1d4a..87549ffff 100644
--- a/src/video_core/texture_cache/image_view_base.h
+++ b/src/video_core/texture_cache/image_view_base.h
@@ -33,6 +33,8 @@ struct ImageViewBase {
return type == ImageViewType::Buffer;
}
+ [[nodiscard]] bool SupportsAnisotropy() const noexcept;
+
ImageId image_id{};
GPUVAddr gpu_addr = 0;
PixelFormat format{};
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index c7f7448e9..4027d860b 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -222,30 +222,50 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
template <class P>
typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
+ return &slot_samplers[GetGraphicsSamplerId(index)];
+}
+
+template <class P>
+typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
+ return &slot_samplers[GetComputeSamplerId(index)];
+}
+
+template <class P>
+SamplerId TextureCache<P>::GetGraphicsSamplerId(u32 index) {
if (index > channel_state->graphics_sampler_table.Limit()) {
LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
- return &slot_samplers[NULL_SAMPLER_ID];
+ return NULL_SAMPLER_ID;
}
const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index);
SamplerId& id = channel_state->graphics_sampler_ids[index];
if (is_new) {
id = FindSampler(descriptor);
}
- return &slot_samplers[id];
+ return id;
}
template <class P>
-typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
+SamplerId TextureCache<P>::GetComputeSamplerId(u32 index) {
if (index > channel_state->compute_sampler_table.Limit()) {
LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
- return &slot_samplers[NULL_SAMPLER_ID];
+ return NULL_SAMPLER_ID;
}
const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index);
SamplerId& id = channel_state->compute_sampler_ids[index];
if (is_new) {
id = FindSampler(descriptor);
}
- return &slot_samplers[id];
+ return id;
+}
+
+template <class P>
+const typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) const noexcept {
+ return slot_samplers[id];
+}
+
+template <class P>
+typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) noexcept {
+ return slot_samplers[id];
}
template <class P>
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 3bfa92154..d96ddea9d 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -159,6 +159,18 @@ public:
/// Get the sampler from the compute descriptor table in the specified index
Sampler* GetComputeSampler(u32 index);
+ /// Get the sampler id from the graphics descriptor table in the specified index
+ SamplerId GetGraphicsSamplerId(u32 index);
+
+ /// Get the sampler id from the compute descriptor table in the specified index
+ SamplerId GetComputeSamplerId(u32 index);
+
+ /// Return a constant reference to the given sampler id
+ [[nodiscard]] const Sampler& GetSampler(SamplerId id) const noexcept;
+
+ /// Return a reference to the given sampler id
+ [[nodiscard]] Sampler& GetSampler(SamplerId id) noexcept;
+
/// Refresh the state for graphics image view and sampler descriptors
void SynchronizeGraphicsDescriptors();
diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp
index 4a80a59f9..d8b88d9bc 100644
--- a/src/video_core/textures/texture.cpp
+++ b/src/video_core/textures/texture.cpp
@@ -62,7 +62,12 @@ std::array<float, 4> TSCEntry::BorderColor() const noexcept {
}
float TSCEntry::MaxAnisotropy() const noexcept {
- if (max_anisotropy == 0 && mipmap_filter != TextureMipmapFilter::Linear) {
+ const bool is_suitable_mipmap_filter = mipmap_filter != TextureMipmapFilter::None;
+ const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256;
+ const bool is_bilinear_filter = min_filter == TextureFilter::Linear &&
+ reduction_filter == SamplerReduction::WeightedAverage;
+ if (max_anisotropy == 0 && (!is_suitable_mipmap_filter || !has_regular_lods ||
+ !is_bilinear_filter || depth_compare_enabled)) {
return 1.0f;
}
const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue();